Merge "Introduce system shortcut key unit tests"
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 65b2511..f8aa7e9 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -17,17 +17,12 @@
                tests/
                tools/
 bpfmt = -d
+
 [Hook Scripts]
 checkstyle_hook = ${REPO_ROOT}/prebuilts/checkstyle/checkstyle.py --sha ${PREUPLOAD_COMMIT}
 
-strings_lint_hook = ${REPO_ROOT}/frameworks/base/tools/stringslint/stringslint_sha.sh ${PREUPLOAD_COMMIT}
-
 hidden_api_txt_checksorted_hook = ${REPO_ROOT}/tools/platform-compat/hiddenapi/checksorted_sha.sh ${PREUPLOAD_COMMIT} ${REPO_ROOT}
 
 hidden_api_txt_exclude_hook = ${REPO_ROOT}/frameworks/base/tools/hiddenapi/exclude.sh ${PREUPLOAD_COMMIT} ${REPO_ROOT}
 
 ktlint_hook = ${REPO_ROOT}/prebuilts/ktlint/ktlint.py -f ${PREUPLOAD_FILES}
-
-owners_hook = ${REPO_ROOT}/frameworks/base/tools/aosp/aosp_sha.sh ${PREUPLOAD_COMMIT} "OWNERS$"
-
-shell_hook = ${REPO_ROOT}/frameworks/base/tools/aosp/aosp_sha.sh ${PREUPLOAD_COMMIT} "^packages/Shell/"
diff --git a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
index 40a3c92..448ee61 100644
--- a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
+++ b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
@@ -316,8 +316,6 @@
                 mIam.startUserInBackground(userId);
             }, Intent.ACTION_USER_STARTED, Intent.ACTION_MEDIA_MOUNTED);
 
-            waitForBroadcastIdle();
-
             mRunner.resumeTiming();
             Log.i(TAG, "Starting timer");
 
@@ -680,8 +678,6 @@
     }
 
     private void stopUser(int userId, boolean force) throws RemoteException {
-        waitForBroadcastIdle();
-
         final CountDownLatch latch = new CountDownLatch(1);
         mIam.stopUser(userId, force /* force */, new IStopUserCallback.Stub() {
             @Override
@@ -886,8 +882,4 @@
         assertEquals("", ShellHelper.runShellCommand("setprop " + name + " " + value));
         return oldValue;
     }
-
-    private void waitForBroadcastIdle() {
-        ShellHelper.runShellCommand("am wait-for-broadcast-idle");
-    }
 }
diff --git a/apct-tests/perftests/surfaceflinger/AndroidTest.xml b/apct-tests/perftests/surfaceflinger/AndroidTest.xml
index b8bd8b4..0f3a068 100644
--- a/apct-tests/perftests/surfaceflinger/AndroidTest.xml
+++ b/apct-tests/perftests/surfaceflinger/AndroidTest.xml
@@ -52,12 +52,20 @@
         <!-- PerfettoListener related arguments -->
         <option name="instrumentation-arg" key="perfetto_config_text_proto" value="true" />
         <option name="instrumentation-arg" key="perfetto_config_file" value="trace_config.textproto" />
+
+        <!-- SimpleperfListener related arguments -->
+        <option name="instrumentation-arg" key="report" value="true" />
+        <option name="instrumentation-arg" key="arguments" value="&quot;&quot;" />
+        <option name="instrumentation-arg" key="events_to_record" value="instructions,cpu-cycles,raw-l3d-cache-refill,sched:sched_waking" />
+        <option name="instrumentation-arg" key="processes_to_record" value="surfaceflinger" />
+        <option name="instrumentation-arg" key="symbols_to_report" value="&quot;android::SurfaceFlinger::commit(long, long, long)&quot;" />
     </test>
 
     <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
         <option name="directory-keys" value="/data/local/tmp/SurfaceFlingerPerfTests" />
         <!-- Needed for pulling the collected trace config on to the host -->
         <option name="pull-pattern-keys" value="perfetto_file_path" />
+        <option name="pull-pattern-keys" value="simpleperf_file_path" />
     </metrics_collector>
 
 </configuration>
diff --git a/apct-tests/perftests/surfaceflinger/src/android/surfaceflinger/SurfaceFlingerPerfTest.java b/apct-tests/perftests/surfaceflinger/src/android/surfaceflinger/SurfaceFlingerPerfTest.java
index 347cb78..8efe48d 100644
--- a/apct-tests/perftests/surfaceflinger/src/android/surfaceflinger/SurfaceFlingerPerfTest.java
+++ b/apct-tests/perftests/surfaceflinger/src/android/surfaceflinger/SurfaceFlingerPerfTest.java
@@ -48,6 +48,5 @@
         while (state.keepRunning()) {
             // Do Something
         }
-
     }
 }
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 2da00c7..6d5c160 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
@@ -274,24 +274,19 @@
     public void onBootPhase(int phase) {
         mBootPhase = phase;
 
-        if (PHASE_SYSTEM_SERVICES_READY == phase) {
-            mConfigObserver.start();
-            mDeviceIdleController = IDeviceIdleController.Stub.asInterface(
-                    ServiceManager.getService(Context.DEVICE_IDLE_CONTROLLER));
-            setupEverything();
-        } else if (PHASE_BOOT_COMPLETED == phase) {
-            if (!mExemptListLoaded) {
-                synchronized (mLock) {
-                    try {
-                        mExemptedApps =
-                                new ArraySet<>(mDeviceIdleController.getFullPowerWhitelist());
-                    } catch (RemoteException e) {
-                        // Shouldn't happen.
-                        Slog.wtf(TAG, e);
-                    }
-                    mExemptListLoaded = true;
-                }
-            }
+        switch (phase) {
+            case PHASE_SYSTEM_SERVICES_READY:
+                mConfigObserver.start();
+                mDeviceIdleController = IDeviceIdleController.Stub.asInterface(
+                        ServiceManager.getService(Context.DEVICE_IDLE_CONTROLLER));
+                onBootPhaseSystemServicesReady();
+                break;
+            case PHASE_THIRD_PARTY_APPS_CAN_START:
+                onBootPhaseThirdPartyAppsCanStart();
+                break;
+            case PHASE_BOOT_COMPLETED:
+                onBootPhaseBootCompleted();
+                break;
         }
     }
 
@@ -403,10 +398,9 @@
             final ArraySet<String> added = new ArraySet<>();
             try {
                 mExemptedApps = new ArraySet<>(mDeviceIdleController.getFullPowerWhitelist());
+                mExemptListLoaded = true;
             } catch (RemoteException e) {
                 // Shouldn't happen.
-                Slog.wtf(TAG, e);
-                return;
             }
 
             for (int i = mExemptedApps.size() - 1; i >= 0; --i) {
@@ -695,17 +689,11 @@
 
     /** Perform long-running and/or heavy setup work. This should be called off the main thread. */
     private void setupHeavyWork() {
+        if (mBootPhase < PHASE_THIRD_PARTY_APPS_CAN_START || !mIsEnabled) {
+            return;
+        }
         synchronized (mLock) {
             loadInstalledPackageListLocked();
-            if (mBootPhase >= PHASE_BOOT_COMPLETED && !mExemptListLoaded) {
-                try {
-                    mExemptedApps = new ArraySet<>(mDeviceIdleController.getFullPowerWhitelist());
-                } catch (RemoteException e) {
-                    // Shouldn't happen.
-                    Slog.wtf(TAG, e);
-                }
-                mExemptListLoaded = true;
-            }
             final boolean isFirstSetup = !mScribe.recordExists();
             if (isFirstSetup) {
                 mAgent.grantBirthrightsLocked();
@@ -726,18 +714,57 @@
         }
     }
 
-    private void setupEverything() {
+    private void onBootPhaseSystemServicesReady() {
         if (mBootPhase < PHASE_SYSTEM_SERVICES_READY || !mIsEnabled) {
             return;
         }
         synchronized (mLock) {
             registerListeners();
             mCurrentBatteryLevel = getCurrentBatteryLevel();
+        }
+    }
+
+    private void onBootPhaseThirdPartyAppsCanStart() {
+        if (mBootPhase < PHASE_THIRD_PARTY_APPS_CAN_START || !mIsEnabled) {
+            return;
+        }
+        synchronized (mLock) {
             mHandler.post(this::setupHeavyWork);
             mCompleteEconomicPolicy.setup(mConfigObserver.getAllDeviceConfigProperties());
         }
     }
 
+    private void onBootPhaseBootCompleted() {
+        if (mBootPhase < PHASE_BOOT_COMPLETED || !mIsEnabled) {
+            return;
+        }
+        synchronized (mLock) {
+            if (!mExemptListLoaded) {
+                try {
+                    mExemptedApps = new ArraySet<>(mDeviceIdleController.getFullPowerWhitelist());
+                    mExemptListLoaded = true;
+                } catch (RemoteException e) {
+                    // Shouldn't happen.
+                }
+            }
+        }
+    }
+
+    private void setupEverything() {
+        if (!mIsEnabled) {
+            return;
+        }
+        if (mBootPhase >= PHASE_SYSTEM_SERVICES_READY) {
+            onBootPhaseSystemServicesReady();
+        }
+        if (mBootPhase >= PHASE_THIRD_PARTY_APPS_CAN_START) {
+            onBootPhaseThirdPartyAppsCanStart();
+        }
+        if (mBootPhase >= PHASE_BOOT_COMPLETED) {
+            onBootPhaseBootCompleted();
+        }
+    }
+
     private void tearDownEverything() {
         if (mIsEnabled) {
             return;
diff --git a/cmds/uinput/src/com/android/commands/uinput/Event.java b/cmds/uinput/src/com/android/commands/uinput/Event.java
index 9add310e..4b090f5 100644
--- a/cmds/uinput/src/com/android/commands/uinput/Event.java
+++ b/cmds/uinput/src/com/android/commands/uinput/Event.java
@@ -25,6 +25,7 @@
 import java.io.InputStreamReader;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.stream.IntStream;
 
 import src.com.android.commands.uinput.InputAbsInfo;
 
@@ -338,7 +339,7 @@
                 mReader.beginArray();
                 while (mReader.hasNext()) {
                     int type = 0;
-                    int[] data = null;
+                    IntStream data = null;
                     mReader.beginObject();
                     while (mReader.hasNext()) {
                         String name = mReader.nextName();
@@ -347,8 +348,7 @@
                                 type = readInt();
                                 break;
                             case "data":
-                                data = readIntList().stream()
-                                            .mapToInt(Integer::intValue).toArray();
+                                data = readIntList().stream().mapToInt(Integer::intValue);
                                 break;
                             default:
                                 consumeRemainingElements();
@@ -359,7 +359,9 @@
                     }
                     mReader.endObject();
                     if (data != null) {
-                        configuration.put(type, data);
+                        final int[] existing = configuration.get(type);
+                        configuration.put(type, existing == null ? data.toArray()
+                                : IntStream.concat(IntStream.of(existing), data).toArray());
                     }
                 }
                 mReader.endArray();
diff --git a/core/api/current.txt b/core/api/current.txt
index 1cd8253..7ed1a43 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -11523,6 +11523,7 @@
     field public static final int INSTALL_LOCATION_INTERNAL_ONLY = 1; // 0x1
     field public static final int INSTALL_LOCATION_PREFER_EXTERNAL = 2; // 0x2
     field public static final int REQUESTED_PERMISSION_GRANTED = 2; // 0x2
+    field public static final int REQUESTED_PERMISSION_IMPLICIT = 4; // 0x4
     field public static final int REQUESTED_PERMISSION_NEVER_FOR_LOCATION = 65536; // 0x10000
     field public android.content.pm.ActivityInfo[] activities;
     field public android.content.pm.ApplicationInfo applicationInfo;
@@ -32262,6 +32263,7 @@
     method @RequiresPermission(anyOf={"android.permission.MANAGE_USERS", "android.permission.INTERACT_ACROSS_USERS"}, conditional=true) public boolean isUserRunningOrStopping(android.os.UserHandle);
     method public boolean isUserUnlocked();
     method @RequiresPermission(anyOf={"android.permission.MANAGE_USERS", "android.permission.INTERACT_ACROSS_USERS"}, conditional=true) public boolean isUserUnlocked(android.os.UserHandle);
+    method public boolean isUserVisible();
     method @RequiresPermission(anyOf={"android.permission.MANAGE_USERS", "android.permission.MODIFY_QUIET_MODE"}, conditional=true) public boolean requestQuietModeEnabled(boolean, @NonNull android.os.UserHandle);
     method public boolean requestQuietModeEnabled(boolean, @NonNull android.os.UserHandle, int);
     method @Deprecated public boolean setRestrictionsChallenge(String);
@@ -49228,6 +49230,10 @@
   }
 
   public final class PixelCopy {
+    method @NonNull public static android.view.PixelCopy.Request ofSurface(@NonNull android.view.Surface, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.view.PixelCopy.CopyResult>);
+    method @NonNull public static android.view.PixelCopy.Request ofSurface(@NonNull android.view.SurfaceView, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.view.PixelCopy.CopyResult>);
+    method @NonNull public static android.view.PixelCopy.Request ofWindow(@NonNull android.view.Window, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.view.PixelCopy.CopyResult>);
+    method @NonNull public static android.view.PixelCopy.Request ofWindow(@NonNull android.view.View, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.view.PixelCopy.CopyResult>);
     method public static void request(@NonNull android.view.SurfaceView, @NonNull android.graphics.Bitmap, @NonNull android.view.PixelCopy.OnPixelCopyFinishedListener, @NonNull android.os.Handler);
     method public static void request(@NonNull android.view.SurfaceView, @Nullable android.graphics.Rect, @NonNull android.graphics.Bitmap, @NonNull android.view.PixelCopy.OnPixelCopyFinishedListener, @NonNull android.os.Handler);
     method public static void request(@NonNull android.view.Surface, @NonNull android.graphics.Bitmap, @NonNull android.view.PixelCopy.OnPixelCopyFinishedListener, @NonNull android.os.Handler);
@@ -49242,10 +49248,21 @@
     field public static final int SUCCESS = 0; // 0x0
   }
 
+  public static final class PixelCopy.CopyResult {
+    method @NonNull public android.graphics.Bitmap getBitmap();
+    method public int getStatus();
+  }
+
   public static interface PixelCopy.OnPixelCopyFinishedListener {
     method public void onPixelCopyFinished(int);
   }
 
+  public static final class PixelCopy.Request {
+    method public void request();
+    method @NonNull public android.view.PixelCopy.Request setDestinationBitmap(@Nullable android.graphics.Bitmap);
+    method @NonNull public android.view.PixelCopy.Request setSourceRect(@Nullable android.graphics.Rect);
+  }
+
   public final class PointerIcon implements android.os.Parcelable {
     method @NonNull public static android.view.PointerIcon create(@NonNull android.graphics.Bitmap, float, float);
     method public int describeContents();
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 09d4ba6..ddbccfa 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -824,6 +824,8 @@
   }
 
   public abstract class PackageManager {
+    method public abstract void addCrossProfileIntentFilter(@NonNull android.content.IntentFilter, int, int, int);
+    method public abstract void clearCrossProfileIntentFilters(int);
     method @Deprecated @Nullable public final String getContentCaptureServicePackageName();
     method @Nullable public String getDefaultTextClassifierPackageName();
     method @RequiresPermission(android.Manifest.permission.INJECT_EVENTS) public android.os.IBinder getHoldLockToken();
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index 90b7828..e86d2f3 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -43,9 +43,9 @@
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
+import android.os.HandlerExecutor;
 import android.os.IBinder;
 import android.os.Looper;
-import android.os.Message;
 import android.os.RemoteCallback;
 import android.os.RemoteException;
 import android.os.SystemClock;
@@ -72,10 +72,7 @@
 import com.android.internal.inputmethod.IAccessibilityInputMethodSessionCallback;
 import com.android.internal.inputmethod.IRemoteAccessibilityInputConnection;
 import com.android.internal.inputmethod.RemoteAccessibilityInputConnection;
-import com.android.internal.os.HandlerCaller;
-import com.android.internal.os.SomeArgs;
 import com.android.internal.util.Preconditions;
-import com.android.internal.util.function.pooled.PooledLambda;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -2634,7 +2631,7 @@
      */
     @Override
     public final IBinder onBind(Intent intent) {
-        return new IAccessibilityServiceClientWrapper(this, getMainLooper(), new Callbacks() {
+        return new IAccessibilityServiceClientWrapper(this, getMainExecutor(), new Callbacks() {
             @Override
             public void onServiceConnected() {
                 AccessibilityService.this.dispatchServiceConnected();
@@ -2751,30 +2748,12 @@
      *
      * @hide
      */
-    public static class IAccessibilityServiceClientWrapper extends IAccessibilityServiceClient.Stub
-            implements HandlerCaller.Callback {
-        private static final int DO_INIT = 1;
-        private static final int DO_ON_INTERRUPT = 2;
-        private static final int DO_ON_ACCESSIBILITY_EVENT = 3;
-        private static final int DO_ON_GESTURE = 4;
-        private static final int DO_CLEAR_ACCESSIBILITY_CACHE = 5;
-        private static final int DO_ON_KEY_EVENT = 6;
-        private static final int DO_ON_MAGNIFICATION_CHANGED = 7;
-        private static final int DO_ON_SOFT_KEYBOARD_SHOW_MODE_CHANGED = 8;
-        private static final int DO_GESTURE_COMPLETE = 9;
-        private static final int DO_ON_FINGERPRINT_ACTIVE_CHANGED = 10;
-        private static final int DO_ON_FINGERPRINT_GESTURE = 11;
-        private static final int DO_ACCESSIBILITY_BUTTON_CLICKED = 12;
-        private static final int DO_ACCESSIBILITY_BUTTON_AVAILABILITY_CHANGED = 13;
-        private static final int DO_ON_SYSTEM_ACTIONS_CHANGED = 14;
-        private static final int DO_CREATE_IME_SESSION = 15;
-        private static final int DO_SET_IME_SESSION_ENABLED = 16;
-        private static final int DO_START_INPUT = 19;
-
-        private final HandlerCaller mCaller;
+    public static class IAccessibilityServiceClientWrapper extends
+            IAccessibilityServiceClient.Stub {
 
         private final Callbacks mCallback;
         private final Context mContext;
+        private final Executor mExecutor;
 
         private int mConnectionId = AccessibilityInteractionClient.NO_ID;
 
@@ -2793,103 +2772,199 @@
         @Nullable
         CancellationGroup mCancellationGroup = null;
 
+        public IAccessibilityServiceClientWrapper(Context context, Executor executor,
+                Callbacks callback) {
+            mCallback = callback;
+            mContext = context;
+            mExecutor = executor;
+        }
+
         public IAccessibilityServiceClientWrapper(Context context, Looper looper,
                 Callbacks callback) {
             mCallback = callback;
             mContext = context;
-            mCaller = new HandlerCaller(context, looper, this, true /*asyncHandler*/);
+            mExecutor = new HandlerExecutor(new Handler(looper));
         }
 
         public void init(IAccessibilityServiceConnection connection, int connectionId,
                 IBinder windowToken) {
-            Message message = mCaller.obtainMessageIOO(DO_INIT, connectionId,
-                    connection, windowToken);
-            mCaller.sendMessage(message);
+            mExecutor.execute(() -> {
+                mConnectionId = connectionId;
+                if (connection != null) {
+                    AccessibilityInteractionClient.getInstance(mContext).addConnection(
+                            mConnectionId, connection, /*initializeCache=*/true);
+                    if (mContext != null) {
+                        try {
+                            connection.setAttributionTag(mContext.getAttributionTag());
+                        } catch (RemoteException re) {
+                            Log.w(LOG_TAG, "Error while setting attributionTag", re);
+                            re.rethrowFromSystemServer();
+                        }
+                    }
+                    mCallback.init(mConnectionId, windowToken);
+                    mCallback.onServiceConnected();
+                } else {
+                    AccessibilityInteractionClient.getInstance(mContext)
+                            .clearCache(mConnectionId);
+                    AccessibilityInteractionClient.getInstance(mContext).removeConnection(
+                            mConnectionId);
+                    mConnectionId = AccessibilityInteractionClient.NO_ID;
+                    mCallback.init(AccessibilityInteractionClient.NO_ID, null);
+                }
+                return;
+            });
         }
 
         public void onInterrupt() {
-            Message message = mCaller.obtainMessage(DO_ON_INTERRUPT);
-            mCaller.sendMessage(message);
+            mExecutor.execute(() -> {
+                if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
+                    mCallback.onInterrupt();
+                }
+            });
         }
 
         public void onAccessibilityEvent(AccessibilityEvent event, boolean serviceWantsEvent) {
-            Message message = mCaller.obtainMessageBO(
-                    DO_ON_ACCESSIBILITY_EVENT, serviceWantsEvent, event);
-            mCaller.sendMessage(message);
+            mExecutor.execute(() -> {
+                if (event != null) {
+                    // Send the event to AccessibilityCache via AccessibilityInteractionClient
+                    AccessibilityInteractionClient.getInstance(mContext).onAccessibilityEvent(
+                            event, mConnectionId);
+                    if (serviceWantsEvent
+                            && (mConnectionId != AccessibilityInteractionClient.NO_ID)) {
+                        // Send the event to AccessibilityService
+                        mCallback.onAccessibilityEvent(event);
+                    }
+                }
+                return;
+            });
         }
 
         @Override
         public void onGesture(AccessibilityGestureEvent gestureInfo) {
-            Message message = mCaller.obtainMessageO(DO_ON_GESTURE, gestureInfo);
-            mCaller.sendMessage(message);
+            mExecutor.execute(() -> {
+                if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
+                    mCallback.onGesture(gestureInfo);
+                }
+                return;
+            });
         }
 
         public void clearAccessibilityCache() {
-            Message message = mCaller.obtainMessage(DO_CLEAR_ACCESSIBILITY_CACHE);
-            mCaller.sendMessage(message);
+            mExecutor.execute(() -> {
+                AccessibilityInteractionClient.getInstance(mContext).clearCache(mConnectionId);
+                return;
+            });
         }
 
         @Override
         public void onKeyEvent(KeyEvent event, int sequence) {
-            Message message = mCaller.obtainMessageIO(DO_ON_KEY_EVENT, sequence, event);
-            mCaller.sendMessage(message);
+            mExecutor.execute(() -> {
+                try {
+                    IAccessibilityServiceConnection connection = AccessibilityInteractionClient
+                            .getInstance(mContext).getConnection(mConnectionId);
+                    if (connection != null) {
+                        final boolean result = mCallback.onKeyEvent(event);
+                        try {
+                            connection.setOnKeyEventResult(result, sequence);
+                        } catch (RemoteException re) {
+                            /* ignore */
+                        }
+                    }
+                } finally {
+                    // Make sure the event is recycled.
+                    try {
+                        event.recycle();
+                    } catch (IllegalStateException ise) {
+                        /* ignore - best effort */
+                    }
+                }
+                return;
+            });
         }
 
         /** Magnification changed callbacks for different displays */
         public void onMagnificationChanged(int displayId, @NonNull Region region,
                 MagnificationConfig config) {
-            final SomeArgs args = SomeArgs.obtain();
-            args.arg1 = region;
-            args.arg2 = config;
-            args.argi1 = displayId;
-
-            final Message message = mCaller.obtainMessageO(DO_ON_MAGNIFICATION_CHANGED, args);
-            mCaller.sendMessage(message);
+            mExecutor.execute(() -> {
+                if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
+                    mCallback.onMagnificationChanged(displayId, region, config);
+                }
+                return;
+            });
         }
 
         public void onSoftKeyboardShowModeChanged(int showMode) {
-          final Message message =
-                  mCaller.obtainMessageI(DO_ON_SOFT_KEYBOARD_SHOW_MODE_CHANGED, showMode);
-          mCaller.sendMessage(message);
+            mExecutor.execute(() -> {
+                if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
+                    mCallback.onSoftKeyboardShowModeChanged(showMode);
+                }
+                return;
+            });
         }
 
         public void onPerformGestureResult(int sequence, boolean successfully) {
-            Message message = mCaller.obtainMessageII(DO_GESTURE_COMPLETE, sequence,
-                    successfully ? 1 : 0);
-            mCaller.sendMessage(message);
+            mExecutor.execute(() -> {
+                if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
+                    mCallback.onPerformGestureResult(sequence, successfully);
+                }
+                return;
+            });
         }
 
         public void onFingerprintCapturingGesturesChanged(boolean active) {
-            mCaller.sendMessage(mCaller.obtainMessageI(
-                    DO_ON_FINGERPRINT_ACTIVE_CHANGED, active ? 1 : 0));
+            mExecutor.execute(() -> {
+                if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
+                    mCallback.onFingerprintCapturingGesturesChanged(active);
+                }
+                return;
+            });
         }
 
         public void onFingerprintGesture(int gesture) {
-            mCaller.sendMessage(mCaller.obtainMessageI(DO_ON_FINGERPRINT_GESTURE, gesture));
+            mExecutor.execute(() -> {
+                if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
+                    mCallback.onFingerprintGesture(gesture);
+                }
+                return;
+            });
         }
 
         /** Accessibility button clicked callbacks for different displays */
         public void onAccessibilityButtonClicked(int displayId) {
-            final Message message = mCaller.obtainMessageI(DO_ACCESSIBILITY_BUTTON_CLICKED,
-                    displayId);
-            mCaller.sendMessage(message);
+            mExecutor.execute(() -> {
+                if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
+                    mCallback.onAccessibilityButtonClicked(displayId);
+                }
+                return;
+            });
         }
 
         public void onAccessibilityButtonAvailabilityChanged(boolean available) {
-            final Message message = mCaller.obtainMessageI(
-                    DO_ACCESSIBILITY_BUTTON_AVAILABILITY_CHANGED, (available ? 1 : 0));
-            mCaller.sendMessage(message);
+            mExecutor.execute(() -> {
+                if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
+                    mCallback.onAccessibilityButtonAvailabilityChanged(available);
+                }
+                return;
+            });
         }
 
         /** This is called when the system action list is changed. */
         public void onSystemActionsChanged() {
-            mCaller.sendMessage(mCaller.obtainMessage(DO_ON_SYSTEM_ACTIONS_CHANGED));
+            mExecutor.execute(() -> {
+                if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
+                    mCallback.onSystemActionsChanged();
+                }
+                return;
+            });
         }
 
         /** This is called when an app requests ime sessions or when the service is enabled. */
         public void createImeSession(IAccessibilityInputMethodSessionCallback callback) {
-            final Message message = mCaller.obtainMessageO(DO_CREATE_IME_SESSION, callback);
-            mCaller.sendMessage(message);
+            mExecutor.execute(() -> {
+                if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
+                    mCallback.createImeSession(callback);
+                }
+            });
         }
 
         /**
@@ -2905,8 +2980,12 @@
                     Log.w(LOG_TAG, "Session is already finished: " + session);
                     return;
                 }
-                mCaller.sendMessage(mCaller.obtainMessageIO(
-                        DO_SET_IME_SESSION_ENABLED, enabled ? 1 : 0, ls));
+                mExecutor.execute(() -> {
+                    if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
+                        ls.setEnabled(enabled);
+                    }
+                    return;
+                });
             } catch (ClassCastException e) {
                 Log.w(LOG_TAG, "Incoming session not of correct type: " + session, e);
             }
@@ -2938,213 +3017,29 @@
                 Log.e(LOG_TAG, "startInput must be called after bindInput.");
                 mCancellationGroup = new CancellationGroup();
             }
-            final Message message = mCaller.obtainMessageOOOOII(DO_START_INPUT, null /* unused */,
-                    connection, editorInfo, mCancellationGroup, restarting ? 1 : 0,
-                    0 /* unused */);
-            mCaller.sendMessage(message);
+            mExecutor.execute(() -> {
+                if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
+                    final RemoteAccessibilityInputConnection ic = connection == null ? null
+                            : new RemoteAccessibilityInputConnection(
+                                    connection, mCancellationGroup);
+                    editorInfo.makeCompatible(mContext.getApplicationInfo().targetSdkVersion);
+                    mCallback.startInput(ic, editorInfo, restarting);
+                }
+            });
         }
 
         @Override
         public void onMotionEvent(MotionEvent event) {
-            final Message message = PooledLambda.obtainMessage(
-                            Callbacks::onMotionEvent, mCallback, event);
-            mCaller.sendMessage(message);
+            mExecutor.execute(() -> {
+                mCallback.onMotionEvent(event);
+            });
         }
 
         @Override
         public void onTouchStateChanged(int displayId, int state) {
-            final Message message = PooledLambda.obtainMessage(Callbacks::onTouchStateChanged,
-                    mCallback,
-                    displayId, state);
-            mCaller.sendMessage(message);
-        }
-
-        @Override
-        public void executeMessage(Message message) {
-            switch (message.what) {
-                case DO_ON_ACCESSIBILITY_EVENT: {
-                    AccessibilityEvent event = (AccessibilityEvent) message.obj;
-                    boolean serviceWantsEvent = message.arg1 != 0;
-                    if (event != null) {
-                        // Send the event to AccessibilityCache via AccessibilityInteractionClient
-                        AccessibilityInteractionClient.getInstance(mContext).onAccessibilityEvent(
-                                event, mConnectionId);
-                        if (serviceWantsEvent
-                                && (mConnectionId != AccessibilityInteractionClient.NO_ID)) {
-                            // Send the event to AccessibilityService
-                            mCallback.onAccessibilityEvent(event);
-                        }
-                        // Make sure the event is recycled.
-                        try {
-                            event.recycle();
-                        } catch (IllegalStateException ise) {
-                            /* ignore - best effort */
-                        }
-                    }
-                    return;
-                }
-                case DO_ON_INTERRUPT: {
-                    if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
-                        mCallback.onInterrupt();
-                    }
-                    return;
-                }
-                case DO_INIT: {
-                    mConnectionId = message.arg1;
-                    SomeArgs args = (SomeArgs) message.obj;
-                    IAccessibilityServiceConnection connection =
-                            (IAccessibilityServiceConnection) args.arg1;
-                    IBinder windowToken = (IBinder) args.arg2;
-                    args.recycle();
-                    if (connection != null) {
-                        AccessibilityInteractionClient.getInstance(mContext).addConnection(
-                                mConnectionId, connection, /*initializeCache=*/true);
-                        if (mContext != null) {
-                            try {
-                                connection.setAttributionTag(mContext.getAttributionTag());
-                            } catch (RemoteException re) {
-                                Log.w(LOG_TAG, "Error while setting attributionTag", re);
-                                re.rethrowFromSystemServer();
-                            }
-                        }
-                        mCallback.init(mConnectionId, windowToken);
-                        mCallback.onServiceConnected();
-                    } else {
-                        AccessibilityInteractionClient.getInstance(mContext)
-                                .clearCache(mConnectionId);
-                        AccessibilityInteractionClient.getInstance(mContext).removeConnection(
-                                mConnectionId);
-                        mConnectionId = AccessibilityInteractionClient.NO_ID;
-                        mCallback.init(AccessibilityInteractionClient.NO_ID, null);
-                    }
-                    return;
-                }
-                case DO_ON_GESTURE: {
-                    if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
-                        mCallback.onGesture((AccessibilityGestureEvent) message.obj);
-                    }
-                    return;
-                }
-                case DO_CLEAR_ACCESSIBILITY_CACHE: {
-                    AccessibilityInteractionClient.getInstance(mContext).clearCache(mConnectionId);
-                    return;
-                }
-                case DO_ON_KEY_EVENT: {
-                    KeyEvent event = (KeyEvent) message.obj;
-                    try {
-                        IAccessibilityServiceConnection connection = AccessibilityInteractionClient
-                                .getInstance(mContext).getConnection(mConnectionId);
-                        if (connection != null) {
-                            final boolean result = mCallback.onKeyEvent(event);
-                            final int sequence = message.arg1;
-                            try {
-                                connection.setOnKeyEventResult(result, sequence);
-                            } catch (RemoteException re) {
-                                /* ignore */
-                            }
-                        }
-                    } finally {
-                        // Make sure the event is recycled.
-                        try {
-                            event.recycle();
-                        } catch (IllegalStateException ise) {
-                            /* ignore - best effort */
-                        }
-                    }
-                    return;
-                }
-                case DO_ON_MAGNIFICATION_CHANGED: {
-                    if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
-                        final SomeArgs args = (SomeArgs) message.obj;
-                        final Region region = (Region) args.arg1;
-                        final MagnificationConfig config = (MagnificationConfig) args.arg2;
-                        final int displayId = args.argi1;
-                        args.recycle();
-                        mCallback.onMagnificationChanged(displayId, region, config);
-                    }
-                    return;
-                }
-                case DO_ON_SOFT_KEYBOARD_SHOW_MODE_CHANGED: {
-                    if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
-                        final int showMode = (int) message.arg1;
-                        mCallback.onSoftKeyboardShowModeChanged(showMode);
-                    }
-                    return;
-                }
-                case DO_GESTURE_COMPLETE: {
-                    if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
-                        final boolean successfully = message.arg2 == 1;
-                        mCallback.onPerformGestureResult(message.arg1, successfully);
-                    }
-                    return;
-                }
-                case DO_ON_FINGERPRINT_ACTIVE_CHANGED: {
-                    if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
-                        mCallback.onFingerprintCapturingGesturesChanged(message.arg1 == 1);
-                    }
-                    return;
-                }
-                case DO_ON_FINGERPRINT_GESTURE: {
-                    if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
-                        mCallback.onFingerprintGesture(message.arg1);
-                    }
-                    return;
-                }
-                case DO_ACCESSIBILITY_BUTTON_CLICKED: {
-                    if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
-                        mCallback.onAccessibilityButtonClicked(message.arg1);
-                    }
-                    return;
-                }
-                case DO_ACCESSIBILITY_BUTTON_AVAILABILITY_CHANGED: {
-                    if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
-                        final boolean available = (message.arg1 != 0);
-                        mCallback.onAccessibilityButtonAvailabilityChanged(available);
-                    }
-                    return;
-                }
-                case DO_ON_SYSTEM_ACTIONS_CHANGED: {
-                    if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
-                        mCallback.onSystemActionsChanged();
-                    }
-                    return;
-                }
-                case DO_CREATE_IME_SESSION: {
-                    if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
-                        IAccessibilityInputMethodSessionCallback callback =
-                                (IAccessibilityInputMethodSessionCallback) message.obj;
-                        mCallback.createImeSession(callback);
-                    }
-                    return;
-                }
-                case DO_SET_IME_SESSION_ENABLED: {
-                    if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
-                        AccessibilityInputMethodSession session =
-                                (AccessibilityInputMethodSession) message.obj;
-                        session.setEnabled(message.arg1 != 0);
-                    }
-                    return;
-                }
-                case DO_START_INPUT: {
-                    if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
-                        final SomeArgs args = (SomeArgs) message.obj;
-                        final IRemoteAccessibilityInputConnection connection =
-                                (IRemoteAccessibilityInputConnection) args.arg2;
-                        final EditorInfo info = (EditorInfo) args.arg3;
-                        final CancellationGroup cancellationGroup = (CancellationGroup) args.arg4;
-                        final boolean restarting = args.argi5 == 1;
-                        final RemoteAccessibilityInputConnection ic = connection == null ? null
-                                : new RemoteAccessibilityInputConnection(
-                                        connection, cancellationGroup);
-                        info.makeCompatible(mContext.getApplicationInfo().targetSdkVersion);
-                        mCallback.startInput(ic, info, restarting);
-                        args.recycle();
-                    }
-                    return;
-                }
-                default:
-                    Log.w(LOG_TAG, "Unknown message type " + message.what);
-            }
+            mExecutor.execute(() -> {
+                mCallback.onTouchStateChanged(displayId, state);
+            });
         }
     }
 
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index e25e374..449729e 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -5124,7 +5124,7 @@
         Preconditions.checkNotNull(listener);
         Preconditions.checkNotNull(executor);
         try {
-            listener.init(mContext, executor, this);
+            listener.init(mContext, executor);
             getService().registerProcessObserver(listener.mObserver);
             // Notify upon first registration.
             executor.execute(() ->
diff --git a/core/java/android/app/ActivityTaskManager.java b/core/java/android/app/ActivityTaskManager.java
index 6fc0c26..f17d5b7 100644
--- a/core/java/android/app/ActivityTaskManager.java
+++ b/core/java/android/app/ActivityTaskManager.java
@@ -16,6 +16,8 @@
 
 package android.app;
 
+import static android.view.Display.INVALID_DISPLAY;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
@@ -369,7 +371,8 @@
      * @hide
      */
     public List<ActivityManager.RunningTaskInfo> getTasks(int maxNum) {
-        return getTasks(maxNum, false /* filterForVisibleRecents */);
+        return getTasks(maxNum, false /* filterForVisibleRecents */, false /* keepIntentExtra */,
+                INVALID_DISPLAY);
     }
 
     /**
@@ -378,7 +381,8 @@
      */
     public List<ActivityManager.RunningTaskInfo> getTasks(
             int maxNum, boolean filterOnlyVisibleRecents) {
-        return getTasks(maxNum, filterOnlyVisibleRecents, false /* keepIntentExtra */);
+        return getTasks(maxNum, filterOnlyVisibleRecents, false /* keepIntentExtra */,
+                INVALID_DISPLAY);
     }
 
     /**
@@ -388,8 +392,20 @@
      */
     public List<ActivityManager.RunningTaskInfo> getTasks(
             int maxNum, boolean filterOnlyVisibleRecents, boolean keepIntentExtra) {
+        return getTasks(maxNum, filterOnlyVisibleRecents, keepIntentExtra, INVALID_DISPLAY);
+    }
+
+    /**
+     * @return List of running tasks that can be filtered by visibility and displayId in recents
+     * and keep intent extra.
+     * @param displayId the target display id, or {@link INVALID_DISPLAY} not to filter by displayId
+     * @hide
+     */
+    public List<ActivityManager.RunningTaskInfo> getTasks(
+            int maxNum, boolean filterOnlyVisibleRecents, boolean keepIntentExtra, int displayId) {
         try {
-            return getService().getTasks(maxNum, filterOnlyVisibleRecents, keepIntentExtra);
+            return getService().getTasks(maxNum, filterOnlyVisibleRecents, keepIntentExtra,
+                    displayId);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index b383d7d..6b3dc82 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -107,6 +107,7 @@
 import android.net.Proxy;
 import android.net.TrafficStats;
 import android.net.Uri;
+import android.net.wifi.WifiFrameworkInitializer;
 import android.os.AsyncTask;
 import android.os.Binder;
 import android.os.BluetoothServiceManager;
@@ -7900,6 +7901,8 @@
         BluetoothFrameworkInitializer.setBluetoothServiceManager(new BluetoothServiceManager());
         BluetoothFrameworkInitializer.setBinderCallsStatsInitializer(context -> {
             BinderCallsStats.startForBluetooth(context); });
+        WifiFrameworkInitializer.setBinderCallsStatsInitializer(context -> {
+            BinderCallsStats.startForWifi(context); });
     }
 
     private void purgePendingResources() {
diff --git a/core/java/android/app/ActivityTransitionState.java b/core/java/android/app/ActivityTransitionState.java
index 57dacd0..877e7d3 100644
--- a/core/java/android/app/ActivityTransitionState.java
+++ b/core/java/android/app/ActivityTransitionState.java
@@ -263,11 +263,6 @@
         // After orientation change, the onResume can come in before the top Activity has
         // left, so if the Activity is not top, wait a second for the top Activity to exit.
         if (mEnterTransitionCoordinator == null || activity.isTopOfTask()) {
-            if (mEnterTransitionCoordinator != null) {
-                mEnterTransitionCoordinator.runAfterTransitionsComplete(() -> {
-                    mEnterTransitionCoordinator = null;
-                });
-            }
             restoreExitedViews();
             restoreReenteringViews();
         } else {
@@ -276,11 +271,6 @@
                 public void run() {
                     if (mEnterTransitionCoordinator == null ||
                             mEnterTransitionCoordinator.isWaitingForRemoteExit()) {
-                        if (mEnterTransitionCoordinator != null) {
-                            mEnterTransitionCoordinator.runAfterTransitionsComplete(() -> {
-                                mEnterTransitionCoordinator = null;
-                            });
-                        }
                         restoreExitedViews();
                         restoreReenteringViews();
                     } else if (mEnterTransitionCoordinator.isReturning()) {
diff --git a/core/java/android/app/GameManagerInternal.java b/core/java/android/app/GameManagerInternal.java
new file mode 100644
index 0000000..c8ff2a8
--- /dev/null
+++ b/core/java/android/app/GameManagerInternal.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.app;
+
+/**
+ * Game manager local system service interface.
+ *
+ * @hide Only for use within the system server.
+ */
+
+public abstract class GameManagerInternal {
+    /**
+     * Used by the CompatModePackages to read game's compat scaling override.
+     */
+    public abstract float getResolutionScalingFactor(String packageName, int userId);
+}
diff --git a/core/java/android/app/HomeVisibilityListener.java b/core/java/android/app/HomeVisibilityListener.java
index 57d868b..ca20648 100644
--- a/core/java/android/app/HomeVisibilityListener.java
+++ b/core/java/android/app/HomeVisibilityListener.java
@@ -16,16 +16,17 @@
 
 package android.app;
 
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
+import static android.view.Display.DEFAULT_DISPLAY;
+
 import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
-import android.content.ComponentName;
 import android.content.Context;
 import android.os.Binder;
 
-import java.util.ArrayList;
 import java.util.List;
-import java.util.Objects;
 import java.util.concurrent.Executor;
 
 /**
@@ -39,20 +40,21 @@
 @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
 @TestApi
 public abstract class HomeVisibilityListener {
-    private Context mContext;
-    private ActivityManager mActivityManager;
+    private ActivityTaskManager mActivityTaskManager;
     private Executor mExecutor;
+    private int mMaxScanTasksForHomeVisibility;
     /** @hide */
     android.app.IProcessObserver.Stub mObserver;
     /** @hide */
     boolean mIsHomeActivityVisible;
 
     /** @hide */
-    void init(Context context, Executor executor, ActivityManager activityManager) {
-        mContext = context;
-        mActivityManager = activityManager;
-        mIsHomeActivityVisible = isHomeActivityVisible();
+    void init(Context context, Executor executor) {
+        mActivityTaskManager = ActivityTaskManager.getInstance();
         mExecutor = executor;
+        mMaxScanTasksForHomeVisibility = context.getResources().getInteger(
+                com.android.internal.R.integer.config_maxScanTasksForHomeVisibility);
+        mIsHomeActivityVisible = isHomeActivityVisible();
     }
 
     /**
@@ -91,22 +93,21 @@
     }
 
     private boolean isHomeActivityVisible() {
-        List<ActivityManager.RunningTaskInfo> tasks = mActivityManager.getRunningTasks(1);
-        if (tasks == null || tasks.isEmpty()) {
+        List<ActivityManager.RunningTaskInfo> tasksTopToBottom = mActivityTaskManager.getTasks(
+                mMaxScanTasksForHomeVisibility, /* filterOnlyVisibleRecents= */ true,
+                /* keepIntentExtra= */ false, DEFAULT_DISPLAY);
+        if (tasksTopToBottom == null || tasksTopToBottom.isEmpty()) {
             return false;
         }
 
-        String top = tasks.get(0).topActivity.getPackageName();
-        if (top == null) {
-            return false;
+        for (int i = 0, taskSize = tasksTopToBottom.size(); i < taskSize; ++i) {
+            ActivityManager.RunningTaskInfo task = tasksTopToBottom.get(i);
+            if (!task.isVisible()
+                    || (task.baseIntent.getFlags() & FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) != 0) {
+                continue;
+            }
+            return task.getActivityType() == ACTIVITY_TYPE_HOME;
         }
-
-        // We can assume that the screen is idle if the home application is in the foreground.
-        ComponentName defaultHomeComponent = mContext.getPackageManager()
-                .getHomeActivities(new ArrayList<>());
-        if (defaultHomeComponent == null) return false;
-
-        String defaultHomePackage = defaultHomeComponent.getPackageName();
-        return Objects.equals(top, defaultHomePackage);
+        return false;
     }
 }
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index 3459d0e..6576a1a 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -158,7 +158,7 @@
     boolean removeTask(int taskId);
     void removeAllVisibleRecentTasks();
     List<ActivityManager.RunningTaskInfo> getTasks(int maxNum, boolean filterOnlyVisibleRecents,
-            boolean keepIntentExtra);
+            boolean keepIntentExtra, int displayId);
     void moveTaskToFront(in IApplicationThread app, in String callingPackage, int task,
             int flags, in Bundle options);
     ParceledListSlice<ActivityManager.RecentTaskInfo> getRecentTasks(int maxNum, int flags,
diff --git a/core/java/android/app/IGameManagerService.aidl b/core/java/android/app/IGameManagerService.aidl
index e60a74a..481e7b0 100644
--- a/core/java/android/app/IGameManagerService.aidl
+++ b/core/java/android/app/IGameManagerService.aidl
@@ -31,4 +31,6 @@
     void setGameState(String packageName, in GameState gameState, int userId);
     GameModeInfo getGameModeInfo(String packageName, int userId);
     void setGameServiceProvider(String packageName);
+    void updateResolutionScalingFactor(String packageName, int gameMode, float scalingFactor, int userId);
+    float getResolutionScalingFactor(String packageName, int gameMode, int userId);
 }
diff --git a/core/java/android/app/PropertyInvalidatedCache.java b/core/java/android/app/PropertyInvalidatedCache.java
index 58ddd49..13934e5 100644
--- a/core/java/android/app/PropertyInvalidatedCache.java
+++ b/core/java/android/app/PropertyInvalidatedCache.java
@@ -310,6 +310,21 @@
     public static final String MODULE_TELEPHONY = "telephony";
 
     /**
+     * Constants that affect retries when the process is unable to write the property.
+     * The first constant is the number of times the process will attempt to set the
+     * property.  The second constant is the delay between attempts.
+     */
+
+    /**
+     * Wait 200ms between retry attempts and the retry limit is 5.  That gives a total possible
+     * delay of 1s, which should be less than ANR timeouts.  The goal is to have the system crash
+     * because the property could not be set (which is a condition that is easily recognized) and
+     * not crash because of an ANR (which can be confusing to debug).
+     */
+    private static final int PROPERTY_FAILURE_RETRY_DELAY_MILLIS = 200;
+    private static final int PROPERTY_FAILURE_RETRY_LIMIT = 5;
+
+    /**
      * Construct a system property that matches the rules described above.  The module is
      * one of the permitted values above.  The API is a string that is a legal Java simple
      * identifier.  The api is modified to conform to the system property style guide by
@@ -670,7 +685,33 @@
                 }
             }
         }
-        SystemProperties.set(name, Long.toString(val));
+        RuntimeException failure = null;
+        for (int attempt = 0; attempt < PROPERTY_FAILURE_RETRY_LIMIT; attempt++) {
+            try {
+                SystemProperties.set(name, Long.toString(val));
+                if (attempt > 0) {
+                    // This log is not guarded.  Based on known bug reports, it should
+                    // occur once a week or less.  The purpose of the log message is to
+                    // identify the retries as a source of delay that might be otherwise
+                    // be attributed to the cache itself.
+                    Log.w(TAG, "Nonce set after " + attempt + " tries");
+                }
+                return;
+            } catch (RuntimeException e) {
+                if (failure == null) {
+                    failure = e;
+                }
+                try {
+                    Thread.sleep(PROPERTY_FAILURE_RETRY_DELAY_MILLIS);
+                } catch (InterruptedException x) {
+                    // Ignore this exception.  The desired delay is only approximate and
+                    // there is no issue if the sleep sometimes terminates early.
+                }
+            }
+        }
+        // This point is reached only if SystemProperties.set() fails at least once.
+        // Rethrow the first exception that was received.
+        throw failure;
     }
 
     // Set the nonce in a static context.  No handle is available.
diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java
index 8be2b48..e3bca9c 100644
--- a/core/java/android/appwidget/AppWidgetHostView.java
+++ b/core/java/android/appwidget/AppWidgetHostView.java
@@ -103,7 +103,6 @@
     private boolean mOnLightBackground;
     private SizeF mCurrentSize = null;
     private RemoteViews.ColorResources mColorResources = null;
-    private SparseIntArray mColorMapping = null;
     // Stores the last remote views last inflated.
     private RemoteViews mLastInflatedRemoteViews = null;
     private long mLastInflatedRemoteViewsId = -1;
@@ -900,11 +899,19 @@
      * {@link android.R.color#system_neutral1_500}.
      */
     public void setColorResources(@NonNull SparseIntArray colorMapping) {
-        if (mColorMapping != null && isSameColorMapping(mColorMapping, colorMapping)) {
+        if (mColorResources != null
+                && isSameColorMapping(mColorResources.getColorMapping(), colorMapping)) {
             return;
         }
-        mColorMapping = colorMapping.clone();
-        mColorResources = RemoteViews.ColorResources.create(mContext, mColorMapping);
+        setColorResources(RemoteViews.ColorResources.create(mContext, colorMapping));
+    }
+
+    /** @hide **/
+    public void setColorResources(RemoteViews.ColorResources colorResources) {
+        if (colorResources == mColorResources) {
+            return;
+        }
+        mColorResources = colorResources;
         mColorMappingChanged = true;
         mViewMode = VIEW_MODE_NOINIT;
         reapplyLastRemoteViews();
@@ -934,7 +941,6 @@
     public void resetColorResources() {
         if (mColorResources != null) {
             mColorResources = null;
-            mColorMapping = null;
             mColorMappingChanged = true;
             mViewMode = VIEW_MODE_NOINIT;
             reapplyLastRemoteViews();
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index 0c171ad..da486ee 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -19,7 +19,7 @@
 import static android.Manifest.permission.INTERACT_ACROSS_USERS;
 import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
 import static android.os.Process.myUserHandle;
-import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
+import static android.os.Trace.TRACE_TAG_DATABASE;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -278,7 +278,7 @@
                 // Return an empty cursor for all columns.
                 return new MatrixCursor(cursor.getColumnNames(), 0);
             }
-            traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "query: ", uri.getAuthority());
+            traceBegin(TRACE_TAG_DATABASE, "query: ", uri.getAuthority());
             final AttributionSource original = setCallingAttributionSource(
                     attributionSource);
             try {
@@ -289,7 +289,7 @@
                 throw e.rethrowAsRuntimeException();
             } finally {
                 setCallingAttributionSource(original);
-                Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+                Trace.traceEnd(TRACE_TAG_DATABASE);
             }
         }
 
@@ -298,13 +298,13 @@
             // getCallingPackage() isn't available in getType(), as the javadoc states.
             uri = validateIncomingUri(uri);
             uri = maybeGetUriWithoutUserId(uri);
-            traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "getType: ", uri.getAuthority());
+            traceBegin(TRACE_TAG_DATABASE, "getType: ", uri.getAuthority());
             try {
                 return mInterface.getType(uri);
             } catch (RemoteException e) {
                 throw e.rethrowAsRuntimeException();
             } finally {
-                Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+                Trace.traceEnd(TRACE_TAG_DATABASE);
             }
         }
 
@@ -336,7 +336,7 @@
                     setCallingAttributionSource(original);
                 }
             }
-            traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "insert: ", uri.getAuthority());
+            traceBegin(TRACE_TAG_DATABASE, "insert: ", uri.getAuthority());
             final AttributionSource original = setCallingAttributionSource(
                     attributionSource);
             try {
@@ -345,7 +345,7 @@
                 throw e.rethrowAsRuntimeException();
             } finally {
                 setCallingAttributionSource(original);
-                Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+                Trace.traceEnd(TRACE_TAG_DATABASE);
             }
         }
 
@@ -358,7 +358,7 @@
                     != PermissionChecker.PERMISSION_GRANTED) {
                 return 0;
             }
-            traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "bulkInsert: ", uri.getAuthority());
+            traceBegin(TRACE_TAG_DATABASE, "bulkInsert: ", uri.getAuthority());
             final AttributionSource original = setCallingAttributionSource(
                     attributionSource);
             try {
@@ -367,7 +367,7 @@
                 throw e.rethrowAsRuntimeException();
             } finally {
                 setCallingAttributionSource(original);
-                Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+                Trace.traceEnd(TRACE_TAG_DATABASE);
             }
         }
 
@@ -404,7 +404,7 @@
                     }
                 }
             }
-            traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "applyBatch: ", authority);
+            traceBegin(TRACE_TAG_DATABASE, "applyBatch: ", authority);
             final AttributionSource original = setCallingAttributionSource(
                     attributionSource);
             try {
@@ -423,7 +423,7 @@
                 throw e.rethrowAsRuntimeException();
             } finally {
                 setCallingAttributionSource(original);
-                Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+                Trace.traceEnd(TRACE_TAG_DATABASE);
             }
         }
 
@@ -436,7 +436,7 @@
                     != PermissionChecker.PERMISSION_GRANTED) {
                 return 0;
             }
-            traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "delete: ", uri.getAuthority());
+            traceBegin(TRACE_TAG_DATABASE, "delete: ", uri.getAuthority());
             final AttributionSource original = setCallingAttributionSource(
                     attributionSource);
             try {
@@ -445,7 +445,7 @@
                 throw e.rethrowAsRuntimeException();
             } finally {
                 setCallingAttributionSource(original);
-                Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+                Trace.traceEnd(TRACE_TAG_DATABASE);
             }
         }
 
@@ -458,7 +458,7 @@
                     != PermissionChecker.PERMISSION_GRANTED) {
                 return 0;
             }
-            traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "update: ", uri.getAuthority());
+            traceBegin(TRACE_TAG_DATABASE, "update: ", uri.getAuthority());
             final AttributionSource original = setCallingAttributionSource(
                     attributionSource);
             try {
@@ -467,7 +467,7 @@
                 throw e.rethrowAsRuntimeException();
             } finally {
                 setCallingAttributionSource(original);
-                Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+                Trace.traceEnd(TRACE_TAG_DATABASE);
             }
         }
 
@@ -478,7 +478,7 @@
             uri = validateIncomingUri(uri);
             uri = maybeGetUriWithoutUserId(uri);
             enforceFilePermission(attributionSource, uri, mode);
-            traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "openFile: ", uri.getAuthority());
+            traceBegin(TRACE_TAG_DATABASE, "openFile: ", uri.getAuthority());
             final AttributionSource original = setCallingAttributionSource(
                     attributionSource);
             try {
@@ -488,7 +488,7 @@
                 throw e.rethrowAsRuntimeException();
             } finally {
                 setCallingAttributionSource(original);
-                Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+                Trace.traceEnd(TRACE_TAG_DATABASE);
             }
         }
 
@@ -499,7 +499,7 @@
             uri = validateIncomingUri(uri);
             uri = maybeGetUriWithoutUserId(uri);
             enforceFilePermission(attributionSource, uri, mode);
-            traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "openAssetFile: ", uri.getAuthority());
+            traceBegin(TRACE_TAG_DATABASE, "openAssetFile: ", uri.getAuthority());
             final AttributionSource original = setCallingAttributionSource(
                     attributionSource);
             try {
@@ -509,7 +509,7 @@
                 throw e.rethrowAsRuntimeException();
             } finally {
                 setCallingAttributionSource(original);
-                Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+                Trace.traceEnd(TRACE_TAG_DATABASE);
             }
         }
 
@@ -518,7 +518,7 @@
                 String method, @Nullable String arg, @Nullable Bundle extras) {
             validateIncomingAuthority(authority);
             Bundle.setDefusable(extras, true);
-            traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "call: ", authority);
+            traceBegin(TRACE_TAG_DATABASE, "call: ", authority);
             final AttributionSource original = setCallingAttributionSource(
                     attributionSource);
             try {
@@ -527,7 +527,7 @@
                 throw e.rethrowAsRuntimeException();
             } finally {
                 setCallingAttributionSource(original);
-                Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+                Trace.traceEnd(TRACE_TAG_DATABASE);
             }
         }
 
@@ -536,13 +536,13 @@
             // getCallingPackage() isn't available in getType(), as the javadoc states.
             uri = validateIncomingUri(uri);
             uri = maybeGetUriWithoutUserId(uri);
-            traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "getStreamTypes: ", uri.getAuthority());
+            traceBegin(TRACE_TAG_DATABASE, "getStreamTypes: ", uri.getAuthority());
             try {
                 return mInterface.getStreamTypes(uri, mimeTypeFilter);
             } catch (RemoteException e) {
                 throw e.rethrowAsRuntimeException();
             } finally {
-                Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+                Trace.traceEnd(TRACE_TAG_DATABASE);
             }
         }
 
@@ -554,7 +554,7 @@
             uri = validateIncomingUri(uri);
             uri = maybeGetUriWithoutUserId(uri);
             enforceFilePermission(attributionSource, uri, "r");
-            traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "openTypedAssetFile: ", uri.getAuthority());
+            traceBegin(TRACE_TAG_DATABASE, "openTypedAssetFile: ", uri.getAuthority());
             final AttributionSource original = setCallingAttributionSource(
                     attributionSource);
             try {
@@ -564,7 +564,7 @@
                 throw e.rethrowAsRuntimeException();
             } finally {
                 setCallingAttributionSource(original);
-                Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+                Trace.traceEnd(TRACE_TAG_DATABASE);
             }
         }
 
@@ -582,7 +582,7 @@
                     != PermissionChecker.PERMISSION_GRANTED) {
                 return null;
             }
-            traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "canonicalize: ", uri.getAuthority());
+            traceBegin(TRACE_TAG_DATABASE, "canonicalize: ", uri.getAuthority());
             final AttributionSource original = setCallingAttributionSource(
                     attributionSource);
             try {
@@ -591,7 +591,7 @@
                 throw e.rethrowAsRuntimeException();
             } finally {
                 setCallingAttributionSource(original);
-                Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+                Trace.traceEnd(TRACE_TAG_DATABASE);
             }
         }
 
@@ -618,7 +618,7 @@
                     != PermissionChecker.PERMISSION_GRANTED) {
                 return null;
             }
-            traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "uncanonicalize: ", uri.getAuthority());
+            traceBegin(TRACE_TAG_DATABASE, "uncanonicalize: ", uri.getAuthority());
             final AttributionSource original = setCallingAttributionSource(
                     attributionSource);
             try {
@@ -627,7 +627,7 @@
                 throw e.rethrowAsRuntimeException();
             } finally {
                 setCallingAttributionSource(original);
-                Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+                Trace.traceEnd(TRACE_TAG_DATABASE);
             }
         }
 
@@ -654,7 +654,7 @@
                     != PermissionChecker.PERMISSION_GRANTED) {
                 return false;
             }
-            traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "refresh: ", uri.getAuthority());
+            traceBegin(TRACE_TAG_DATABASE, "refresh: ", uri.getAuthority());
             final AttributionSource original = setCallingAttributionSource(
                     attributionSource);
             try {
@@ -662,7 +662,7 @@
                         CancellationSignal.fromTransport(cancellationSignal));
             } finally {
                 setCallingAttributionSource(original);
-                Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+                Trace.traceEnd(TRACE_TAG_DATABASE);
             }
         }
 
@@ -671,7 +671,7 @@
                 int uid, int modeFlags) {
             uri = validateIncomingUri(uri);
             uri = maybeGetUriWithoutUserId(uri);
-            traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "checkUriPermission: ", uri.getAuthority());
+            traceBegin(TRACE_TAG_DATABASE, "checkUriPermission: ", uri.getAuthority());
             final AttributionSource original = setCallingAttributionSource(
                     attributionSource);
             try {
@@ -680,7 +680,7 @@
                 throw e.rethrowAsRuntimeException();
             } finally {
                 setCallingAttributionSource(original);
-                Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+                Trace.traceEnd(TRACE_TAG_DATABASE);
             }
         }
 
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 26b2f07..0e0b2dc 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -11051,10 +11051,7 @@
         if (!Objects.equals(this.mData, other.mData)) return false;
         if (!Objects.equals(this.mType, other.mType)) return false;
         if (!Objects.equals(this.mIdentifier, other.mIdentifier)) return false;
-        if (!(this.hasPackageEquivalentComponent() && other.hasPackageEquivalentComponent())
-                && !Objects.equals(this.mPackage, other.mPackage)) {
-            return false;
-        }
+        if (!Objects.equals(this.mPackage, other.mPackage)) return false;
         if (!Objects.equals(this.mComponent, other.mComponent)) return false;
         if (!Objects.equals(this.mCategories, other.mCategories)) return false;
 
@@ -11062,15 +11059,6 @@
     }
 
     /**
-     * Return {@code true} if the component name is not null and is in the same package that this
-     * intent limited to. otherwise return {@code false}.
-     */
-    private boolean hasPackageEquivalentComponent() {
-        return mComponent != null
-            && (mPackage == null || mPackage.equals(mComponent.getPackageName()));
-    }
-
-    /**
      * Generate hash code that matches semantics of filterEquals().
      *
      * @return Returns the hash value of the action, data, type, class, and
diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java
index 9708493..4259600 100644
--- a/core/java/android/content/pm/PackageInfo.java
+++ b/core/java/android/content/pm/PackageInfo.java
@@ -252,6 +252,13 @@
     public static final int REQUESTED_PERMISSION_NEVER_FOR_LOCATION = 0x00010000;
 
     /**
+     * Flag for {@link #requestedPermissionsFlags}: the requested permission was
+     * not explicitly requested via uses-permission, but was instead implicitly
+     * requested (e.g., for version compatibility reasons).
+     */
+    public static final int REQUESTED_PERMISSION_IMPLICIT = 0x00000004;
+
+    /**
      * Array of all signatures read from the package file. This is only filled
      * in if the flag {@link PackageManager#GET_SIGNATURES} was set. A package
      * must be signed with at least one certificate which is at position zero.
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index dd517c9..5839b87 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -9473,6 +9473,7 @@
      */
     @SuppressWarnings("HiddenAbstractMethod")
     @UnsupportedAppUsage
+    @TestApi
     public abstract void addCrossProfileIntentFilter(@NonNull IntentFilter filter,
             @UserIdInt int sourceUserId, @UserIdInt int targetUserId, int flags);
 
@@ -9485,6 +9486,7 @@
      */
     @SuppressWarnings("HiddenAbstractMethod")
     @UnsupportedAppUsage
+    @TestApi
     public abstract void clearCrossProfileIntentFilters(@UserIdInt int sourceUserId);
 
     /**
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index 3dedc41..f47c1e0 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -1974,10 +1974,10 @@
             return true;
         }
         int diff = other.seq - seq;
-        if (diff > 0x10000) {
+        if (Math.abs(diff) > 0x10000000) {
             // If there has been a sufficiently large jump, assume the
             // sequence has wrapped around.
-            return false;
+            return diff < 0;
         }
         return diff > 0;
     }
diff --git a/core/java/android/database/TEST_MAPPING b/core/java/android/database/TEST_MAPPING
new file mode 100644
index 0000000..4a7fa66
--- /dev/null
+++ b/core/java/android/database/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit": [
+    {
+      "name": "CtsDatabaseTestCases"
+    }
+  ]
+}
diff --git a/core/java/android/hardware/location/ContextHubClient.java b/core/java/android/hardware/location/ContextHubClient.java
index 0848697..6ed87fff 100644
--- a/core/java/android/hardware/location/ContextHubClient.java
+++ b/core/java/android/hardware/location/ContextHubClient.java
@@ -81,7 +81,7 @@
      *
      * @param clientProxy the proxy of the client at the service
      */
-    /* package */ void setClientProxy(IContextHubClient clientProxy) {
+    /* package */ synchronized void setClientProxy(IContextHubClient clientProxy) {
         Objects.requireNonNull(clientProxy, "IContextHubClient cannot be null");
         if (mClientProxy != null) {
             throw new IllegalStateException("Cannot change client proxy multiple times");
@@ -93,6 +93,7 @@
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
+        this.notifyAll();
     }
 
     /**
@@ -221,8 +222,15 @@
     }
 
     /** @hide */
-    public void callbackFinished() {
+    public synchronized void callbackFinished() {
         try {
+            while (mClientProxy == null) {
+                try {
+                    this.wait();
+                } catch (InterruptedException e) {
+                    Thread.currentThread().interrupt();
+                }
+            }
             mClientProxy.callbackFinished();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
diff --git a/core/java/android/os/BaseBundle.java b/core/java/android/os/BaseBundle.java
index 0418a4b..87579eb 100644
--- a/core/java/android/os/BaseBundle.java
+++ b/core/java/android/os/BaseBundle.java
@@ -29,6 +29,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.IndentingPrintWriter;
+import com.android.internal.util.Preconditions;
 
 import java.io.Serializable;
 import java.lang.ref.WeakReference;
@@ -113,17 +114,21 @@
      */
     private boolean mParcelledByNative;
 
-    /*
+    /**
      * Flag indicating if mParcelledData is only referenced in this bundle.
-     * mParcelledData could be referenced by other bundles if mMap contains lazy values,
+     * mParcelledData could be referenced elsewhere if mMap contains lazy values,
      * and bundle data is copied to another bundle using putAll or the copy constructors.
      */
     boolean mOwnsLazyValues = true;
 
-    /*
+    /** Tracks how many lazy values are referenced in mMap */
+    private int mLazyValues = 0;
+
+    /**
      * As mParcelledData is set to null when it is unparcelled, we keep a weak reference to
      * it to aid in recycling it. Do not use this reference otherwise.
-     */
+     * Is non-null iff mMap contains lazy values.
+    */
     private WeakReference<Parcel> mWeakParcelledData = null;
 
     /**
@@ -310,7 +315,8 @@
         synchronized (this) {
             final Parcel source = mParcelledData;
             if (source != null) {
-                initializeFromParcelLocked(source, /*recycleParcel=*/ true, mParcelledByNative);
+                Preconditions.checkState(mOwnsLazyValues);
+                initializeFromParcelLocked(source, /*ownsParcel*/ true, mParcelledByNative);
             } else {
                 if (DEBUG) {
                     Log.d(TAG, "unparcel "
@@ -401,11 +407,23 @@
                 }
             }
             mMap.setValueAt(i, object);
+            mLazyValues--;
+            if (mOwnsLazyValues) {
+                Preconditions.checkState(mLazyValues >= 0, "Lazy values ref count below 0");
+                // No more lazy values in mMap, so we can recycle the parcel early rather than
+                // waiting for the next GC run
+                if (mLazyValues == 0) {
+                    Preconditions.checkState(mWeakParcelledData.get() != null,
+                            "Parcel recycled earlier than expected");
+                    recycleParcel(mWeakParcelledData.get());
+                    mWeakParcelledData = null;
+                }
+            }
         }
         return (clazz != null) ? clazz.cast(object) : (T) object;
     }
 
-    private void initializeFromParcelLocked(@NonNull Parcel parcelledData, boolean recycleParcel,
+    private void initializeFromParcelLocked(@NonNull Parcel parcelledData, boolean ownsParcel,
             boolean parcelledByNative) {
         if (isEmptyParcel(parcelledData)) {
             if (DEBUG) {
@@ -437,9 +455,10 @@
             map.erase();
             map.ensureCapacity(count);
         }
+        int numLazyValues = 0;
         try {
-            recycleParcel &= parcelledData.readArrayMap(map, count, !parcelledByNative,
-                    /* lazy */ true, mClassLoader);
+            numLazyValues = parcelledData.readArrayMap(map, count, !parcelledByNative,
+                    /* lazy */ ownsParcel, mClassLoader);
         } catch (BadParcelableException e) {
             if (sShouldDefuse) {
                 Log.w(TAG, "Failed to parse Bundle, but defusing quietly", e);
@@ -448,14 +467,19 @@
                 throw e;
             }
         } finally {
-            mMap = map;
-            if (recycleParcel) {
-                recycleParcel(parcelledData);
-                mWeakParcelledData = null;
-            } else {
-                mWeakParcelledData = new WeakReference<>(parcelledData);
+            mWeakParcelledData = null;
+            if (ownsParcel) {
+                if (numLazyValues == 0) {
+                    recycleParcel(parcelledData);
+                } else {
+                    mWeakParcelledData = new WeakReference<>(parcelledData);
+                }
             }
+
+            mLazyValues = numLazyValues;
             mParcelledByNative = false;
+            mMap = map;
+            // Set field last as it is volatile
             mParcelledData = null;
         }
         if (DEBUG) {
@@ -592,13 +616,17 @@
 
     /**
      * Removes all elements from the mapping of this Bundle.
+     * Recycles the underlying parcel if it is still present.
      */
     public void clear() {
         unparcel();
         if (mOwnsLazyValues && mWeakParcelledData != null) {
             recycleParcel(mWeakParcelledData.get());
-            mWeakParcelledData = null;
         }
+
+        mWeakParcelledData = null;
+        mLazyValues = 0;
+        mOwnsLazyValues = true;
         mMap.clear();
     }
 
@@ -1844,8 +1872,8 @@
             // had been constructed with parcel or to make sure they trigger deserialization of the
             // bundle immediately; neither of which is obvious.
             synchronized (this) {
-                initializeFromParcelLocked(parcel, /*recycleParcel=*/ false, isNativeBundle);
-                unparcel(/* itemwise */ true);
+                mOwnsLazyValues = false;
+                initializeFromParcelLocked(parcel, /*ownsParcel*/ false, isNativeBundle);
             }
             return;
         }
@@ -1862,6 +1890,7 @@
                 + ": " + length + " bundle bytes starting at " + offset);
         p.setDataPosition(0);
 
+        mOwnsLazyValues = true;
         mParcelledByNative = isNativeBundle;
         mParcelledData = p;
     }
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index e5de3e1..f69d6b0 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -125,6 +125,7 @@
     boolean isUserUnlocked(int userId);
     boolean isUserRunning(int userId);
     boolean isUserForeground(int userId);
+    boolean isUserVisible(int userId);
     boolean isUserNameSet(int userId);
     boolean hasRestrictedProfiles(int userId);
     boolean requestQuietModeEnabled(String callingPackage, boolean enableQuietMode, int userId, in IntentSender target, int flags);
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index d553132..80201d3 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -71,7 +71,6 @@
 import java.util.function.BiFunction;
 import java.util.function.Function;
 import java.util.function.IntFunction;
-import java.util.function.Supplier;
 
 /**
  * Container for a message (data and object references) that can
@@ -5242,20 +5241,20 @@
      * Reads a map into {@code map}.
      *
      * @param sorted Whether the keys are sorted by their hashes, if so we use an optimized path.
-     * @param lazy   Whether to populate the map with lazy {@link Supplier} objects for
+     * @param lazy   Whether to populate the map with lazy {@link Function} objects for
      *               length-prefixed values. See {@link Parcel#readLazyValue(ClassLoader)} for more
      *               details.
-     * @return whether the parcel can be recycled or not.
+     * @return a count of the lazy values in the map
      * @hide
      */
-    boolean readArrayMap(ArrayMap<? super String, Object> map, int size, boolean sorted,
+    int readArrayMap(ArrayMap<? super String, Object> map, int size, boolean sorted,
             boolean lazy, @Nullable ClassLoader loader) {
-        boolean recycle = true;
+        int lazyValues = 0;
         while (size > 0) {
             String key = readString();
             Object value = (lazy) ? readLazyValue(loader) : readValue(loader);
             if (value instanceof LazyValue) {
-                recycle = false;
+                lazyValues++;
             }
             if (sorted) {
                 map.append(key, value);
@@ -5267,7 +5266,7 @@
         if (sorted) {
             map.validate();
         }
-        return recycle;
+        return lazyValues;
     }
 
     /**
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 8d3509c..c802e56 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -2838,6 +2838,50 @@
     }
 
     /**
+     * @hide
+     */
+    public static boolean isUsersOnSecondaryDisplaysEnabled() {
+        return SystemProperties.getBoolean("fw.users_on_secondary_displays",
+                Resources.getSystem()
+                        .getBoolean(R.bool.config_multiuserUsersOnSecondaryDisplays));
+    }
+
+    /**
+     * Returns whether the device allows users to run (and launch activities) on secondary displays.
+     *
+     * @return {@code false} for most devices, except automotive vehicles with passenger displays.
+     *
+     * @hide
+     */
+    public boolean isUsersOnSecondaryDisplaysSupported() {
+        return isUsersOnSecondaryDisplaysEnabled();
+    }
+
+    /**
+     * Checks if the user is visible at the moment.
+     *
+     * <p>Roughly speaking, a "visible user" is a user that can present UI on at least one display.
+     * It includes:
+     *
+     * <ol>
+     *   <li>The current foreground user in the main display.
+     *   <li>Current background users in secondary displays (for example, passenger users on
+     *   automotive, using the display associated with their seats).
+     *   <li>Profile users (in the running / started state) of other visible users.
+     * </ol>
+     *
+     * @return whether the user is visible at the moment, as defined above.
+     */
+    @UserHandleAware
+    public boolean isUserVisible() {
+        try {
+            return mService.isUserVisible(mUserId);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Return whether the context user is running in an "unlocked" state.
      * <p>
      * On devices with direct boot, a user is unlocked only after they've
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 9000899..6fc2811 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -14257,7 +14257,7 @@
          *
          * @hide
          */
-        public static final int DEFAULT_ENABLE_TARE = 1;
+        public static final int DEFAULT_ENABLE_TARE = 0;
 
         /**
          * Whether to enable the TARE AlarmManager economic policy or not.
diff --git a/core/java/android/view/IWindowFocusObserver.aidl b/core/java/android/view/IWindowFocusObserver.aidl
index d14bb48..3b23c77 100644
--- a/core/java/android/view/IWindowFocusObserver.aidl
+++ b/core/java/android/view/IWindowFocusObserver.aidl
@@ -16,7 +16,7 @@
 package android.view;
 
 /** {@hide} */
-interface IWindowFocusObserver
+oneway interface IWindowFocusObserver
 {
     void focusGained(IBinder inputToken);
     void focusLost(IBinder inputToken);
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index cce3e8c..a2cb1d5 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -733,7 +733,7 @@
         }
         for (@InternalInsetsType int type = 0; type < InsetsState.SIZE; type++) {
             // Only update the server side insets here.
-            if (type == ITYPE_CAPTION_BAR) continue;
+            if (!CAPTION_ON_SHELL && type == ITYPE_CAPTION_BAR) continue;
             InsetsSource source = mState.peekSource(type);
             if (source == null) continue;
             if (newState.peekSource(type) == null) {
diff --git a/core/java/android/view/RemoteAnimationTarget.java b/core/java/android/view/RemoteAnimationTarget.java
index 44f419a..0db28d4 100644
--- a/core/java/android/view/RemoteAnimationTarget.java
+++ b/core/java/android/view/RemoteAnimationTarget.java
@@ -363,11 +363,11 @@
         pw.print(prefix); pw.print("leash="); pw.println(leash);
         pw.print(prefix); pw.print("taskInfo="); pw.println(taskInfo);
         pw.print(prefix); pw.print("allowEnterPip="); pw.println(allowEnterPip);
-        pw.print(prefix); pw.print("windowType="); pw.print(windowType);
-        pw.print(prefix); pw.print("hasAnimatingParent="); pw.print(hasAnimatingParent);
-        pw.print(prefix); pw.print("backgroundColor="); pw.print(backgroundColor);
-        pw.print(prefix); pw.print("showBackdrop="); pw.print(showBackdrop);
-        pw.print(prefix); pw.print("willShowImeOnTarget="); pw.print(willShowImeOnTarget);
+        pw.print(prefix); pw.print("windowType="); pw.println(windowType);
+        pw.print(prefix); pw.print("hasAnimatingParent="); pw.println(hasAnimatingParent);
+        pw.print(prefix); pw.print("backgroundColor="); pw.println(backgroundColor);
+        pw.print(prefix); pw.print("showBackdrop="); pw.println(showBackdrop);
+        pw.print(prefix); pw.print("willShowImeOnTarget="); pw.println(willShowImeOnTarget);
     }
 
     public void dumpDebug(ProtoOutputStream proto, long fieldId) {
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 5f6c24b..84f04c1 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -172,7 +172,6 @@
     private static native boolean nativeGetAnimationFrameStats(WindowAnimationFrameStats outStats);
 
     private static native long[] nativeGetPhysicalDisplayIds();
-    private static native long nativeGetPrimaryPhysicalDisplayId();
     private static native IBinder nativeGetPhysicalDisplayToken(long physicalDisplayId);
     private static native IBinder nativeCreateDisplay(String name, boolean secure);
     private static native void nativeDestroyDisplay(IBinder displayToken);
@@ -2395,15 +2394,6 @@
     }
 
     /**
-     * Exposed to identify the correct display to apply the primary display orientation. Avoid using
-     * for any other purpose.
-     * @hide
-     */
-    public static long getPrimaryPhysicalDisplayId() {
-        return nativeGetPrimaryPhysicalDisplayId();
-    }
-
-    /**
      * @hide
      */
     public static IBinder getPhysicalDisplayToken(long physicalDisplayId) {
diff --git a/core/java/android/view/SurfaceControlViewHost.java b/core/java/android/view/SurfaceControlViewHost.java
index d75ff2f..5721fa6 100644
--- a/core/java/android/view/SurfaceControlViewHost.java
+++ b/core/java/android/view/SurfaceControlViewHost.java
@@ -422,7 +422,7 @@
     public void relayout(WindowManager.LayoutParams attrs,
             WindowlessWindowManager.ResizeCompleteCallback callback) {
         mViewRoot.setLayoutParams(attrs, false);
-        mViewRoot.setReportNextDraw(true /* syncBuffer */);
+        mViewRoot.setReportNextDraw(true /* syncBuffer */, "scvh_relayout");
         mWm.setCompletionCallback(mViewRoot.mWindow.asBinder(), callback);
     }
 
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index e1966a0..b2854a3 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -277,7 +277,7 @@
      * @hide
      */
     public static final boolean CAPTION_ON_SHELL =
-            SystemProperties.getBoolean("persist.debug.caption_on_shell", false);
+            SystemProperties.getBoolean("persist.wm.debug.caption_on_shell", false);
 
     /**
      * Whether the client should compute the window frame on its own.
@@ -587,8 +587,21 @@
     int mContentCaptureEnabled = CONTENT_CAPTURE_ENABLED_NOT_CHECKED;
     boolean mPerformContentCapture;
 
-
     boolean mReportNextDraw;
+    /** Set only while mReportNextDraw=true, indicating the last reason that was triggered */
+    String mLastReportNextDrawReason;
+    /** The reaason the last call to performDraw() returned false */
+    String mLastPerformDrawSkippedReason;
+    /** The reason the last call to performTraversals() returned without drawing */
+    String mLastPerformTraversalsSkipDrawReason;
+    /** The state of the local sync, if one is in progress. Can be one of the states below. */
+    int mLocalSyncState;
+
+    // The possible states of the local sync, see createSyncIfNeeded()
+    private final int LOCAL_SYNC_NONE = 0;
+    private final int LOCAL_SYNC_PENDING = 1;
+    private final int LOCAL_SYNC_RETURNED = 2;
+    private final int LOCAL_SYNC_MERGED = 3;
 
     /**
      * Set whether the draw should send the buffer to system server. When set to true, VRI will
@@ -1836,7 +1849,7 @@
         mSyncSeqId = args.argi4 > mSyncSeqId ? args.argi4 : mSyncSeqId;
 
         if (msg == MSG_RESIZED_REPORT) {
-            reportNextDraw();
+            reportNextDraw("resized");
         }
 
         if (mView != null && (frameChanged || configChanged)) {
@@ -2741,6 +2754,8 @@
     }
 
     private void performTraversals() {
+        mLastPerformTraversalsSkipDrawReason = null;
+
         // cache mView since it is used so much below...
         final View host = mView;
         if (DBG) {
@@ -2750,12 +2765,14 @@
         }
 
         if (host == null || !mAdded) {
+            mLastPerformTraversalsSkipDrawReason = host == null ? "no_host" : "not_added";
             return;
         }
 
         mIsInTraversal = true;
         mWillDrawSoon = true;
         boolean cancelDraw = false;
+        String cancelReason = null;
         boolean isSyncRequest = false;
 
         boolean windowSizeMayChange = false;
@@ -3038,13 +3055,14 @@
                 relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
                 cancelDraw = (relayoutResult & RELAYOUT_RES_CANCEL_AND_REDRAW)
                         == RELAYOUT_RES_CANCEL_AND_REDRAW;
+                cancelReason = "relayout";
                 final boolean dragResizing = mPendingDragResizing;
                 if (mSyncSeqId > mLastSyncSeqId) {
                     mLastSyncSeqId = mSyncSeqId;
                     if (DEBUG_BLAST) {
                         Log.d(mTag, "Relayout called with blastSync");
                     }
-                    reportNextDraw();
+                    reportNextDraw("relayout");
                     mSyncBuffer = true;
                     isSyncRequest = true;
                     if (!cancelDraw) {
@@ -3147,6 +3165,7 @@
                             }
                         } catch (OutOfResourcesException e) {
                             handleOutOfResourcesException(e);
+                            mLastPerformTraversalsSkipDrawReason = "oom_initialize_renderer";
                             return;
                         }
                     }
@@ -3184,6 +3203,7 @@
                         mAttachInfo.mThreadedRenderer.updateSurface(mSurface);
                     } catch (OutOfResourcesException e) {
                         handleOutOfResourcesException(e);
+                        mLastPerformTraversalsSkipDrawReason = "oom_update_surface";
                         return;
                     }
                 }
@@ -3349,6 +3369,7 @@
             if (mCheckIfCanDraw) {
                 try {
                     cancelDraw = mWindowSession.cancelDraw(mWindow);
+                    cancelReason = "wm_sync";
                     if (DEBUG_BLAST) {
                         Log.d(mTag, "cancelDraw returned " + cancelDraw);
                     }
@@ -3571,19 +3592,21 @@
         mImeFocusController.onTraversal(hasWindowFocus, mWindowAttributes);
 
         if ((relayoutResult & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {
-            reportNextDraw();
+            reportNextDraw("first_relayout");
         }
 
         mCheckIfCanDraw = isSyncRequest || cancelDraw;
 
-        boolean cancelAndRedraw =
-                mAttachInfo.mTreeObserver.dispatchOnPreDraw() || (cancelDraw && mDrewOnceForSync);
+        boolean cancelDueToPreDrawListener = mAttachInfo.mTreeObserver.dispatchOnPreDraw();
+        boolean cancelAndRedraw = cancelDueToPreDrawListener
+                 || (cancelDraw && mDrewOnceForSync);
         if (!cancelAndRedraw) {
             createSyncIfNeeded();
             mDrewOnceForSync = true;
         }
 
         if (!isViewVisible) {
+            mLastPerformTraversalsSkipDrawReason = "view_not_visible";
             if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
                 for (int i = 0; i < mPendingTransitions.size(); ++i) {
                     mPendingTransitions.get(i).endChangingAnimations();
@@ -3595,6 +3618,9 @@
                 mSyncBufferCallback.onBufferReady(null);
             }
         } else if (cancelAndRedraw) {
+            mLastPerformTraversalsSkipDrawReason = cancelDueToPreDrawListener
+                ? "predraw_" + mAttachInfo.mTreeObserver.getLastDispatchOnPreDrawCanceledReason()
+                : "cancel_" + cancelReason;
             // Try again
             scheduleTraversals();
         } else {
@@ -3618,11 +3644,13 @@
 
         if (!cancelAndRedraw) {
             mReportNextDraw = false;
+            mLastReportNextDrawReason = null;
             mSyncBufferCallback = null;
             mSyncBuffer = false;
             if (isInLocalSync()) {
                 mSyncGroup.markSyncReady();
                 mSyncGroup = null;
+                mLocalSyncState = LOCAL_SYNC_NONE;
             }
         }
     }
@@ -3634,12 +3662,15 @@
         }
 
         final int seqId = mSyncSeqId;
+        mLocalSyncState = LOCAL_SYNC_PENDING;
         mSyncGroup = new SurfaceSyncGroup(transaction -> {
+            mLocalSyncState = LOCAL_SYNC_RETURNED;
             // Callback will be invoked on executor thread so post to main thread.
             mHandler.postAtFrontOfQueue(() -> {
                 if (transaction != null) {
                     mSurfaceChangedTransaction.merge(transaction);
                 }
+                mLocalSyncState = LOCAL_SYNC_MERGED;
                 reportDrawFinished(seqId);
             });
         });
@@ -4353,9 +4384,12 @@
     }
 
     private boolean performDraw() {
+        mLastPerformDrawSkippedReason = null;
         if (mAttachInfo.mDisplayState == Display.STATE_OFF && !mReportNextDraw) {
+            mLastPerformDrawSkippedReason = "screen_off";
             return false;
         } else if (mView == null) {
+            mLastPerformDrawSkippedReason = "no_root_view";
             return false;
         }
 
@@ -8458,6 +8492,21 @@
         if (mTraversalScheduled) {
             writer.println(innerPrefix + " (barrier=" + mTraversalBarrier + ")");
         }
+        writer.println(innerPrefix + "mReportNextDraw=" + mReportNextDraw);
+        if (mReportNextDraw) {
+            writer.println(innerPrefix + " (reason=" + mLastReportNextDrawReason + ")");
+        }
+        if (mLastPerformTraversalsSkipDrawReason != null) {
+            writer.println(innerPrefix + "mLastPerformTraversalsFailedReason="
+                + mLastPerformTraversalsSkipDrawReason);
+        }
+        if (mLastPerformDrawSkippedReason != null) {
+            writer.println(innerPrefix + "mLastPerformDrawFailedReason="
+                + mLastPerformDrawSkippedReason);
+        }
+        if (mLocalSyncState != LOCAL_SYNC_NONE) {
+            writer.println(innerPrefix + "mLocalSyncState=" + mLocalSyncState);
+        }
         writer.println(innerPrefix + "mIsAmbientMode="  + mIsAmbientMode);
         writer.println(innerPrefix + "mUnbufferedInputSource="
                 + Integer.toHexString(mUnbufferedInputSource));
@@ -9958,11 +10007,12 @@
         }
     }
 
-    private void reportNextDraw() {
+    private void reportNextDraw(String reason) {
         if (DEBUG_BLAST) {
             Log.d(mTag, "reportNextDraw " + Debug.getCallers(5));
         }
         mReportNextDraw = true;
+        mLastReportNextDrawReason = reason;
     }
 
     /**
@@ -9975,11 +10025,12 @@
      * @param syncBuffer If true, the transaction that contains the buffer from the draw should be
      *                   sent to system to be synced. If false, VRI will not try to sync the buffer,
      *                   but only report back that a buffer was drawn.
+     * @param reason A debug string indicating the reason for reporting the next draw
      * @hide
      */
-    public void setReportNextDraw(boolean syncBuffer) {
+    public void setReportNextDraw(boolean syncBuffer, String reason) {
         mSyncBuffer = syncBuffer;
-        reportNextDraw();
+        reportNextDraw(reason);
         invalidate();
     }
 
diff --git a/core/java/android/view/ViewTreeObserver.java b/core/java/android/view/ViewTreeObserver.java
index 2c077c3..c9526fd 100644
--- a/core/java/android/view/ViewTreeObserver.java
+++ b/core/java/android/view/ViewTreeObserver.java
@@ -74,6 +74,9 @@
      * that the listener will be immediately called. */
     private boolean mWindowShown;
 
+    // The reason that the last call to dispatchOnPreDraw() returned true to cancel and redraw
+    private String mLastDispatchOnPreDrawCanceledReason;
+
     private boolean mAlive = true;
 
     /**
@@ -1161,6 +1164,7 @@
      */
     @SuppressWarnings("unchecked")
     public final boolean dispatchOnPreDraw() {
+        mLastDispatchOnPreDrawCanceledReason = null;
         boolean cancelDraw = false;
         final CopyOnWriteArray<OnPreDrawListener> listeners = mOnPreDrawListeners;
         if (listeners != null && listeners.size() > 0) {
@@ -1168,7 +1172,11 @@
             try {
                 int count = access.size();
                 for (int i = 0; i < count; i++) {
-                    cancelDraw |= !(access.get(i).onPreDraw());
+                    final OnPreDrawListener preDrawListener = access.get(i);
+                    cancelDraw |= !(preDrawListener.onPreDraw());
+                    if (cancelDraw) {
+                        mLastDispatchOnPreDrawCanceledReason = preDrawListener.getClass().getName();
+                    }
                 }
             } finally {
                 listeners.end();
@@ -1178,6 +1186,15 @@
     }
 
     /**
+     * @return the reason that the last call to dispatchOnPreDraw() returned true to cancel the
+     *         current draw, or null if the last call did not cancel.
+     * @hide
+     */
+    final String getLastDispatchOnPreDrawCanceledReason() {
+        return mLastDispatchOnPreDrawCanceledReason;
+    }
+
+    /**
      * Notifies registered listeners that the window is now shown
      * @hide
      */
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index e56c43e..6f69361 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -834,8 +834,8 @@
      *
      * <p>Default is {@code false}.
      *
-     * <p>The system enforcement will be added in Android 14, but some devices may start following
-     * the requirement before that. The best practice for apps is to always explicitly set this
+     * <p>The system enforcement is added in Android 14, but some devices may start following the
+     * requirement before that. The best practice for apps is to always explicitly set this
      * property in AndroidManifest instead of relying on the default value.
      *
      * <p>Example usage:
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 2879cd8..a339062 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -594,8 +594,8 @@
      *  SUBCLASSES MUST BE IMMUTABLE SO CLONE WORKS!!!!!
      */
     private abstract static class Action implements Parcelable {
-        public abstract void apply(View root, ViewGroup rootParent, InteractionHandler handler,
-                ColorResources colorResources) throws ActionException;
+        public abstract void apply(View root, ViewGroup rootParent, ActionApplyParams params)
+                throws ActionException;
 
         public static final int MERGE_REPLACE = 0;
         public static final int MERGE_APPEND = 1;
@@ -626,7 +626,7 @@
          * Override this if some of the tasks can be performed async.
          */
         public Action initActionAsync(ViewTree root, ViewGroup rootParent,
-                InteractionHandler handler, ColorResources colorResources) {
+                ActionApplyParams params) {
             return this;
         }
 
@@ -661,9 +661,7 @@
     // Constant used during async execution. It is not parcelable.
     private static final Action ACTION_NOOP = new RuntimeAction() {
         @Override
-        public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
-                ColorResources colorResources) {
-        }
+        public void apply(View root, ViewGroup rootParent, ActionApplyParams params) { }
     };
 
     /**
@@ -798,8 +796,7 @@
         }
 
         @Override
-        public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
-                ColorResources colorResources) {
+        public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
             final View view = root.findViewById(viewId);
             if (!(view instanceof AdapterView<?>)) return;
 
@@ -834,8 +831,7 @@
         }
 
         @Override
-        public void apply(View root, ViewGroup rootParent, final InteractionHandler handler,
-                ColorResources colorResources) {
+        public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
             final View target = root.findViewById(viewId);
             if (target == null) return;
 
@@ -846,7 +842,7 @@
                 OnItemClickListener listener = (parent, view, position, id) -> {
                     RemoteResponse response = findRemoteResponseTag(view);
                     if (response != null) {
-                        response.handleViewInteraction(view, handler);
+                        response.handleViewInteraction(view, params.handler);
                     }
                 };
                 av.setOnItemClickListener(listener);
@@ -910,8 +906,7 @@
         }
 
         @Override
-        public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
-                ColorResources colorResources) {
+        public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
             final View target = root.findViewById(viewId);
             if (target == null) return;
 
@@ -935,7 +930,7 @@
                     ((RemoteViewsListAdapter) a).setViewsList(list);
                 } else {
                     v.setAdapter(new RemoteViewsListAdapter(v.getContext(), list, viewTypeCount,
-                            colorResources));
+                            params.colorResources));
                 }
             } else if (target instanceof AdapterViewAnimator) {
                 AdapterViewAnimator v = (AdapterViewAnimator) target;
@@ -944,7 +939,7 @@
                     ((RemoteViewsListAdapter) a).setViewsList(list);
                 } else {
                     v.setAdapter(new RemoteViewsListAdapter(v.getContext(), list, viewTypeCount,
-                            colorResources));
+                            params.colorResources));
                 }
             }
         }
@@ -1025,8 +1020,8 @@
         }
 
         @Override
-        public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
-                ColorResources colorResources) throws ActionException {
+        public void apply(View root, ViewGroup rootParent, ActionApplyParams params)
+                throws ActionException {
             View target = root.findViewById(viewId);
             if (target == null) return;
 
@@ -1053,7 +1048,7 @@
                     && adapter.getViewTypeCount() >= mItems.getViewTypeCount()) {
                 try {
                     ((RemoteCollectionItemsAdapter) adapter).setData(
-                            mItems, handler, colorResources);
+                            mItems, params.handler, params.colorResources);
                 } catch (Throwable throwable) {
                     // setData should never failed with the validation in the items builder, but if
                     // it does, catch and rethrow.
@@ -1063,8 +1058,8 @@
             }
 
             try {
-                adapterView.setAdapter(
-                        new RemoteCollectionItemsAdapter(mItems, handler, colorResources));
+                adapterView.setAdapter(new RemoteCollectionItemsAdapter(mItems,
+                        params.handler, params.colorResources));
             } catch (Throwable throwable) {
                 // This could throw if the AdapterView somehow doesn't accept BaseAdapter due to
                 // a type error.
@@ -1095,8 +1090,7 @@
         }
 
         @Override
-        public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
-                ColorResources colorResources) {
+        public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
             final View target = root.findViewById(viewId);
             if (target == null) return;
 
@@ -1124,17 +1118,17 @@
             if (target instanceof AbsListView) {
                 AbsListView v = (AbsListView) target;
                 v.setRemoteViewsAdapter(intent, isAsync);
-                v.setRemoteViewsInteractionHandler(handler);
+                v.setRemoteViewsInteractionHandler(params.handler);
             } else if (target instanceof AdapterViewAnimator) {
                 AdapterViewAnimator v = (AdapterViewAnimator) target;
                 v.setRemoteViewsAdapter(intent, isAsync);
-                v.setRemoteViewsOnClickHandler(handler);
+                v.setRemoteViewsOnClickHandler(params.handler);
             }
         }
 
         @Override
         public Action initActionAsync(ViewTree root, ViewGroup rootParent,
-                InteractionHandler handler, ColorResources colorResources) {
+                ActionApplyParams params) {
             SetRemoteViewsAdapterIntent copy = new SetRemoteViewsAdapterIntent(viewId, intent);
             copy.isAsync = true;
             return copy;
@@ -1173,8 +1167,7 @@
         }
 
         @Override
-        public void apply(View root, ViewGroup rootParent, final InteractionHandler handler,
-                ColorResources colorResources) {
+        public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
             final View target = root.findViewById(viewId);
             if (target == null) return;
 
@@ -1215,7 +1208,7 @@
                 target.setTagInternal(com.android.internal.R.id.fillInIntent, null);
                 return;
             }
-            target.setOnClickListener(v -> mResponse.handleViewInteraction(v, handler));
+            target.setOnClickListener(v -> mResponse.handleViewInteraction(v, params.handler));
         }
 
         @Override
@@ -1253,8 +1246,7 @@
         }
 
         @Override
-        public void apply(View root, ViewGroup rootParent, final InteractionHandler handler,
-                ColorResources colorResources) {
+        public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
             final View target = root.findViewById(viewId);
             if (target == null) return;
             if (!(target instanceof CompoundButton)) {
@@ -1287,7 +1279,7 @@
             }
 
             OnCheckedChangeListener onCheckedChangeListener =
-                    (v, isChecked) -> mResponse.handleViewInteraction(v, handler);
+                    (v, isChecked) -> mResponse.handleViewInteraction(v, params.handler);
             button.setTagInternal(R.id.remote_checked_change_listener_tag, onCheckedChangeListener);
             button.setOnCheckedChangeListener(onCheckedChangeListener);
         }
@@ -1459,8 +1451,7 @@
         }
 
         @Override
-        public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
-                ColorResources colorResources) {
+        public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
             final View target = root.findViewById(viewId);
             if (target == null) return;
 
@@ -1517,8 +1508,7 @@
         }
 
         @Override
-        public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
-                ColorResources colorResources) {
+        public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
             final View target = root.findViewById(viewId);
             if (target == null) return;
 
@@ -1561,8 +1551,7 @@
         }
 
         @Override
-        public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
-                ColorResources colorResources) {
+        public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
             final View view = root.findViewById(viewId);
             if (view == null) return;
 
@@ -1598,7 +1587,13 @@
 
         public BitmapCache(Parcel source) {
             mBitmaps = source.createTypedArrayList(Bitmap.CREATOR);
-            mBitmapHashes = source.readSparseIntArray();
+            mBitmapHashes = new SparseIntArray();
+            for (int i = 0; i < mBitmaps.size(); i++) {
+                Bitmap b = mBitmaps.get(i);
+                if (b != null) {
+                    mBitmapHashes.put(b.hashCode(), i);
+                }
+            }
         }
 
         public int getBitmapId(Bitmap b) {
@@ -1614,7 +1609,7 @@
                         b = b.asShared();
                     }
                     mBitmaps.add(b);
-                    mBitmapHashes.put(mBitmaps.size() - 1, hash);
+                    mBitmapHashes.put(hash, mBitmaps.size() - 1);
                     mBitmapMemory = -1;
                     return (mBitmaps.size() - 1);
                 }
@@ -1631,7 +1626,6 @@
 
         public void writeBitmapsToParcel(Parcel dest, int flags) {
             dest.writeTypedList(mBitmaps, flags);
-            dest.writeSparseIntArray(mBitmapHashes);
         }
 
         public int getBitmapMemory() {
@@ -1675,12 +1669,12 @@
         }
 
         @Override
-        public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
-                ColorResources colorResources) throws ActionException {
+        public void apply(View root, ViewGroup rootParent, ActionApplyParams params)
+                throws ActionException {
             ReflectionAction ra = new ReflectionAction(viewId, methodName,
                     BaseReflectionAction.BITMAP,
                     bitmap);
-            ra.apply(root, rootParent, handler, colorResources);
+            ra.apply(root, rootParent, params);
         }
 
         @Override
@@ -1756,8 +1750,7 @@
         protected abstract Object getParameterValue(@Nullable View view) throws ActionException;
 
         @Override
-        public final void apply(View root, ViewGroup rootParent, InteractionHandler handler,
-                ColorResources colorResources) {
+        public final void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
             final View view = root.findViewById(viewId);
             if (view == null) return;
 
@@ -1775,7 +1768,7 @@
 
         @Override
         public final Action initActionAsync(ViewTree root, ViewGroup rootParent,
-                InteractionHandler handler, ColorResources colorResources) {
+                ActionApplyParams params) {
             final View view = root.findViewById(viewId);
             if (view == null) return ACTION_NOOP;
 
@@ -2307,8 +2300,7 @@
         }
 
         @Override
-        public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
-                ColorResources colorResources) {
+        public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
             mRunnable.run();
         }
     }
@@ -2421,8 +2413,7 @@
         }
 
         @Override
-        public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
-                ColorResources colorResources) {
+        public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
             final Context context = root.getContext();
             final ViewGroup target = root.findViewById(viewId);
 
@@ -2451,8 +2442,7 @@
                             target.removeViews(nextChild, recycledViewIndex - nextChild);
                         }
                         setNextRecyclableChild(target, nextChild + 1, target.getChildCount());
-                        rvToApply.reapplyNestedViews(context, child, rootParent, handler,
-                                null /* size */, colorResources);
+                        rvToApply.reapplyNestedViews(context, child, rootParent, params);
                         return;
                     }
                     // If we cannot recycle the views, we still remove all views in between to
@@ -2463,8 +2453,7 @@
             // If we cannot recycle, insert the new view before the next recyclable child.
 
             // Inflate nested views and add as children
-            View nestedView = rvToApply.applyNestedViews(context, target, rootParent, handler,
-                    null /* size */, colorResources);
+            View nestedView = rvToApply.apply(context, target, rootParent, null /* size */, params);
             if (mStableId != NO_ID) {
                 setStableId(nestedView, mStableId);
             }
@@ -2477,7 +2466,7 @@
 
         @Override
         public Action initActionAsync(ViewTree root, ViewGroup rootParent,
-                InteractionHandler handler, ColorResources colorResources) {
+                ActionApplyParams params) {
             // In the async implementation, update the view tree so that subsequent calls to
             // findViewById return the current view.
             root.createTree();
@@ -2511,8 +2500,7 @@
                         setNextRecyclableChild(targetVg, nextChild + 1, target.mChildren.size());
                         final AsyncApplyTask reapplyTask = rvToApply.getInternalAsyncApplyTask(
                                 context,
-                                targetVg, null /* listener */, handler, null /* size */,
-                                colorResources,
+                                targetVg, null /* listener */, params, null /* size */,
                                 recycled.mRoot);
                         final ViewTree tree = reapplyTask.doInBackground();
                         if (tree == null) {
@@ -2521,8 +2509,7 @@
                         return new RuntimeAction() {
                             @Override
                             public void apply(View root, ViewGroup rootParent,
-                                    InteractionHandler handler, ColorResources colorResources)
-                                    throws ActionException {
+                                    ActionApplyParams params) throws ActionException {
                                 reapplyTask.onPostExecute(tree);
                                 if (recycledViewIndex > nextChild) {
                                     targetVg.removeViews(nextChild, recycledViewIndex - nextChild);
@@ -2533,23 +2520,22 @@
                     // If the layout id is different, still remove the children as if we recycled
                     // the view, to insert at the same place.
                     target.removeChildren(nextChild, recycledViewIndex - nextChild + 1);
-                    return insertNewView(context, target, handler, colorResources,
+                    return insertNewView(context, target, params,
                             () -> targetVg.removeViews(nextChild,
                                     recycledViewIndex - nextChild + 1));
 
                 }
             }
             // If we cannot recycle, simply add the view at the same available slot.
-            return insertNewView(context, target, handler, colorResources, () -> {});
+            return insertNewView(context, target, params, () -> {});
         }
 
-        private Action insertNewView(Context context, ViewTree target, InteractionHandler handler,
-                ColorResources colorResources, Runnable finalizeAction) {
+        private Action insertNewView(Context context, ViewTree target,
+                ActionApplyParams params, Runnable finalizeAction) {
             ViewGroup targetVg = (ViewGroup) target.mRoot;
             int nextChild = getNextRecyclableChild(targetVg);
             final AsyncApplyTask task = mNestedViews.getInternalAsyncApplyTask(context, targetVg,
-                    null /* listener */, handler, null /* size */, colorResources,
-                    null /* result */);
+                    null /* listener */, params, null /* size */,  null /* result */);
             final ViewTree tree = task.doInBackground();
 
             if (tree == null) {
@@ -2569,8 +2555,7 @@
 
             return new RuntimeAction() {
                 @Override
-                public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
-                        ColorResources colorResources) throws ActionException {
+                public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
                     task.onPostExecute(tree);
                     finalizeAction.run();
                     targetVg.addView(task.mResult, insertIndex);
@@ -2627,8 +2612,7 @@
         }
 
         @Override
-        public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
-                ColorResources colorResources) {
+        public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
             final ViewGroup target = root.findViewById(viewId);
 
             if (target == null) {
@@ -2652,7 +2636,7 @@
 
         @Override
         public Action initActionAsync(ViewTree root, ViewGroup rootParent,
-                InteractionHandler handler, ColorResources colorResources) {
+                ActionApplyParams params) {
             // In the async implementation, update the view tree so that subsequent calls to
             // findViewById return the current view.
             root.createTree();
@@ -2676,8 +2660,7 @@
             }
             return new RuntimeAction() {
                 @Override
-                public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
-                        ColorResources colorResources) throws ActionException {
+                public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
                     if (mViewIdToKeep == REMOVE_ALL_VIEWS_ID) {
                         for (int i = targetVg.getChildCount() - 1; i >= 0; i--) {
                             if (!hasStableId(targetVg.getChildAt(i))) {
@@ -2736,8 +2719,7 @@
         }
 
         @Override
-        public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
-                ColorResources colorResources) {
+        public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
             final View target = root.findViewById(viewId);
 
             if (target == null || target == root) {
@@ -2752,7 +2734,7 @@
 
         @Override
         public Action initActionAsync(ViewTree root, ViewGroup rootParent,
-                InteractionHandler handler, ColorResources colorResources) {
+                ActionApplyParams params) {
             // In the async implementation, update the view tree so that subsequent calls to
             // findViewById return the correct view.
             root.createTree();
@@ -2771,8 +2753,7 @@
             parent.mChildren.remove(target);
             return new RuntimeAction() {
                 @Override
-                public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
-                        ColorResources colorResources) throws ActionException {
+                public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
                     parentVg.removeView(target.mRoot);
                 }
             };
@@ -2851,8 +2832,7 @@
         }
 
         @Override
-        public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
-                ColorResources colorResources) {
+        public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
             final TextView target = root.findViewById(viewId);
             if (target == null) return;
             if (drawablesLoaded) {
@@ -2883,7 +2863,7 @@
 
         @Override
         public Action initActionAsync(ViewTree root, ViewGroup rootParent,
-                InteractionHandler handler, ColorResources colorResources) {
+                ActionApplyParams params) {
             final TextView target = root.findViewById(viewId);
             if (target == null) return ACTION_NOOP;
 
@@ -2961,8 +2941,7 @@
         }
 
         @Override
-        public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
-                ColorResources colorResources) {
+        public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
             final TextView target = root.findViewById(viewId);
             if (target == null) return;
             target.setTextSize(units, size);
@@ -3007,8 +2986,7 @@
         }
 
         @Override
-        public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
-                ColorResources colorResources) {
+        public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
             final View target = root.findViewById(viewId);
             if (target == null) return;
             target.setPadding(left, top, right, bottom);
@@ -3084,8 +3062,7 @@
         }
 
         @Override
-        public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
-                ColorResources colorResources) {
+        public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
             final View target = root.findViewById(viewId);
             if (target == null) {
                 return;
@@ -3230,8 +3207,7 @@
         }
 
         @Override
-        public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
-                ColorResources colorResources) {
+        public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
             final View target = root.findViewById(viewId);
             if (target == null) return;
 
@@ -3266,8 +3242,7 @@
         }
 
         @Override
-        public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
-                ColorResources colorResources) {
+        public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
             // Let's traverse the viewtree and override all textColors!
             Stack<View> viewsToProcess = new Stack<>();
             viewsToProcess.add(root);
@@ -3317,8 +3292,7 @@
         }
 
         @Override
-        public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
-                ColorResources colorResources) {
+        public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
             final View target = root.findViewById(mViewId);
             if (target == null) return;
 
@@ -3352,8 +3326,7 @@
         }
 
         @Override
-        public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
-                ColorResources colorResources)
+        public void apply(View root, ViewGroup rootParent, ActionApplyParams params)
                 throws ActionException {
             final View target = root.findViewById(viewId);
             if (target == null) return;
@@ -3404,8 +3377,8 @@
         }
 
         @Override
-        public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
-                ColorResources colorResources) throws ActionException {
+        public void apply(View root, ViewGroup rootParent, ActionApplyParams params)
+                throws ActionException {
             final View target = root.findViewById(viewId);
             if (target == null) return;
 
@@ -3483,8 +3456,8 @@
         }
 
         @Override
-        public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
-                ColorResources colorResources) throws ActionException {
+        public void apply(View root, ViewGroup rootParent, ActionApplyParams params)
+                throws ActionException {
             final View target = root.findViewById(viewId);
             if (target == null) return;
 
@@ -5578,52 +5551,39 @@
     /** @hide */
     public View apply(@NonNull Context context, @NonNull ViewGroup parent,
             @Nullable InteractionHandler handler, @Nullable SizeF size) {
-        RemoteViews rvToApply = getRemoteViewsToApply(context, size);
-
-        View result = inflateView(context, rvToApply, parent);
-        rvToApply.performApply(result, parent, handler, null);
-        return result;
+        return apply(context, parent, size, new ActionApplyParams()
+                .withInteractionHandler(handler));
     }
 
     /** @hide */
     public View applyWithTheme(@NonNull Context context, @NonNull ViewGroup parent,
             @Nullable InteractionHandler handler, @StyleRes int applyThemeResId) {
-        return applyWithTheme(context, parent, handler, applyThemeResId, null);
-    }
-
-    /** @hide */
-    public View applyWithTheme(@NonNull Context context, @NonNull ViewGroup parent,
-            @Nullable InteractionHandler handler, @StyleRes int applyThemeResId,
-            @Nullable SizeF size) {
-        RemoteViews rvToApply = getRemoteViewsToApply(context, size);
-
-        View result = inflateView(context, rvToApply, parent, applyThemeResId, null);
-        rvToApply.performApply(result, parent, handler, null);
-        return result;
+        return apply(context, parent, null, new ActionApplyParams()
+                .withInteractionHandler(handler)
+                .withThemeResId(applyThemeResId));
     }
 
     /** @hide */
     public View apply(Context context, ViewGroup parent, InteractionHandler handler,
             @Nullable SizeF size, @Nullable ColorResources colorResources) {
-        RemoteViews rvToApply = getRemoteViewsToApply(context, size);
-
-        View result = inflateView(context, rvToApply, parent, 0, colorResources);
-        rvToApply.performApply(result, parent, handler, colorResources);
-        return result;
+        return apply(context, parent, size, new ActionApplyParams()
+                .withInteractionHandler(handler)
+                .withColorResources(colorResources));
     }
 
-    private View applyNestedViews(Context context, ViewGroup directParent,
-            ViewGroup rootParent, InteractionHandler handler, SizeF size,
-            ColorResources colorResources) {
-        RemoteViews rvToApply = getRemoteViewsToApply(context, size);
-
-        View result = inflateView(context, rvToApply, directParent, 0, colorResources);
-        rvToApply.performApply(result, rootParent, handler, colorResources);
-        return result;
+    /** @hide **/
+    public View apply(Context context, ViewGroup parent, @Nullable SizeF size,
+            ActionApplyParams params) {
+        return apply(context, parent, parent, size, params);
     }
 
-    private View inflateView(Context context, RemoteViews rv, ViewGroup parent) {
-        return inflateView(context, rv, parent, 0, null);
+    private View apply(Context context, ViewGroup directParent, ViewGroup rootParent,
+            @Nullable SizeF size, ActionApplyParams params) {
+        RemoteViews rvToApply = getRemoteViewsToApply(context, size);
+        View result = inflateView(context, rvToApply, directParent,
+                params.applyThemeResId, params.colorResources);
+        rvToApply.performApply(result, rootParent, params);
+        return result;
     }
 
     private View inflateView(Context context, RemoteViews rv, @Nullable ViewGroup parent,
@@ -5704,7 +5664,6 @@
         return applyAsync(context, parent, executor, listener, null /* handler */);
     }
 
-
     /** @hide */
     public CancellationSignal applyAsync(Context context, ViewGroup parent,
             Executor executor, OnViewAppliedListener listener, InteractionHandler handler) {
@@ -5723,16 +5682,19 @@
     public CancellationSignal applyAsync(Context context, ViewGroup parent, Executor executor,
             OnViewAppliedListener listener, InteractionHandler handler, SizeF size,
             ColorResources colorResources) {
+
+        ActionApplyParams params = new ActionApplyParams()
+                .withInteractionHandler(handler)
+                .withColorResources(colorResources)
+                .withExecutor(executor);
         return new AsyncApplyTask(getRemoteViewsToApply(context, size), parent, context, listener,
-                handler, colorResources, null /* result */,
-                true /* topLevel */).startTaskOnExecutor(executor);
+                params, null /* result */, true /* topLevel */).startTaskOnExecutor(executor);
     }
 
     private AsyncApplyTask getInternalAsyncApplyTask(Context context, ViewGroup parent,
-            OnViewAppliedListener listener, InteractionHandler handler, SizeF size,
-            ColorResources colorResources, View result) {
+            OnViewAppliedListener listener, ActionApplyParams params, SizeF size, View result) {
         return new AsyncApplyTask(getRemoteViewsToApply(context, size), parent, context, listener,
-                handler, colorResources, result, false /* topLevel */);
+                params, result, false /* topLevel */);
     }
 
     private class AsyncApplyTask extends AsyncTask<Void, Void, ViewTree>
@@ -5742,8 +5704,8 @@
         final ViewGroup mParent;
         final Context mContext;
         final OnViewAppliedListener mListener;
-        final InteractionHandler mHandler;
-        final ColorResources mColorResources;
+        final ActionApplyParams mApplyParams;
+
         /**
          * Whether the remote view is the top-level one (i.e. not within an action).
          *
@@ -5758,16 +5720,13 @@
 
         private AsyncApplyTask(
                 RemoteViews rv, ViewGroup parent, Context context, OnViewAppliedListener listener,
-                InteractionHandler handler, ColorResources colorResources,
-                View result, boolean topLevel) {
+                ActionApplyParams applyParams, View result, boolean topLevel) {
             mRV = rv;
             mParent = parent;
             mContext = context;
             mListener = listener;
-            mColorResources = colorResources;
-            mHandler = handler;
             mTopLevel = topLevel;
-
+            mApplyParams = applyParams;
             mResult = result;
         }
 
@@ -5776,17 +5735,18 @@
         protected ViewTree doInBackground(Void... params) {
             try {
                 if (mResult == null) {
-                    mResult = inflateView(mContext, mRV, mParent, 0, mColorResources);
+                    mResult = inflateView(mContext, mRV, mParent, 0, mApplyParams.colorResources);
                 }
 
                 mTree = new ViewTree(mResult);
+
                 if (mRV.mActions != null) {
                     int count = mRV.mActions.size();
                     mActions = new Action[count];
                     for (int i = 0; i < count && !isCancelled(); i++) {
                         // TODO: check if isCancelled in nested views.
-                        mActions[i] = mRV.mActions.get(i).initActionAsync(mTree, mParent, mHandler,
-                                mColorResources);
+                        mActions[i] = mRV.mActions.get(i)
+                                .initActionAsync(mTree, mParent, mApplyParams);
                     }
                 } else {
                     mActions = null;
@@ -5808,10 +5768,13 @@
 
                 try {
                     if (mActions != null) {
-                        InteractionHandler handler = mHandler == null
-                                ? DEFAULT_INTERACTION_HANDLER : mHandler;
+
+                        ActionApplyParams applyParams = mApplyParams.clone();
+                        if (applyParams.handler == null) {
+                            applyParams.handler = DEFAULT_INTERACTION_HANDLER;
+                        }
                         for (Action a : mActions) {
-                            a.apply(viewTree.mRoot, mParent, handler, mColorResources);
+                            a.apply(viewTree.mRoot, mParent, applyParams);
                         }
                     }
                     // If the parent of the view is has is a root, resolve the recycling.
@@ -5859,18 +5822,43 @@
      * the {@link #apply(Context,ViewGroup)} call.
      */
     public void reapply(Context context, View v) {
-        reapply(context, v, null /* handler */);
+        reapply(context, v, null /* size */, new ActionApplyParams());
     }
 
     /** @hide */
     public void reapply(Context context, View v, InteractionHandler handler) {
-        reapply(context, v, handler, null /* size */, null /* colorResources */);
+        reapply(context, v, null /* size */,
+                new ActionApplyParams().withInteractionHandler(handler));
     }
 
     /** @hide */
     public void reapply(Context context, View v, InteractionHandler handler, SizeF size,
             ColorResources colorResources) {
-        reapply(context, v, handler, size, colorResources, true);
+        reapply(context, v, size, new ActionApplyParams()
+                .withInteractionHandler(handler).withColorResources(colorResources));
+    }
+
+    /** @hide */
+    public void reapply(Context context, View v, @Nullable SizeF size, ActionApplyParams params) {
+        reapply(context, v, (ViewGroup) v.getParent(), size, params, true);
+    }
+
+    private void reapplyNestedViews(Context context, View v, ViewGroup rootParent,
+            ActionApplyParams params) {
+        reapply(context, v, rootParent, null, params, false);
+    }
+
+    // Note: topLevel should be true only for calls on the topLevel RemoteViews, internal calls
+    // should set it to false.
+    private void reapply(Context context, View v, ViewGroup rootParent,
+            @Nullable SizeF size, ActionApplyParams params, boolean topLevel) {
+        RemoteViews rvToApply = getRemoteViewsToReapply(context, v, size);
+        rvToApply.performApply(v, rootParent, params);
+
+        // If the parent of the view is has is a root, resolve the recycling.
+        if (topLevel && v instanceof ViewGroup) {
+            finalizeViewRecycling((ViewGroup) v);
+        }
     }
 
     /** @hide */
@@ -5922,27 +5910,6 @@
         return rvToApply;
     }
 
-    // Note: topLevel should be true only for calls on the topLevel RemoteViews, internal calls
-    // should set it to false.
-    private void reapply(Context context, View v, InteractionHandler handler, SizeF size,
-            ColorResources colorResources, boolean topLevel) {
-
-        RemoteViews rvToApply = getRemoteViewsToReapply(context, v, size);
-
-        rvToApply.performApply(v, (ViewGroup) v.getParent(), handler, colorResources);
-
-        // If the parent of the view is has is a root, resolve the recycling.
-        if (topLevel && v instanceof ViewGroup) {
-            finalizeViewRecycling((ViewGroup) v);
-        }
-    }
-
-    private void reapplyNestedViews(Context context, View v, ViewGroup rootParent,
-            InteractionHandler handler, SizeF size, ColorResources colorResources) {
-        RemoteViews rvToApply = getRemoteViewsToReapply(context, v, size);
-        rvToApply.performApply(v, rootParent, handler, colorResources);
-    }
-
     /**
      * Applies all the actions to the provided view, moving as much of the task on the background
      * thread as possible.
@@ -5973,19 +5940,25 @@
             ColorResources colorResources) {
         RemoteViews rvToApply = getRemoteViewsToReapply(context, v, size);
 
+        ActionApplyParams params = new ActionApplyParams()
+                .withColorResources(colorResources)
+                .withInteractionHandler(handler)
+                .withExecutor(executor);
+
         return new AsyncApplyTask(rvToApply, (ViewGroup) v.getParent(),
-                context, listener, handler, colorResources, v, true /* topLevel */)
+                context, listener, params, v, true /* topLevel */)
                 .startTaskOnExecutor(executor);
     }
 
-    private void performApply(View v, ViewGroup parent, InteractionHandler handler,
-            ColorResources colorResources) {
+    private void performApply(View v, ViewGroup parent, ActionApplyParams params) {
+        params = params.clone();
+        if (params.handler == null) {
+            params.handler = DEFAULT_INTERACTION_HANDLER;
+        }
         if (mActions != null) {
-            handler = handler == null ? DEFAULT_INTERACTION_HANDLER : handler;
             final int count = mActions.size();
             for (int i = 0; i < count; i++) {
-                Action a = mActions.get(i);
-                a.apply(v, parent, handler, colorResources);
+                mActions.get(i).apply(v, parent, params);
             }
         }
     }
@@ -6043,6 +6016,47 @@
     }
 
     /**
+     * Utility class to hold all the options when applying the remote views
+     * @hide
+     */
+    public class ActionApplyParams {
+
+        public InteractionHandler handler;
+        public ColorResources colorResources;
+        public Executor executor;
+        @StyleRes public int applyThemeResId;
+
+        @Override
+        public ActionApplyParams clone() {
+            return new ActionApplyParams()
+                    .withInteractionHandler(handler)
+                    .withColorResources(colorResources)
+                    .withExecutor(executor)
+                    .withThemeResId(applyThemeResId);
+        }
+
+        public ActionApplyParams withInteractionHandler(InteractionHandler handler) {
+            this.handler = handler;
+            return this;
+        }
+
+        public ActionApplyParams withColorResources(ColorResources colorResources) {
+            this.colorResources = colorResources;
+            return this;
+        }
+
+        public ActionApplyParams withThemeResId(@StyleRes int themeResId) {
+            this.applyThemeResId = themeResId;
+            return this;
+        }
+
+        public ActionApplyParams withExecutor(Executor executor) {
+            this.executor = executor;
+            return this;
+        }
+    }
+
+    /**
      * Object allowing the modification of a context to overload the system's dynamic colors.
      *
      * Only colors from {@link android.R.color#system_accent1_0} to
@@ -6056,10 +6070,12 @@
         // Size, in bytes, of an entry in the array of colors in an ARSC file.
         private static final int ARSC_ENTRY_SIZE = 16;
 
-        private ResourcesLoader mLoader;
+        private final ResourcesLoader mLoader;
+        private final SparseIntArray mColorMapping;
 
-        private ColorResources(ResourcesLoader loader) {
+        private ColorResources(ResourcesLoader loader, SparseIntArray colorMapping) {
             mLoader = loader;
+            mColorMapping = colorMapping;
         }
 
         /**
@@ -6071,6 +6087,10 @@
             context.getResources().addLoaders(mLoader);
         }
 
+        public SparseIntArray getColorMapping() {
+            return mColorMapping;
+        }
+
         private static ByteArrayOutputStream readFileContent(InputStream input) throws IOException {
             ByteArrayOutputStream content = new ByteArrayOutputStream(2048);
             byte[] buffer = new byte[4096];
@@ -6145,7 +6165,7 @@
                             ResourcesLoader colorsLoader = new ResourcesLoader();
                             colorsLoader.addProvider(ResourcesProvider
                                     .loadFromTable(pfd, null /* assetsProvider */));
-                            return new ColorResources(colorsLoader);
+                            return new ColorResources(colorsLoader, colorMapping.clone());
                         }
                     }
                 } finally {
diff --git a/core/java/android/window/ITaskFragmentOrganizer.aidl b/core/java/android/window/ITaskFragmentOrganizer.aidl
index 3335c9c..79ddadb 100644
--- a/core/java/android/window/ITaskFragmentOrganizer.aidl
+++ b/core/java/android/window/ITaskFragmentOrganizer.aidl
@@ -16,54 +16,9 @@
 
 package android.window;
 
-import android.content.Intent;
-import android.content.res.Configuration;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.window.TaskFragmentInfo;
+import android.window.TaskFragmentTransaction;
 
 /** @hide */
 oneway interface ITaskFragmentOrganizer {
-    void onTaskFragmentAppeared(in TaskFragmentInfo taskFragmentInfo);
-    void onTaskFragmentInfoChanged(in TaskFragmentInfo taskFragmentInfo);
-    void onTaskFragmentVanished(in TaskFragmentInfo taskFragmentInfo);
-
-    /**
-     * Called when the parent leaf Task of organized TaskFragments is changed.
-     * When the leaf Task is changed, the organizer may want to update the TaskFragments in one
-     * transaction.
-     *
-     * For case like screen size change, it will trigger onTaskFragmentParentInfoChanged with new
-     * Task bounds, but may not trigger onTaskFragmentInfoChanged because there can be an override
-     * bounds.
-     */
-    void onTaskFragmentParentInfoChanged(in IBinder fragmentToken, in Configuration parentConfig);
-
-    /**
-     * Called when the {@link WindowContainerTransaction} created with
-     * {@link WindowContainerTransaction#setErrorCallbackToken(IBinder)} failed on the server side.
-     *
-     * @param errorCallbackToken    Token set through {@link
-     *                              WindowContainerTransaction#setErrorCallbackToken(IBinder)}
-     * @param errorBundle       Bundle containing the exception, operation type and TaskFragmentInfo
-     *                          if any. Should be created with
-     *                          {@link TaskFragmentOrganizer#putErrorInfoInBundle}.
-     */
-    void onTaskFragmentError(in IBinder errorCallbackToken, in Bundle errorBundle);
-
-    /**
-     * Called when an Activity is reparented to the Task with organized TaskFragment. For example,
-     * when an Activity enters and then exits Picture-in-picture, it will be reparented back to its
-     * orginial Task. In this case, we need to notify the organizer so that it can check if the
-     * Activity matches any split rule.
-     *
-     * @param taskId            The Task that the activity is reparented to.
-     * @param activityIntent    The intent that the activity is original launched with.
-     * @param activityToken     If the activity belongs to the same process as the organizer, this
-     *                          will be the actual activity token; if the activity belongs to a
-     *                          different process, the server will generate a temporary token that
-     *                          the organizer can use to reparent the activity through
-     *                          {@link WindowContainerTransaction} if needed.
-     */
-    void onActivityReparentToTask(int taskId, in Intent activityIntent, in IBinder activityToken);
+    void onTransactionReady(in TaskFragmentTransaction transaction);
 }
diff --git a/core/java/android/window/TaskFragmentOrganizer.java b/core/java/android/window/TaskFragmentOrganizer.java
index 765cd81..e567ced 100644
--- a/core/java/android/window/TaskFragmentOrganizer.java
+++ b/core/java/android/window/TaskFragmentOrganizer.java
@@ -16,6 +16,13 @@
 
 package android.window;
 
+import static android.window.TaskFragmentTransaction.TYPE_ACTIVITY_REPARENT_TO_TASK;
+import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_APPEARED;
+import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_ERROR;
+import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_INFO_CHANGED;
+import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_PARENT_INFO_CHANGED;
+import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_VANISHED;
+
 import android.annotation.CallSuper;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -25,8 +32,11 @@
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.util.SparseArray;
 import android.view.RemoteAnimationDefinition;
 
+import java.util.ArrayList;
+import java.util.List;
 import java.util.concurrent.Executor;
 
 /**
@@ -64,6 +74,12 @@
      */
     private final Executor mExecutor;
 
+    // TODO(b/240519866): doing so to keep CTS compatibility. Remove in the next release.
+    /** Map from Task id to client tokens of TaskFragments in the Task. */
+    private final SparseArray<List<IBinder>> mTaskIdToFragmentTokens = new SparseArray<>();
+    /** Map from Task id to Task configuration. */
+    private final SparseArray<Configuration> mTaskIdToConfigurations = new SparseArray<>();
+
     public TaskFragmentOrganizer(@NonNull Executor executor) {
         mExecutor = executor;
     }
@@ -153,6 +169,27 @@
             @NonNull IBinder fragmentToken, @NonNull Configuration parentConfig) {}
 
     /**
+     * Called when the parent leaf Task of organized TaskFragments is changed.
+     * When the leaf Task is changed, the organizer may want to update the TaskFragments in one
+     * transaction.
+     *
+     * For case like screen size change, it will trigger onTaskFragmentParentInfoChanged with new
+     * Task bounds, but may not trigger onTaskFragmentInfoChanged because there can be an override
+     * bounds.
+     * @hide
+     */
+    public void onTaskFragmentParentInfoChanged(int taskId, @NonNull Configuration parentConfig) {
+        // TODO(b/240519866): doing so to keep CTS compatibility. Remove in the next release.
+        final List<IBinder> tokens = mTaskIdToFragmentTokens.get(taskId);
+        if (tokens == null || tokens.isEmpty()) {
+            return;
+        }
+        for (int i = tokens.size() - 1; i >= 0; i--) {
+            onTaskFragmentParentInfoChanged(tokens.get(i), parentConfig);
+        }
+    }
+
+    /**
      * Called when the {@link WindowContainerTransaction} created with
      * {@link WindowContainerTransaction#setErrorCallbackToken(IBinder)} failed on the server side.
      *
@@ -186,6 +223,76 @@
     public void onActivityReparentToTask(int taskId, @NonNull Intent activityIntent,
             @NonNull IBinder activityToken) {}
 
+    /**
+     * Called when the transaction is ready so that the organizer can update the TaskFragments based
+     * on the changes in transaction.
+     * @hide
+     */
+    public void onTransactionReady(@NonNull TaskFragmentTransaction transaction) {
+        final List<TaskFragmentTransaction.Change> changes = transaction.getChanges();
+        for (TaskFragmentTransaction.Change change : changes) {
+            // TODO(b/240519866): apply all changes in one WCT.
+            final int taskId = change.getTaskId();
+            switch (change.getType()) {
+                case TYPE_TASK_FRAGMENT_APPEARED:
+                    // TODO(b/240519866): doing so to keep CTS compatibility. Remove in the next
+                    // release.
+                    if (!mTaskIdToFragmentTokens.contains(taskId)) {
+                        mTaskIdToFragmentTokens.put(taskId, new ArrayList<>());
+                    }
+                    mTaskIdToFragmentTokens.get(taskId).add(change.getTaskFragmentToken());
+                    onTaskFragmentParentInfoChanged(change.getTaskFragmentToken(),
+                            mTaskIdToConfigurations.get(taskId));
+
+                    onTaskFragmentAppeared(change.getTaskFragmentInfo());
+                    break;
+                case TYPE_TASK_FRAGMENT_INFO_CHANGED:
+                    onTaskFragmentInfoChanged(change.getTaskFragmentInfo());
+                    break;
+                case TYPE_TASK_FRAGMENT_VANISHED:
+                    // TODO(b/240519866): doing so to keep CTS compatibility. Remove in the next
+                    // release.
+                    if (mTaskIdToFragmentTokens.contains(taskId)) {
+                        final List<IBinder> tokens = mTaskIdToFragmentTokens.get(taskId);
+                        tokens.remove(change.getTaskFragmentToken());
+                        if (tokens.isEmpty()) {
+                            mTaskIdToFragmentTokens.remove(taskId);
+                            mTaskIdToConfigurations.remove(taskId);
+                        }
+                    }
+
+                    onTaskFragmentVanished(change.getTaskFragmentInfo());
+                    break;
+                case TYPE_TASK_FRAGMENT_PARENT_INFO_CHANGED:
+                    // TODO(b/240519866): doing so to keep CTS compatibility. Remove in the next
+                    // release.
+                    mTaskIdToConfigurations.put(taskId, change.getTaskConfiguration());
+
+                    onTaskFragmentParentInfoChanged(taskId, change.getTaskConfiguration());
+                    break;
+                case TYPE_TASK_FRAGMENT_ERROR:
+                    final Bundle errorBundle = change.getErrorBundle();
+                    onTaskFragmentError(
+                            change.getErrorCallbackToken(),
+                            errorBundle.getParcelable(
+                                    KEY_ERROR_CALLBACK_TASK_FRAGMENT_INFO, TaskFragmentInfo.class),
+                            errorBundle.getInt(KEY_ERROR_CALLBACK_OP_TYPE),
+                            errorBundle.getSerializable(KEY_ERROR_CALLBACK_EXCEPTION,
+                                    java.lang.Throwable.class));
+                    break;
+                case TYPE_ACTIVITY_REPARENT_TO_TASK:
+                    onActivityReparentToTask(
+                            change.getTaskId(),
+                            change.getActivityIntent(),
+                            change.getActivityToken());
+                    break;
+                default:
+                    throw new IllegalArgumentException(
+                            "Unknown TaskFragmentEvent=" + change.getType());
+            }
+        }
+    }
+
     @Override
     public void applyTransaction(@NonNull WindowContainerTransaction t) {
         t.setTaskFragmentOrganizer(mInterface);
@@ -203,51 +310,8 @@
 
     private final ITaskFragmentOrganizer mInterface = new ITaskFragmentOrganizer.Stub() {
         @Override
-        public void onTaskFragmentAppeared(@NonNull TaskFragmentInfo taskFragmentInfo) {
-            mExecutor.execute(
-                    () -> TaskFragmentOrganizer.this.onTaskFragmentAppeared(taskFragmentInfo));
-        }
-
-        @Override
-        public void onTaskFragmentInfoChanged(@NonNull TaskFragmentInfo taskFragmentInfo) {
-            mExecutor.execute(
-                    () -> TaskFragmentOrganizer.this.onTaskFragmentInfoChanged(taskFragmentInfo));
-        }
-
-        @Override
-        public void onTaskFragmentVanished(@NonNull TaskFragmentInfo taskFragmentInfo) {
-            mExecutor.execute(
-                    () -> TaskFragmentOrganizer.this.onTaskFragmentVanished(taskFragmentInfo));
-        }
-
-        @Override
-        public void onTaskFragmentParentInfoChanged(
-                @NonNull IBinder fragmentToken, @NonNull Configuration parentConfig) {
-            mExecutor.execute(
-                    () -> TaskFragmentOrganizer.this.onTaskFragmentParentInfoChanged(
-                            fragmentToken, parentConfig));
-        }
-
-        @Override
-        public void onTaskFragmentError(
-                @NonNull IBinder errorCallbackToken, @NonNull Bundle errorBundle) {
-            mExecutor.execute(() -> {
-                final TaskFragmentInfo info = errorBundle.getParcelable(
-                        KEY_ERROR_CALLBACK_TASK_FRAGMENT_INFO, TaskFragmentInfo.class);
-                TaskFragmentOrganizer.this.onTaskFragmentError(
-                        errorCallbackToken, info,
-                        errorBundle.getInt(KEY_ERROR_CALLBACK_OP_TYPE),
-                        (Throwable) errorBundle.getSerializable(KEY_ERROR_CALLBACK_EXCEPTION,
-                                java.lang.Throwable.class));
-            });
-        }
-
-        @Override
-        public void onActivityReparentToTask(int taskId, @NonNull Intent activityIntent,
-                @NonNull IBinder activityToken) {
-            mExecutor.execute(
-                    () -> TaskFragmentOrganizer.this.onActivityReparentToTask(
-                            taskId, activityIntent, activityToken));
+        public void onTransactionReady(@NonNull TaskFragmentTransaction transaction) {
+            mExecutor.execute(() -> TaskFragmentOrganizer.this.onTransactionReady(transaction));
         }
     };
 
diff --git a/core/java/android/window/TaskFragmentTransaction.aidl b/core/java/android/window/TaskFragmentTransaction.aidl
new file mode 100644
index 0000000..aaa2db4
--- /dev/null
+++ b/core/java/android/window/TaskFragmentTransaction.aidl
@@ -0,0 +1,20 @@
+/*
+ * 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.window;
+
+parcelable TaskFragmentTransaction;
+parcelable TaskFragmentTransaction.Change;
diff --git a/core/java/android/window/TaskFragmentTransaction.java b/core/java/android/window/TaskFragmentTransaction.java
new file mode 100644
index 0000000..755864f
--- /dev/null
+++ b/core/java/android/window/TaskFragmentTransaction.java
@@ -0,0 +1,335 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.window;
+
+import static java.util.Objects.requireNonNull;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Intent;
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Used to communicate information about what are changing on embedded TaskFragments belonging to
+ * the same TaskFragmentOrganizer. A transaction can contain multiple changes.
+ * @see TaskFragmentTransaction.Change
+ * @hide
+ */
+public final class TaskFragmentTransaction implements Parcelable {
+
+    private final ArrayList<Change> mChanges = new ArrayList<>();
+
+    public TaskFragmentTransaction() {}
+
+    private TaskFragmentTransaction(Parcel in) {
+        in.readTypedList(mChanges, Change.CREATOR);
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeTypedList(mChanges);
+    }
+
+    /** Adds a {@link Change} to this transaction. */
+    public void addChange(@Nullable Change change) {
+        if (change != null) {
+            mChanges.add(change);
+        }
+    }
+
+    /** Whether this transaction contains any {@link Change}. */
+    public boolean isEmpty() {
+        return mChanges.isEmpty();
+    }
+
+    @NonNull
+    public List<Change> getChanges() {
+        return mChanges;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("TaskFragmentTransaction{changes=[");
+        for (int i = 0; i < mChanges.size(); ++i) {
+            if (i > 0) {
+                sb.append(',');
+            }
+            sb.append(mChanges.get(i));
+        }
+        sb.append("]}");
+        return sb.toString();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    public static final Creator<TaskFragmentTransaction> CREATOR = new Creator<>() {
+        @Override
+        public TaskFragmentTransaction createFromParcel(Parcel in) {
+            return new TaskFragmentTransaction(in);
+        }
+
+        @Override
+        public TaskFragmentTransaction[] newArray(int size) {
+            return new TaskFragmentTransaction[size];
+        }
+    };
+
+    /** Change type: the TaskFragment is attached to the hierarchy. */
+    public static final int TYPE_TASK_FRAGMENT_APPEARED = 1;
+
+    /** Change type: the status of the TaskFragment is changed. */
+    public static final int TYPE_TASK_FRAGMENT_INFO_CHANGED = 2;
+
+    /** Change type: the TaskFragment is removed form the hierarchy. */
+    public static final int TYPE_TASK_FRAGMENT_VANISHED = 3;
+
+    /** Change type: the status of the parent leaf Task is changed. */
+    public static final int TYPE_TASK_FRAGMENT_PARENT_INFO_CHANGED = 4;
+
+    /** Change type: the TaskFragment related operation failed on the server side. */
+    public static final int TYPE_TASK_FRAGMENT_ERROR = 5;
+
+    /**
+     * Change type: an Activity is reparented to the Task. For example, when an Activity enters and
+     * then exits Picture-in-picture, it will be reparented back to its original Task. In this case,
+     * we need to notify the organizer so that it can check if the Activity matches any split rule.
+     */
+    public static final int TYPE_ACTIVITY_REPARENT_TO_TASK = 6;
+
+    @IntDef(prefix = { "TYPE_" }, value = {
+            TYPE_TASK_FRAGMENT_APPEARED,
+            TYPE_TASK_FRAGMENT_INFO_CHANGED,
+            TYPE_TASK_FRAGMENT_VANISHED,
+            TYPE_TASK_FRAGMENT_PARENT_INFO_CHANGED,
+            TYPE_TASK_FRAGMENT_ERROR,
+            TYPE_ACTIVITY_REPARENT_TO_TASK
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    @interface ChangeType {}
+
+    /** Represents the change an embedded TaskFragment undergoes. */
+    public static final class Change implements Parcelable {
+
+        /** @see ChangeType */
+        @ChangeType
+        private final int mType;
+
+        /** @see #setTaskFragmentToken(IBinder) */
+        @Nullable
+        private IBinder mTaskFragmentToken;
+
+        /** @see #setTaskFragmentInfo(TaskFragmentInfo) */
+        @Nullable
+        private TaskFragmentInfo mTaskFragmentInfo;
+
+        /** @see #setTaskId(int) */
+        private int mTaskId;
+
+        /** @see #setTaskConfiguration(Configuration) */
+        @Nullable
+        private Configuration mTaskConfiguration;
+
+        /** @see #setErrorCallbackToken(IBinder) */
+        @Nullable
+        private IBinder mErrorCallbackToken;
+
+        /** @see #setErrorBundle(Bundle) */
+        @Nullable
+        private Bundle mErrorBundle;
+
+        /** @see #setActivityIntent(Intent) */
+        @Nullable
+        private Intent mActivityIntent;
+
+        /** @see #setActivityToken(IBinder) */
+        @Nullable
+        private IBinder mActivityToken;
+
+        public Change(@ChangeType int type) {
+            mType = type;
+        }
+
+        private Change(Parcel in) {
+            mType = in.readInt();
+            mTaskFragmentToken = in.readStrongBinder();
+            mTaskFragmentInfo = in.readTypedObject(TaskFragmentInfo.CREATOR);
+            mTaskId = in.readInt();
+            mTaskConfiguration = in.readTypedObject(Configuration.CREATOR);
+            mErrorCallbackToken = in.readStrongBinder();
+            mErrorBundle = in.readBundle(TaskFragmentTransaction.class.getClassLoader());
+            mActivityIntent = in.readTypedObject(Intent.CREATOR);
+            mActivityToken = in.readStrongBinder();
+        }
+
+        @Override
+        public void writeToParcel(@NonNull Parcel dest, int flags) {
+            dest.writeInt(mType);
+            dest.writeStrongBinder(mTaskFragmentToken);
+            dest.writeTypedObject(mTaskFragmentInfo, flags);
+            dest.writeInt(mTaskId);
+            dest.writeTypedObject(mTaskConfiguration, flags);
+            dest.writeStrongBinder(mErrorCallbackToken);
+            dest.writeBundle(mErrorBundle);
+            dest.writeTypedObject(mActivityIntent, flags);
+            dest.writeStrongBinder(mActivityToken);
+        }
+
+        /** The change is related to the TaskFragment created with this unique token. */
+        public Change setTaskFragmentToken(@NonNull IBinder taskFragmentToken) {
+            mTaskFragmentToken = requireNonNull(taskFragmentToken);
+            return this;
+        }
+
+        /** Info of the embedded TaskFragment. */
+        public Change setTaskFragmentInfo(@NonNull TaskFragmentInfo info) {
+            mTaskFragmentInfo = requireNonNull(info);
+            return this;
+        }
+
+        /** Task id the parent Task. */
+        public Change setTaskId(int taskId) {
+            mTaskId = taskId;
+            return this;
+        }
+
+        /** Configuration of the parent Task. */
+        public Change setTaskConfiguration(@NonNull Configuration configuration) {
+            mTaskConfiguration = requireNonNull(configuration);
+            return this;
+        }
+
+        /**
+         * If the {@link #TYPE_TASK_FRAGMENT_ERROR} is from a {@link WindowContainerTransaction}
+         * from the {@link TaskFragmentOrganizer}, it may come with an error callback token to
+         * report back.
+         */
+        public Change setErrorCallbackToken(@Nullable IBinder errorCallbackToken) {
+            mErrorCallbackToken = errorCallbackToken;
+            return this;
+        }
+
+        /**
+         * Bundle with necessary info about the failure operation of
+         * {@link #TYPE_TASK_FRAGMENT_ERROR}.
+         */
+        public Change setErrorBundle(@NonNull Bundle errorBundle) {
+            mErrorBundle = requireNonNull(errorBundle);
+            return this;
+        }
+
+        /**
+         * Intent of the activity that is reparented to the Task for
+         * {@link #TYPE_ACTIVITY_REPARENT_TO_TASK}.
+         */
+        public Change setActivityIntent(@NonNull Intent intent) {
+            mActivityIntent = requireNonNull(intent);
+            return this;
+        }
+
+        /**
+         * Token of the reparent activity for {@link #TYPE_ACTIVITY_REPARENT_TO_TASK}.
+         * If the activity belongs to the same process as the organizer, this will be the actual
+         * activity token; if the activity belongs to a different process, the server will generate
+         * a temporary token that the organizer can use to reparent the activity through
+         * {@link WindowContainerTransaction} if needed.
+         */
+        public Change setActivityToken(@NonNull IBinder activityToken) {
+            mActivityToken = requireNonNull(activityToken);
+            return this;
+        }
+
+        @ChangeType
+        public int getType() {
+            return mType;
+        }
+
+        @Nullable
+        public IBinder getTaskFragmentToken() {
+            return mTaskFragmentToken;
+        }
+
+        @Nullable
+        public TaskFragmentInfo getTaskFragmentInfo() {
+            return mTaskFragmentInfo;
+        }
+
+        public int getTaskId() {
+            return mTaskId;
+        }
+
+        @Nullable
+        public Configuration getTaskConfiguration() {
+            return mTaskConfiguration;
+        }
+
+        @Nullable
+        public IBinder getErrorCallbackToken() {
+            return mErrorCallbackToken;
+        }
+
+        @Nullable
+        public Bundle getErrorBundle() {
+            return mErrorBundle;
+        }
+
+        @Nullable
+        public Intent getActivityIntent() {
+            return mActivityIntent;
+        }
+
+        @Nullable
+        public IBinder getActivityToken() {
+            return mActivityToken;
+        }
+
+        @Override
+        public String toString() {
+            return "Change{ type=" + mType + " }";
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        public static final Creator<Change> CREATOR = new Creator<>() {
+            @Override
+            public Change createFromParcel(Parcel in) {
+                return new Change(in);
+            }
+
+            @Override
+            public Change[] newArray(int size) {
+                return new Change[size];
+            }
+        };
+    }
+}
diff --git a/core/java/android/window/WindowOnBackInvokedDispatcher.java b/core/java/android/window/WindowOnBackInvokedDispatcher.java
index 1d396be..0730f3d 100644
--- a/core/java/android/window/WindowOnBackInvokedDispatcher.java
+++ b/core/java/android/window/WindowOnBackInvokedDispatcher.java
@@ -325,7 +325,8 @@
         public boolean checkApplicationCallbackRegistration(int priority,
                 OnBackInvokedCallback callback) {
             if (!mApplicationCallBackEnabled
-                    && !(callback instanceof CompatOnBackInvokedCallback)) {
+                    && !(callback instanceof CompatOnBackInvokedCallback)
+                    && !ALWAYS_ENFORCE_PREDICTIVE_BACK) {
                 Log.w("OnBackInvokedCallback",
                         "OnBackInvokedCallback is not enabled for the application."
                                 + "\nSet 'android:enableOnBackInvokedCallback=\"true\"' in the"
diff --git a/core/java/com/android/internal/jank/FrameTracker.java b/core/java/com/android/internal/jank/FrameTracker.java
index dbfa4d3..48343803 100644
--- a/core/java/com/android/internal/jank/FrameTracker.java
+++ b/core/java/com/android/internal/jank/FrameTracker.java
@@ -66,6 +66,8 @@
     private static final long INVALID_ID = -1;
     public static final int NANOS_IN_MILLISECOND = 1_000_000;
 
+    private static final int MAX_LENGTH_EVENT_DESC = 20;
+
     static final int REASON_END_UNKNOWN = -1;
     static final int REASON_END_NORMAL = 0;
     static final int REASON_END_SURFACE_DESTROYED = 1;
@@ -394,7 +396,18 @@
         return true;
     }
 
-    private void markEvent(String desc) {
+    /**
+     * Mark the FrameTracker events in the trace.
+     *
+     * @param desc The description of the trace event,
+     *            shouldn't exceed {@link #MAX_LENGTH_EVENT_DESC}.
+     */
+    private void markEvent(@NonNull String desc) {
+        if (desc.length() > MAX_LENGTH_EVENT_DESC) {
+            throw new IllegalArgumentException(TextUtils.formatSimple(
+                    "The length of the trace event description <%s> exceeds %d",
+                    desc, MAX_LENGTH_EVENT_DESC));
+        }
         Trace.beginSection(TextUtils.formatSimple("%s#%s", mSession.getName(), desc));
         Trace.endSection();
     }
diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java
index e625b31..72de78c 100644
--- a/core/java/com/android/internal/jank/InteractionJankMonitor.java
+++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java
@@ -149,6 +149,10 @@
     private static final int DEFAULT_TRACE_THRESHOLD_MISSED_FRAMES = 3;
     private static final int DEFAULT_TRACE_THRESHOLD_FRAME_TIME_MILLIS = 64;
 
+    @VisibleForTesting
+    public static final int MAX_LENGTH_OF_CUJ_NAME = 80;
+    private static final int MAX_LENGTH_SESSION_NAME = 100;
+
     public static final String ACTION_SESSION_END = ACTION_PREFIX + ".ACTION_SESSION_END";
     public static final String ACTION_SESSION_CANCEL = ACTION_PREFIX + ".ACTION_SESSION_CANCEL";
 
@@ -732,6 +736,9 @@
      * @return the name of the cuj type
      */
     public static String getNameOfCuj(int cujType) {
+        // Please note:
+        // 1. The length of the returned string shouldn't exceed MAX_LENGTH_OF_CUJ_NAME.
+        // 2. The returned string should be the same with the name defined in atoms.proto.
         switch (cujType) {
             case CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE:
                 return "SHADE_EXPAND_COLLAPSE";
@@ -1106,9 +1113,27 @@
         public Session(@CujType int cujType, @NonNull String postfix) {
             mCujType = cujType;
             mTimeStamp = System.nanoTime();
-            mName = TextUtils.isEmpty(postfix)
-                    ? String.format("J<%s>", getNameOfCuj(mCujType))
-                    : String.format("J<%s::%s>", getNameOfCuj(mCujType), postfix);
+            mName = generateSessionName(getNameOfCuj(cujType), postfix);
+        }
+
+        private String generateSessionName(@NonNull String cujName, @NonNull String cujPostfix) {
+            final boolean hasPostfix = !TextUtils.isEmpty(cujPostfix);
+            // We assert that the cujName shouldn't exceed MAX_LENGTH_OF_CUJ_NAME.
+            if (cujName.length() > MAX_LENGTH_OF_CUJ_NAME) {
+                throw new IllegalArgumentException(TextUtils.formatSimple(
+                        "The length of cuj name <%s> exceeds %d", cujName, MAX_LENGTH_OF_CUJ_NAME));
+            }
+            if (hasPostfix) {
+                final int remaining = MAX_LENGTH_SESSION_NAME - cujName.length();
+                if (cujPostfix.length() > remaining) {
+                    cujPostfix = cujPostfix.substring(0, remaining - 3).concat("...");
+                }
+            }
+            // The max length of the whole string should be:
+            // 105 with postfix, 83 without postfix
+            return hasPostfix
+                    ? TextUtils.formatSimple("J<%s::%s>", cujName, cujPostfix)
+                    : TextUtils.formatSimple("J<%s>", cujName);
         }
 
         @CujType
diff --git a/core/java/com/android/internal/os/BinderCallsStats.java b/core/java/com/android/internal/os/BinderCallsStats.java
index 0a29fc52..fa6fa55 100644
--- a/core/java/com/android/internal/os/BinderCallsStats.java
+++ b/core/java/com/android/internal/os/BinderCallsStats.java
@@ -1169,7 +1169,15 @@
 
     }
 
+    /** @hide */
+    public static void startForWifi(Context context) {
+        new BinderCallsStats.SettingsObserver(
+            context,
+            new BinderCallsStats(
+                new BinderCallsStats.Injector(),
+                com.android.internal.os.BinderLatencyProto.Dims.WIFI));
 
+    }
 
     /**
      * Settings observer for other processes (not system_server).
diff --git a/core/java/com/android/internal/util/ScreenshotHelper.java b/core/java/com/android/internal/util/ScreenshotHelper.java
index 8e54746..b9243ec 100644
--- a/core/java/com/android/internal/util/ScreenshotHelper.java
+++ b/core/java/com/android/internal/util/ScreenshotHelper.java
@@ -219,8 +219,9 @@
                 throw new IllegalArgumentException("Bundle does not contain a hardware bitmap");
             }
 
-            HardwareBuffer buffer = bundle.getParcelable(KEY_BUFFER, android.hardware.HardwareBuffer.class);
-            ParcelableColorSpace colorSpace = bundle.getParcelable(KEY_COLOR_SPACE, android.graphics.ParcelableColorSpace.class);
+            HardwareBuffer buffer = bundle.getParcelable(KEY_BUFFER, HardwareBuffer.class);
+            ParcelableColorSpace colorSpace = bundle.getParcelable(KEY_COLOR_SPACE,
+                    ParcelableColorSpace.class);
 
             return Bitmap.wrapHardwareBuffer(Objects.requireNonNull(buffer),
                     colorSpace.getColorSpace());
diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java
index 578b588..2c9ef4f 100644
--- a/core/java/com/android/server/SystemConfig.java
+++ b/core/java/com/android/server/SystemConfig.java
@@ -339,6 +339,8 @@
     // A map from package name of vendor APEXes that can be updated to an installer package name
     // allowed to install updates for it.
     private final ArrayMap<String, String> mAllowedVendorApexes = new ArrayMap<>();
+    // A set of package names that are allowed to use <install-constraints> manifest tag.
+    private final Set<String> mInstallConstraintsAllowlist = new ArraySet<>();
 
     private String mModulesInstallerPackageName;
 
@@ -535,6 +537,10 @@
         return mAllowedVendorApexes;
     }
 
+    public Set<String> getInstallConstraintsAllowlist() {
+        return mInstallConstraintsAllowlist;
+    }
+
     public String getModulesInstallerPackageName() {
         return mModulesInstallerPackageName;
     }
@@ -1455,6 +1461,20 @@
                         }
                         XmlUtils.skipCurrentTag(parser);
                     } break;
+                    case "install-constraints-allowed": {
+                        if (allowAppConfigs) {
+                            String packageName = parser.getAttributeValue(null, "package");
+                            if (packageName == null) {
+                                Slog.w(TAG, "<" + name + "> without package in " + permFile
+                                        + " at " + parser.getPositionDescription());
+                            } else {
+                                mInstallConstraintsAllowlist.add(packageName);
+                            }
+                        } else {
+                            logNotAllowedInPartition(name, permFile, parser);
+                        }
+                        XmlUtils.skipCurrentTag(parser);
+                    } break;
                     default: {
                         Slog.w(TAG, "Tag " + name + " is unknown in "
                                 + permFile + " at " + parser.getPositionDescription());
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 22f84b6..9ae1630 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -1116,12 +1116,6 @@
     return array;
 }
 
-static jlong nativeGetPrimaryPhysicalDisplayId(JNIEnv* env, jclass clazz) {
-    PhysicalDisplayId displayId;
-    SurfaceComposerClient::getPrimaryPhysicalDisplayId(&displayId);
-    return static_cast<jlong>(displayId.value);
-}
-
 static jobject nativeGetPhysicalDisplayToken(JNIEnv* env, jclass clazz, jlong physicalDisplayId) {
     const auto id = DisplayId::fromValue<PhysicalDisplayId>(physicalDisplayId);
     if (!id) return nullptr;
@@ -2210,8 +2204,6 @@
             (void*)nativeSetDefaultFrameRateCompatibility},
     {"nativeGetPhysicalDisplayIds", "()[J",
             (void*)nativeGetPhysicalDisplayIds },
-    {"nativeGetPrimaryPhysicalDisplayId", "()J",
-            (void*)nativeGetPrimaryPhysicalDisplayId },
     {"nativeGetPhysicalDisplayToken", "(J)Landroid/os/IBinder;",
             (void*)nativeGetPhysicalDisplayToken },
     {"nativeCreateDisplay", "(Ljava/lang/String;Z)Landroid/os/IBinder;",
diff --git a/core/res/res/values-sw600dp/config.xml b/core/res/res/values-sw600dp/config.xml
index d686dd2..34b6a54 100644
--- a/core/res/res/values-sw600dp/config.xml
+++ b/core/res/res/values-sw600dp/config.xml
@@ -51,7 +51,5 @@
 
     <!-- If true, show multiuser switcher by default unless the user specifically disables it. -->
     <bool name="config_showUserSwitcherByDefault">true</bool>
-
-    <integer name="config_chooser_max_targets_per_row">6</integer>
 </resources>
 
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 0acf703..062523e 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -3607,4 +3607,19 @@
              false, the application cannot be profiled at all. Defaults to true. -->
         <attr name="enabled" format="boolean" />
     </declare-styleable>
+
+    <!-- <code>install-constraints</code> tag rejects installs unless one the constraints defined by
+         its child elements is true.
+         It is possible to have multiple <code>install-constraints</code> tags in a single manifest,
+         where each tag is evaluated independently.
+         @hide -->
+    <declare-styleable name="AndroidManifestInstallConstraints" parent="AndroidManifest" />
+
+    <!-- A constraint for <code>install-constraints</code>. Checks that the device fingerprint
+         starts with the given prefix.
+         @hide -->
+    <declare-styleable name="AndroidManifestInstallConstraintsFingerprintPrefix"
+                       parent="AndroidManifestInstallConstraints">
+        <attr name="value" />
+    </declare-styleable>
 </resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 9d2c3d7..d176a1d 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2629,6 +2629,10 @@
          will be locked. -->
     <bool name="config_multiuserDelayUserDataLocking">false</bool>
 
+    <!-- Whether the device allows users to start in background on secondary displays.
+         Should be false for most devices, except automotive vehicle with passenger displays. -->
+    <bool name="config_multiuserUsersOnSecondaryDisplays">false</bool>
+
     <!-- Whether to automatically switch a non-primary user back to the primary user after a
          timeout when the device is docked.  -->
     <bool name="config_enableTimeoutToUserZeroWhenDocked">false</bool>
@@ -5850,4 +5854,7 @@
 
     <!-- Whether the wake screen on notifications feature is available. -->
     <bool name="config_pulseOnNotificationsAvailable">true</bool>
+
+    <!-- The number of tasks to scan to get the visibility of Home -->
+    <integer name="config_maxScanTasksForHomeVisibility">10</integer>
 </resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index cb0393f..e6ae7b1 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -453,6 +453,7 @@
   <java-symbol type="integer" name="config_multiuserMaximumUsers" />
   <java-symbol type="integer" name="config_multiuserMaxRunningUsers" />
   <java-symbol type="bool" name="config_multiuserDelayUserDataLocking" />
+  <java-symbol type="bool" name="config_multiuserUsersOnSecondaryDisplays" />
   <java-symbol type="bool" name="config_enableTimeoutToUserZeroWhenDocked" />
   <java-symbol type="integer" name="config_userTypePackageWhitelistMode"/>
   <java-symbol type="xml" name="config_user_types" />
@@ -3977,6 +3978,8 @@
 
   <java-symbol type="string" name="config_customCountryDetector" />
 
+  <java-symbol type="integer" name="config_maxScanTasksForHomeVisibility" />
+
   <!-- For Foldables -->
   <java-symbol type="array" name="config_foldedDeviceStates" />
   <java-symbol type="array" name="config_deviceStatesOnWhichToWakeUp" />
diff --git a/core/tests/coretests/src/android/content/res/ConfigurationTest.java b/core/tests/coretests/src/android/content/res/ConfigurationTest.java
index 669138c..0d5cd72 100644
--- a/core/tests/coretests/src/android/content/res/ConfigurationTest.java
+++ b/core/tests/coretests/src/android/content/res/ConfigurationTest.java
@@ -157,6 +157,22 @@
         assertFalse(config.isNightModeActive());
     }
 
+    @Test
+    public void testSequenceNumberWrapAround() {
+        Configuration config = new Configuration();
+        Configuration otherConfig = new Configuration();
+
+        // Verify the other configuration is not newer when this sequence number warp around.
+        config.seq = 1000;
+        otherConfig.seq = Integer.MAX_VALUE - 1000;
+        assertFalse(config.isOtherSeqNewer(otherConfig));
+
+        // Verify the other configuration is newer when the other sequence number warp around.
+        config.seq = Integer.MAX_VALUE - 1000;
+        otherConfig.seq = 1000;
+        assertTrue(config.isOtherSeqNewer(otherConfig));
+    }
+
     private void dumpDebug(File f, Configuration config) throws Exception {
         final AtomicFile af = new AtomicFile(f);
         FileOutputStream fos = af.startWrite();
diff --git a/core/tests/coretests/src/android/graphics/PathOffsetTest.java b/core/tests/coretests/src/android/graphics/PathOffsetTest.java
deleted file mode 100644
index 6cc42f6..0000000
--- a/core/tests/coretests/src/android/graphics/PathOffsetTest.java
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-import static org.junit.Assert.assertTrue;
-
-import android.graphics.Bitmap.Config;
-import android.graphics.Path.Direction;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@RunWith(AndroidJUnit4.class)
-public class PathOffsetTest {
-
-    private static final int SQUARE = 10;
-    private static final int WIDTH = 100;
-    private static final int HEIGHT = 100;
-    private static final int START_X = 10;
-    private static final int START_Y = 20;
-    private static final int OFFSET_X = 30;
-    private static final int OFFSET_Y = 40;
-
-    @Test
-    @SmallTest
-    public void testPathOffset() {
-        Path actualPath = new Path();
-        actualPath.addRect(START_X, START_Y, START_X + SQUARE, START_Y + SQUARE, Direction.CW);
-        assertTrue(actualPath.isSimplePath);
-        actualPath.offset(OFFSET_X, OFFSET_Y);
-        assertTrue(actualPath.isSimplePath);
-
-        Path expectedPath = new Path();
-        expectedPath.addRect(START_X + OFFSET_X, START_Y + OFFSET_Y, START_X + OFFSET_X + SQUARE,
-                START_Y + OFFSET_Y + SQUARE, Direction.CW);
-
-        assertPaths(actualPath, expectedPath);
-    }
-
-    @Test
-    @SmallTest
-    public void testPathOffsetWithDestination() {
-        Path initialPath = new Path();
-        initialPath.addRect(START_X, START_Y, START_X + SQUARE, START_Y + SQUARE, Direction.CW);
-        Path actualPath = new Path();
-        assertTrue(initialPath.isSimplePath);
-        assertTrue(actualPath.isSimplePath);
-        initialPath.offset(OFFSET_X, OFFSET_Y, actualPath);
-        assertTrue(actualPath.isSimplePath);
-
-        Path expectedPath = new Path();
-        expectedPath.addRect(START_X + OFFSET_X, START_Y + OFFSET_Y, START_X + OFFSET_X + SQUARE,
-                START_Y + OFFSET_Y + SQUARE, Direction.CW);
-
-        assertPaths(actualPath, expectedPath);
-    }
-
-    private static void assertPaths(Path actual, Path expected) {
-        Bitmap actualBitmap = drawAndGetBitmap(actual);
-        Bitmap expectedBitmap = drawAndGetBitmap(expected);
-        assertTrue(actualBitmap.sameAs(expectedBitmap));
-    }
-
-    private static Bitmap drawAndGetBitmap(Path path) {
-        Bitmap bitmap = Bitmap.createBitmap(WIDTH, HEIGHT, Config.ARGB_8888);
-        bitmap.eraseColor(Color.BLACK);
-        Paint paint = new Paint();
-        paint.setColor(Color.RED);
-        Canvas canvas = new Canvas(bitmap);
-        canvas.drawPath(path, paint);
-        return bitmap;
-    }
-
-}
diff --git a/core/tests/coretests/src/android/graphics/PathTest.java b/core/tests/coretests/src/android/graphics/PathTest.java
deleted file mode 100644
index b50792c..0000000
--- a/core/tests/coretests/src/android/graphics/PathTest.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-import androidx.test.filters.SmallTest;
-
-import junit.framework.TestCase;
-
-public class PathTest extends TestCase {
-
-    @SmallTest
-    public void testResetPreservesFillType() throws Exception {
-        Path path = new Path();
-
-        final Path.FillType defaultFillType = path.getFillType();
-        final Path.FillType fillType = Path.FillType.INVERSE_EVEN_ODD;
-
-        // This test is only meaningful if it changes from the default.
-        assertFalse(fillType.equals(defaultFillType));
-
-        path.setFillType(fillType);
-        path.reset();
-        assertEquals(path.getFillType(), fillType);
-    }
-}
diff --git a/core/tests/coretests/src/android/os/FileUtilsTest.java b/core/tests/coretests/src/android/os/FileUtilsTest.java
index c1e72fe..32c3a26 100644
--- a/core/tests/coretests/src/android/os/FileUtilsTest.java
+++ b/core/tests/coretests/src/android/os/FileUtilsTest.java
@@ -58,10 +58,10 @@
 import androidx.test.InstrumentationRegistry;
 import androidx.test.runner.AndroidJUnit4;
 
-import libcore.io.Streams;
-
 import com.google.android.collect.Sets;
 
+import libcore.io.Streams;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -477,6 +477,14 @@
         new File(mTarget, "test (1).jpg").createNewFile();
         assertNameEquals("test (2).jpg",
                 FileUtils.buildUniqueFile(mTarget, "image/jpeg", "test.jpg"));
+
+        assertNameEquals("test.mp3", FileUtils.buildUniqueFile(mTarget, "audio/mp3", "test.mp3"));
+        new File(mTarget, "test.mp3").createNewFile();
+        assertNameEquals("test (1).mp3",
+                FileUtils.buildUniqueFile(mTarget, "audio/mp3", "test.mp3"));
+        new File(mTarget, "test (1).mp3").createNewFile();
+        assertNameEquals("test (2).mp3",
+                FileUtils.buildUniqueFile(mTarget, "audio/mp3", "test.mp3"));
     }
 
     @Test
diff --git a/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java b/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java
index d96f041..1519e48 100644
--- a/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java
+++ b/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java
@@ -16,10 +16,26 @@
 
 package com.android.internal.jank;
 
+import static android.text.TextUtils.formatSimple;
+
 import static com.android.internal.jank.FrameTracker.REASON_CANCEL_TIMEOUT;
 import static com.android.internal.jank.FrameTracker.REASON_END_NORMAL;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_LOCKSCREEN_LAUNCH_CAMERA;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_ADD;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_APP_START;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_HEADS_UP_APPEAR;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_HEADS_UP_DISAPPEAR;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_REMOVE;
 import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_QS_SCROLL_SWIPE;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_ROW_EXPAND;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_ROW_SWIPE;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_SCROLL_FLING;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_SPLIT_SCREEN_RESIZE;
 import static com.android.internal.jank.InteractionJankMonitor.CUJ_TO_STATSD_INTERACTION_TYPE;
+import static com.android.internal.jank.InteractionJankMonitor.MAX_LENGTH_OF_CUJ_NAME;
+import static com.android.internal.jank.InteractionJankMonitor.getNameOfCuj;
 
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.common.truth.Truth.assertWithMessage;
@@ -38,6 +54,7 @@
 import android.os.HandlerThread;
 import android.os.SystemClock;
 import android.provider.DeviceConfig;
+import android.util.SparseArray;
 import android.view.View;
 import android.view.ViewAttachTestActivity;
 
@@ -52,8 +69,12 @@
 import com.android.internal.jank.FrameTracker.ViewRootWrapper;
 import com.android.internal.jank.InteractionJankMonitor.Configuration;
 import com.android.internal.jank.InteractionJankMonitor.Session;
+import com.android.internal.util.FrameworkStatsLog;
+
+import com.google.common.truth.Expect;
 
 import org.junit.Before;
+import org.junit.BeforeClass;
 import org.junit.Rule;
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
@@ -64,11 +85,17 @@
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.stream.Collectors;
 
 @SmallTest
 public class InteractionJankMonitorTest {
     private static final String CUJ_POSTFIX = "";
+    private static final SparseArray<String> ENUM_NAME_EXCEPTION_MAP = new SparseArray<>();
+    private static final SparseArray<String> CUJ_NAME_EXCEPTION_MAP = new SparseArray<>();
+    private static final String ENUM_NAME_PREFIX =
+            "UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__";
+
     private ViewAttachTestActivity mActivity;
     private View mView;
     private HandlerThread mWorker;
@@ -77,6 +104,53 @@
     public ActivityTestRule<ViewAttachTestActivity> mRule =
             new ActivityTestRule<>(ViewAttachTestActivity.class);
 
+    @Rule
+    public final Expect mExpect = Expect.create();
+
+    @BeforeClass
+    public static void initialize() {
+        ENUM_NAME_EXCEPTION_MAP.put(CUJ_NOTIFICATION_ADD, getEnumName("SHADE_NOTIFICATION_ADD"));
+        ENUM_NAME_EXCEPTION_MAP.put(CUJ_NOTIFICATION_APP_START, getEnumName("SHADE_APP_LAUNCH"));
+        ENUM_NAME_EXCEPTION_MAP.put(
+                CUJ_NOTIFICATION_HEADS_UP_APPEAR, getEnumName("SHADE_HEADS_UP_APPEAR"));
+        ENUM_NAME_EXCEPTION_MAP.put(
+                CUJ_NOTIFICATION_HEADS_UP_DISAPPEAR, getEnumName("SHADE_HEADS_UP_DISAPPEAR"));
+        ENUM_NAME_EXCEPTION_MAP.put(
+                CUJ_NOTIFICATION_REMOVE, getEnumName("SHADE_NOTIFICATION_REMOVE"));
+        ENUM_NAME_EXCEPTION_MAP.put(
+                CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, getEnumName("NOTIFICATION_SHADE_SWIPE"));
+        ENUM_NAME_EXCEPTION_MAP.put(
+                CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE, getEnumName("SHADE_QS_EXPAND_COLLAPSE"));
+        ENUM_NAME_EXCEPTION_MAP.put(
+                CUJ_NOTIFICATION_SHADE_QS_SCROLL_SWIPE, getEnumName("SHADE_QS_SCROLL_SWIPE"));
+        ENUM_NAME_EXCEPTION_MAP.put(
+                CUJ_NOTIFICATION_SHADE_ROW_EXPAND, getEnumName("SHADE_ROW_EXPAND"));
+        ENUM_NAME_EXCEPTION_MAP.put(
+                CUJ_NOTIFICATION_SHADE_ROW_SWIPE, getEnumName("SHADE_ROW_SWIPE"));
+        ENUM_NAME_EXCEPTION_MAP.put(
+                CUJ_NOTIFICATION_SHADE_SCROLL_FLING, getEnumName("SHADE_SCROLL_FLING"));
+
+        CUJ_NAME_EXCEPTION_MAP.put(
+                CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, "CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE");
+        CUJ_NAME_EXCEPTION_MAP.put(
+                CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE,
+                "CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE");
+        CUJ_NAME_EXCEPTION_MAP.put(
+                CUJ_NOTIFICATION_SHADE_QS_SCROLL_SWIPE, "CUJ_NOTIFICATION_SHADE_QS_SCROLL_SWIPE");
+        CUJ_NAME_EXCEPTION_MAP.put(
+                CUJ_NOTIFICATION_SHADE_ROW_EXPAND, "CUJ_NOTIFICATION_SHADE_ROW_EXPAND");
+        CUJ_NAME_EXCEPTION_MAP.put(
+                CUJ_NOTIFICATION_SHADE_ROW_SWIPE, "CUJ_NOTIFICATION_SHADE_ROW_SWIPE");
+        CUJ_NAME_EXCEPTION_MAP.put(
+                CUJ_NOTIFICATION_SHADE_SCROLL_FLING, "CUJ_NOTIFICATION_SHADE_SCROLL_FLING");
+        CUJ_NAME_EXCEPTION_MAP.put(CUJ_LOCKSCREEN_LAUNCH_CAMERA, "CUJ_LOCKSCREEN_LAUNCH_CAMERA");
+        CUJ_NAME_EXCEPTION_MAP.put(CUJ_SPLIT_SCREEN_RESIZE, "CUJ_SPLIT_SCREEN_RESIZE");
+    }
+
+    private static String getEnumName(String name) {
+        return formatSimple("%s%s", ENUM_NAME_PREFIX, name);
+    }
+
     @Before
     public void setup() {
         // Prepare an activity for getting ThreadedRenderer later.
@@ -174,6 +248,82 @@
         }
     }
 
+    @Test
+    public void testCujsMapToEnumsCorrectly() {
+        List<Field> cujs = Arrays.stream(InteractionJankMonitor.class.getDeclaredFields())
+                .filter(f -> f.getName().startsWith("CUJ_")
+                        && Modifier.isStatic(f.getModifiers())
+                        && f.getType() == int.class)
+                .collect(Collectors.toList());
+
+        Map<Integer, String> enumsMap = Arrays.stream(FrameworkStatsLog.class.getDeclaredFields())
+                .filter(f -> f.getName().startsWith(ENUM_NAME_PREFIX)
+                        && Modifier.isStatic(f.getModifiers())
+                        && f.getType() == int.class)
+                .collect(Collectors.toMap(this::getIntFieldChecked, Field::getName));
+
+        cujs.forEach(f -> {
+            final int cuj = getIntFieldChecked(f);
+            final String cujName = f.getName();
+            final String expectedEnumName = ENUM_NAME_EXCEPTION_MAP.contains(cuj)
+                    ? ENUM_NAME_EXCEPTION_MAP.get(cuj)
+                    : formatSimple("%s%s", ENUM_NAME_PREFIX, cujName.substring(4));
+            final int enumKey = CUJ_TO_STATSD_INTERACTION_TYPE[cuj];
+            final String enumName = enumsMap.get(enumKey);
+            final String expectedNameOfCuj = CUJ_NAME_EXCEPTION_MAP.contains(cuj)
+                    ? CUJ_NAME_EXCEPTION_MAP.get(cuj)
+                    : formatSimple("CUJ_%s", getNameOfCuj(cuj));
+            mExpect
+                    .withMessage(formatSimple(
+                            "%s (%d) not matches %s (%d)", cujName, cuj, enumName, enumKey))
+                    .that(expectedEnumName.equals(enumName))
+                    .isTrue();
+            mExpect
+                    .withMessage(formatSimple("getNameOfCuj(%d) not matches %s", cuj, cujName))
+                    .that(cujName.equals(expectedNameOfCuj))
+                    .isTrue();
+        });
+    }
+
+    @Test
+    public void testCujNameLimit() {
+        Arrays.stream(InteractionJankMonitor.class.getDeclaredFields())
+                .filter(f -> f.getName().startsWith("CUJ_")
+                        && Modifier.isStatic(f.getModifiers())
+                        && f.getType() == int.class)
+                .forEach(f -> {
+                    final int cuj = getIntFieldChecked(f);
+                    mExpect
+                            .withMessage(formatSimple(
+                                    "Too long CUJ(%d) name: %s", cuj, getNameOfCuj(cuj)))
+                            .that(getNameOfCuj(cuj).length())
+                            .isAtMost(MAX_LENGTH_OF_CUJ_NAME);
+                });
+    }
+
+    @Test
+    public void testSessionNameLengthLimit() {
+        final int cujType = CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE;
+        final String cujName = getNameOfCuj(cujType);
+        final String cujTag = "ThisIsTheCujTag";
+        final String tooLongTag = cujTag.repeat(10);
+
+        // Normal case, no postfix.
+        Session noPostfix = new Session(cujType, "");
+        assertThat(noPostfix.getName()).isEqualTo(formatSimple("J<%s>", cujName));
+
+        // Normal case, with postfix.
+        Session withPostfix = new Session(cujType, cujTag);
+        assertThat(withPostfix.getName()).isEqualTo(formatSimple("J<%s::%s>", cujName, cujTag));
+
+        // Since the length of the cuj name is tested in another test, no need to test it here.
+        // Too long postfix case, should trim the postfix and keep the cuj name completed.
+        final String expectedTrimmedName = formatSimple("J<%s::%s>", cujName,
+                "ThisIsTheCujTagThisIsTheCujTagThisIsTheCujTagThisIsTheCujTagThisIsTheCujTagT...");
+        Session longPostfix = new Session(cujType, tooLongTag);
+        assertThat(longPostfix.getName()).isEqualTo(expectedTrimmedName);
+    }
+
     private InteractionJankMonitor createMockedInteractionJankMonitor() {
         InteractionJankMonitor monitor = spy(new InteractionJankMonitor(mWorker));
         doReturn(true).when(monitor).shouldMonitor(anyInt());
@@ -217,4 +367,12 @@
 
         return tracker;
     }
+
+    private int getIntFieldChecked(Field field) {
+        try {
+            return field.getInt(null);
+        } catch (IllegalAccessException ex) {
+            throw new RuntimeException(ex);
+        }
+    }
 }
diff --git a/core/tests/mockingcoretests/src/android/os/BundleRecyclingTest.java b/core/tests/mockingcoretests/src/android/os/BundleRecyclingTest.java
index 7c76498..c88ab90 100644
--- a/core/tests/mockingcoretests/src/android/os/BundleRecyclingTest.java
+++ b/core/tests/mockingcoretests/src/android/os/BundleRecyclingTest.java
@@ -23,6 +23,7 @@
 
 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.Mockito.never;
 import static org.mockito.Mockito.times;
@@ -34,7 +35,6 @@
 
 import com.android.dx.mockito.inline.extended.StaticMockitoSession;
 
-import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.quality.Strictness;
@@ -56,16 +56,11 @@
     private Parcel mParcelSpy;
     private Bundle mBundle;
 
-    @Before
-    public void setUp() throws Exception {
-        setUpBundle(/* hasLazy */ true);
-    }
-
     @Test
     public void bundleClear_whenUnparcelledWithoutLazy_recyclesParcelOnce() {
-        setUpBundle(/* hasLazy */ false);
+        setUpBundle(/* lazy */ 0, /* nonLazy */ 1);
         // Will unparcel and immediately recycle parcel
-        assertNotNull(mBundle.getString("key"));
+        assertNotNull(mBundle.getString("nonLazy0"));
         verify(mParcelSpy, times(1)).recycle();
         assertFalse(mBundle.isDefinitelyEmpty());
 
@@ -77,6 +72,7 @@
 
     @Test
     public void bundleClear_whenParcelled_recyclesParcel() {
+        setUpBundle(/* lazy */ 1);
         assertTrue(mBundle.isParcelled());
         verify(mParcelSpy, times(0)).recycle();
 
@@ -90,23 +86,9 @@
     }
 
     @Test
-    public void bundleClear_whenUnparcelledWithLazyValueUnwrapped_recyclesParcel() {
-        // Will unparcel with a lazy value, and immediately unwrap the lazy value,
-        // with no lazy values left at the end of getParcelable
-        assertNotNull(mBundle.getParcelable("key", CustomParcelable.class));
-        verify(mParcelSpy, times(0)).recycle();
-
-        mBundle.clear();
-        verify(mParcelSpy, times(1)).recycle();
-        assertTrue(mBundle.isDefinitelyEmpty());
-
-        // Should not recycle again
-        mBundle.clear();
-        verify(mParcelSpy, times(1)).recycle();
-    }
-
-    @Test
     public void bundleClear_whenUnparcelledWithLazy_recyclesParcel() {
+        setUpBundle(/* lazy */ 1);
+
         // Will unparcel but keep the CustomParcelable lazy
         assertFalse(mBundle.isEmpty());
         verify(mParcelSpy, times(0)).recycle();
@@ -122,6 +104,8 @@
 
     @Test
     public void bundleClear_whenClearedWithSharedParcel_doesNotRecycleParcel() {
+        setUpBundle(/* lazy */ 1);
+
         Bundle copy = new Bundle();
         copy.putAll(mBundle);
 
@@ -136,6 +120,8 @@
 
     @Test
     public void bundleClear_whenClearedWithCopiedParcel_doesNotRecycleParcel() {
+        setUpBundle(/* lazy */ 1);
+
         // Will unparcel but keep the CustomParcelable lazy
         assertFalse(mBundle.isEmpty());
 
@@ -151,7 +137,101 @@
         verify(mParcelSpy, never()).recycle();
     }
 
-    private void setUpBundle(boolean hasLazy) {
+    @Test
+    public void bundleGet_whenUnparcelledWithLazyValueUnwrapped_recyclesParcel() {
+        setUpBundle(/* lazy */ 1);
+
+        // Will unparcel with a lazy value, and immediately unwrap the lazy value,
+        // with no lazy values left at the end of getParcelable
+        // Ref counting should immediately recycle
+        assertNotNull(mBundle.getParcelable("lazy0", CustomParcelable.class));
+        verify(mParcelSpy, times(1)).recycle();
+
+        // Should not recycle again
+        assertNotNull(mBundle.getParcelable("lazy0", CustomParcelable.class));
+        mBundle.clear();
+        verify(mParcelSpy, times(1)).recycle();
+    }
+
+    @Test
+    public void bundleGet_whenMultipleLazy_recyclesParcelWhenAllUnwrapped() {
+        setUpBundle(/* lazy */ 2);
+
+        assertNotNull(mBundle.getParcelable("lazy0", CustomParcelable.class));
+        verify(mParcelSpy, times(0)).recycle();
+
+        assertNotNull(mBundle.getParcelable("lazy0", CustomParcelable.class));
+        verify(mParcelSpy, times(0)).recycle();
+
+        assertNotNull(mBundle.getParcelable("lazy1", CustomParcelable.class));
+        verify(mParcelSpy, times(1)).recycle();
+
+        // Should not recycle again
+        assertNotNull(mBundle.getParcelable("lazy0", CustomParcelable.class));
+        mBundle.clear();
+        verify(mParcelSpy, times(1)).recycle();
+        assertTrue(mBundle.isDefinitelyEmpty());
+    }
+
+    @Test
+    public void bundleGet_whenLazyAndNonLazy_recyclesParcelWhenAllUnwrapped() {
+        setUpBundle(/* lazy */ 1, /* nonLazy */ 1);
+
+        assertNotNull(mBundle.getParcelable("lazy0", CustomParcelable.class));
+        verify(mParcelSpy, times(1)).recycle();
+
+        // Should not recycle again
+        assertNotNull(mBundle.getString("nonLazy0"));
+        assertNotNull(mBundle.getParcelable("lazy0", CustomParcelable.class));
+        mBundle.clear();
+        verify(mParcelSpy, times(1)).recycle();
+    }
+
+    @Test
+    public void bundleGet_whenLazyAndNonLazy_doesNotRecycleWhenOnlyNonLazyRetrieved() {
+        setUpBundle(/* lazy */ 1, /* nonLazy */ 1);
+
+        assertNotNull(mBundle.getString("nonLazy0"));
+        verify(mParcelSpy, times(0)).recycle();
+
+        assertNotNull(mBundle.getString("nonLazy0"));
+        verify(mParcelSpy, times(0)).recycle();
+
+        assertNotNull(mBundle.getParcelable("lazy0", CustomParcelable.class));
+        verify(mParcelSpy, times(1)).recycle();
+    }
+
+    @Test
+    public void bundleGet_withWithSharedParcel_doesNotRecycleParcel() {
+        setUpBundle(/* lazy */ 1);
+
+        Bundle copy = new Bundle();
+        copy.putAll(mBundle);
+
+        assertNotNull(mBundle.getParcelable("lazy0", CustomParcelable.class));
+        mBundle.clear();
+
+        assertNotNull(copy.getParcelable("lazy0", CustomParcelable.class));
+        copy.clear();
+
+        verify(mParcelSpy, never()).recycle();
+    }
+
+    @Test
+    public void bundleGet_getAfterLazyCleared_doesNotRecycleAgain() {
+        setUpBundle(/* lazy */ 1);
+        mBundle.clear();
+        verify(mParcelSpy, times(1)).recycle();
+
+        assertNull(mBundle.getParcelable("lazy0", CustomParcelable.class));
+        verify(mParcelSpy, times(1)).recycle();
+    }
+
+    private void setUpBundle(int lazy) {
+        setUpBundle(lazy, /* nonLazy */ 0);
+    }
+
+    private void setUpBundle(int lazy, int nonLazy) {
         AtomicReference<Parcel> parcel = new AtomicReference<>();
         StaticMockitoSession session = mockitoSession()
                 .strictness(Strictness.STRICT_STUBS)
@@ -166,7 +246,7 @@
 
         Bundle bundle = new Bundle();
         bundle.setClassLoader(getClass().getClassLoader());
-        Parcel p = createBundle(hasLazy);
+        Parcel p = createBundle(lazy, nonLazy);
         bundle.readFromParcel(p);
         p.recycle();
 
@@ -179,13 +259,17 @@
     /**
      * Create a test bundle, parcel it and return the parcel.
      */
-    private Parcel createBundle(boolean hasLazy) {
+    private Parcel createBundle(int lazy, int nonLazy) {
         final Bundle source = new Bundle();
-        if (hasLazy) {
-            source.putParcelable("key", new CustomParcelable(13, "Tiramisu"));
-        } else {
-            source.putString("key", "tiramisu");
+
+        for (int i = 0; i < nonLazy; i++) {
+            source.putString("nonLazy" + i, "Tiramisu");
         }
+
+        for (int i = 0; i < lazy; i++) {
+            source.putParcelable("lazy" + i, new CustomParcelable(13, "Tiramisu"));
+        }
+
         return getParcelledBundle(source);
     }
 
diff --git a/graphics/java/android/graphics/BaseCanvas.java b/graphics/java/android/graphics/BaseCanvas.java
index 425a378..a8ab6d9 100644
--- a/graphics/java/android/graphics/BaseCanvas.java
+++ b/graphics/java/android/graphics/BaseCanvas.java
@@ -330,11 +330,7 @@
 
     public void drawPath(@NonNull Path path, @NonNull Paint paint) {
         throwIfHasHwFeaturesInSwMode(paint);
-        if (path.isSimplePath && path.rects != null) {
-            nDrawRegion(mNativeCanvasWrapper, path.rects.mNativeRegion, paint.getNativeInstance());
-        } else {
-            nDrawPath(mNativeCanvasWrapper, path.readOnlyNI(), paint.getNativeInstance());
-        }
+        nDrawPath(mNativeCanvasWrapper, path.readOnlyNI(), paint.getNativeInstance());
     }
 
     public void drawPoint(float x, float y, @NonNull Paint paint) {
diff --git a/graphics/java/android/graphics/BaseRecordingCanvas.java b/graphics/java/android/graphics/BaseRecordingCanvas.java
index a998ba8..d06f665 100644
--- a/graphics/java/android/graphics/BaseRecordingCanvas.java
+++ b/graphics/java/android/graphics/BaseRecordingCanvas.java
@@ -286,11 +286,7 @@
 
     @Override
     public final void drawPath(@NonNull Path path, @NonNull Paint paint) {
-        if (path.isSimplePath && path.rects != null) {
-            nDrawRegion(mNativeCanvasWrapper, path.rects.mNativeRegion, paint.getNativeInstance());
-        } else {
-            nDrawPath(mNativeCanvasWrapper, path.readOnlyNI(), paint.getNativeInstance());
-        }
+        nDrawPath(mNativeCanvasWrapper, path.readOnlyNI(), paint.getNativeInstance());
     }
 
     @Override
diff --git a/graphics/java/android/graphics/HardwareRenderer.java b/graphics/java/android/graphics/HardwareRenderer.java
index 7cc22d7..0e67f1f 100644
--- a/graphics/java/android/graphics/HardwareRenderer.java
+++ b/graphics/java/android/graphics/HardwareRenderer.java
@@ -1046,14 +1046,39 @@
     }
 
     /** @hide */
-    public static int copySurfaceInto(Surface surface, Rect srcRect, Bitmap bitmap) {
-        if (srcRect == null) {
-            // Empty rect means entire surface
-            return nCopySurfaceInto(surface, 0, 0, 0, 0, bitmap.getNativeInstance());
-        } else {
-            return nCopySurfaceInto(surface, srcRect.left, srcRect.top,
-                    srcRect.right, srcRect.bottom, bitmap.getNativeInstance());
+    public abstract static class CopyRequest {
+        protected Bitmap mDestinationBitmap;
+        final Rect mSrcRect;
+
+        protected CopyRequest(Rect srcRect, Bitmap destinationBitmap) {
+            mDestinationBitmap = destinationBitmap;
+            if (srcRect != null) {
+                mSrcRect = srcRect;
+            } else {
+                mSrcRect = new Rect();
+            }
         }
+
+        /**
+         * Retrieve the bitmap in which to store the result of the copy request
+         */
+        public long getDestinationBitmap(int srcWidth, int srcHeight) {
+            if (mDestinationBitmap == null) {
+                mDestinationBitmap =
+                        Bitmap.createBitmap(srcWidth, srcHeight, Bitmap.Config.ARGB_8888);
+            }
+            return mDestinationBitmap.getNativeInstance();
+        }
+
+        /** Called when the copy is completed */
+        public abstract void onCopyFinished(int result);
+    }
+
+    /** @hide */
+    public static void copySurfaceInto(Surface surface, CopyRequest copyRequest) {
+        final Rect srcRect = copyRequest.mSrcRect;
+        nCopySurfaceInto(surface, srcRect.left, srcRect.top, srcRect.right, srcRect.bottom,
+                copyRequest);
     }
 
     /**
@@ -1464,8 +1489,8 @@
 
     private static native void nRemoveObserver(long nativeProxy, long nativeObserver);
 
-    private static native int nCopySurfaceInto(Surface surface,
-            int srcLeft, int srcTop, int srcRight, int srcBottom, long bitmapHandle);
+    private static native void nCopySurfaceInto(Surface surface,
+            int srcLeft, int srcTop, int srcRight, int srcBottom, CopyRequest request);
 
     private static native Bitmap nCreateHardwareBitmap(long renderNode, int width, int height);
 
diff --git a/graphics/java/android/graphics/Path.java b/graphics/java/android/graphics/Path.java
index e5ef10d..afcaabe 100644
--- a/graphics/java/android/graphics/Path.java
+++ b/graphics/java/android/graphics/Path.java
@@ -20,8 +20,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.Size;
-import android.compat.annotation.UnsupportedAppUsage;
-import android.os.Build;
 
 import dalvik.annotation.optimization.CriticalNative;
 import dalvik.annotation.optimization.FastNative;
@@ -47,18 +45,6 @@
     public final long mNativePath;
 
     /**
-     * @hide
-     */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    public boolean isSimplePath = true;
-    /**
-     * @hide
-     */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    public Region rects;
-    private Direction mLastDirection = null;
-
-    /**
      * Create an empty path
      */
     public Path() {
@@ -72,15 +58,7 @@
      * @param src The path to copy from when initializing the new path
      */
     public Path(@Nullable Path src) {
-        long valNative = 0;
-        if (src != null) {
-            valNative = src.mNativePath;
-            isSimplePath = src.isSimplePath;
-            if (src.rects != null) {
-                rects = new Region(src.rects);
-            }
-        }
-        mNativePath = nInit(valNative);
+        mNativePath = nInit(src != null ? src.mNativePath : 0);
         sRegistry.registerNativeAllocation(this, mNativePath);
     }
 
@@ -89,9 +67,6 @@
      * This does NOT change the fill-type setting.
      */
     public void reset() {
-        isSimplePath = true;
-        mLastDirection = null;
-        if (rects != null) rects.setEmpty();
         // We promised not to change this, so preserve it around the native
         // call, which does now reset fill type.
         final FillType fillType = getFillType();
@@ -104,9 +79,6 @@
      * keeps the internal data structure for faster reuse.
      */
     public void rewind() {
-        isSimplePath = true;
-        mLastDirection = null;
-        if (rects != null) rects.setEmpty();
         nRewind(mNativePath);
     }
 
@@ -116,19 +88,7 @@
         if (this == src) {
             return;
         }
-        isSimplePath = src.isSimplePath;
         nSet(mNativePath, src.mNativePath);
-        if (!isSimplePath) {
-            return;
-        }
-
-        if (rects != null && src.rects != null) {
-            rects.set(src.rects);
-        } else if (rects != null && src.rects == null) {
-            rects.setEmpty();
-        } else if (src.rects != null) {
-            rects = new Region(src.rects);
-        }
     }
 
     /**
@@ -192,12 +152,7 @@
      * @see #op(Path, android.graphics.Path.Op)
      */
     public boolean op(@NonNull Path path1, @NonNull Path path2, @NonNull Op op) {
-        if (nOp(path1.mNativePath, path2.mNativePath, op.ordinal(), this.mNativePath)) {
-            isSimplePath = false;
-            rects = null;
-            return true;
-        }
-        return false;
+        return nOp(path1.mNativePath, path2.mNativePath, op.ordinal(), this.mNativePath);
     }
 
     /**
@@ -378,7 +333,6 @@
      * @param y The y-coordinate of the end of a line
      */
     public void lineTo(float x, float y) {
-        isSimplePath = false;
         nLineTo(mNativePath, x, y);
     }
 
@@ -393,7 +347,6 @@
      *           this contour, to specify a line
      */
     public void rLineTo(float dx, float dy) {
-        isSimplePath = false;
         nRLineTo(mNativePath, dx, dy);
     }
 
@@ -408,7 +361,6 @@
      * @param y2 The y-coordinate of the end point on a quadratic curve
      */
     public void quadTo(float x1, float y1, float x2, float y2) {
-        isSimplePath = false;
         nQuadTo(mNativePath, x1, y1, x2, y2);
     }
 
@@ -427,7 +379,6 @@
      *            this contour, for the end point of a quadratic curve
      */
     public void rQuadTo(float dx1, float dy1, float dx2, float dy2) {
-        isSimplePath = false;
         nRQuadTo(mNativePath, dx1, dy1, dx2, dy2);
     }
 
@@ -445,7 +396,6 @@
      */
     public void cubicTo(float x1, float y1, float x2, float y2,
                         float x3, float y3) {
-        isSimplePath = false;
         nCubicTo(mNativePath, x1, y1, x2, y2, x3, y3);
     }
 
@@ -456,7 +406,6 @@
      */
     public void rCubicTo(float x1, float y1, float x2, float y2,
                          float x3, float y3) {
-        isSimplePath = false;
         nRCubicTo(mNativePath, x1, y1, x2, y2, x3, y3);
     }
 
@@ -507,7 +456,6 @@
      */
     public void arcTo(float left, float top, float right, float bottom, float startAngle,
             float sweepAngle, boolean forceMoveTo) {
-        isSimplePath = false;
         nArcTo(mNativePath, left, top, right, bottom, startAngle, sweepAngle, forceMoveTo);
     }
 
@@ -516,7 +464,6 @@
      * first point of the contour, a line segment is automatically added.
      */
     public void close() {
-        isSimplePath = false;
         nClose(mNativePath);
     }
 
@@ -536,18 +483,6 @@
         final int nativeInt;
     }
 
-    private void detectSimplePath(float left, float top, float right, float bottom, Direction dir) {
-        if (mLastDirection == null) {
-            mLastDirection = dir;
-        }
-        if (mLastDirection != dir) {
-            isSimplePath = false;
-        } else {
-            if (rects == null) rects = new Region();
-            rects.op((int) left, (int) top, (int) right, (int) bottom, Region.Op.UNION);
-        }
-    }
-
     /**
      * Add a closed rectangle contour to the path
      *
@@ -568,7 +503,6 @@
      * @param dir    The direction to wind the rectangle's contour
      */
     public void addRect(float left, float top, float right, float bottom, @NonNull Direction dir) {
-        detectSimplePath(left, top, right, bottom, dir);
         nAddRect(mNativePath, left, top, right, bottom, dir.nativeInt);
     }
 
@@ -588,7 +522,6 @@
      * @param dir The direction to wind the oval's contour
      */
     public void addOval(float left, float top, float right, float bottom, @NonNull Direction dir) {
-        isSimplePath = false;
         nAddOval(mNativePath, left, top, right, bottom, dir.nativeInt);
     }
 
@@ -601,7 +534,6 @@
      * @param dir    The direction to wind the circle's contour
      */
     public void addCircle(float x, float y, float radius, @NonNull Direction dir) {
-        isSimplePath = false;
         nAddCircle(mNativePath, x, y, radius, dir.nativeInt);
     }
 
@@ -624,7 +556,6 @@
      */
     public void addArc(float left, float top, float right, float bottom, float startAngle,
             float sweepAngle) {
-        isSimplePath = false;
         nAddArc(mNativePath, left, top, right, bottom, startAngle, sweepAngle);
     }
 
@@ -649,7 +580,6 @@
      */
     public void addRoundRect(float left, float top, float right, float bottom, float rx, float ry,
             @NonNull Direction dir) {
-        isSimplePath = false;
         nAddRoundRect(mNativePath, left, top, right, bottom, rx, ry, dir.nativeInt);
     }
 
@@ -682,7 +612,6 @@
         if (radii.length < 8) {
             throw new ArrayIndexOutOfBoundsException("radii[] needs 8 values");
         }
-        isSimplePath = false;
         nAddRoundRect(mNativePath, left, top, right, bottom, radii, dir.nativeInt);
     }
 
@@ -693,7 +622,6 @@
      * @param dx  The amount to translate the path in X as it is added
      */
     public void addPath(@NonNull Path src, float dx, float dy) {
-        isSimplePath = false;
         nAddPath(mNativePath, src.mNativePath, dx, dy);
     }
 
@@ -703,7 +631,6 @@
      * @param src The path that is appended to the current path
      */
     public void addPath(@NonNull Path src) {
-        isSimplePath = false;
         nAddPath(mNativePath, src.mNativePath);
     }
 
@@ -713,7 +640,6 @@
      * @param src The path to add as a new contour
      */
     public void addPath(@NonNull Path src, @NonNull Matrix matrix) {
-        if (!src.isSimplePath) isSimplePath = false;
         nAddPath(mNativePath, src.mNativePath, matrix.ni());
     }
 
@@ -741,15 +667,6 @@
      * @param dy The amount in the Y direction to offset the entire path
      */
     public void offset(float dx, float dy) {
-        if (isSimplePath && rects == null) {
-            // nothing to offset
-            return;
-        }
-        if (isSimplePath && dx == Math.rint(dx) && dy == Math.rint(dy)) {
-            rects.translate((int) dx, (int) dy);
-        } else {
-            isSimplePath = false;
-        }
         nOffset(mNativePath, dx, dy);
     }
 
@@ -760,7 +677,6 @@
      * @param dy The new Y coordinate for the last point
      */
     public void setLastPoint(float dx, float dy) {
-        isSimplePath = false;
         nSetLastPoint(mNativePath, dx, dy);
     }
 
@@ -773,12 +689,7 @@
      *               then the the original path is modified
      */
     public void transform(@NonNull Matrix matrix, @Nullable Path dst) {
-        long dstNative = 0;
-        if (dst != null) {
-            dst.isSimplePath = false;
-            dstNative = dst.mNativePath;
-        }
-        nTransform(mNativePath, matrix.ni(), dstNative);
+        nTransform(mNativePath, matrix.ni(), dst != null ? dst.mNativePath : 0);
     }
 
     /**
@@ -787,7 +698,6 @@
      * @param matrix The matrix to apply to the path
      */
     public void transform(@NonNull Matrix matrix) {
-        isSimplePath = false;
         nTransform(mNativePath, matrix.ni());
     }
 
@@ -797,7 +707,6 @@
     }
 
     final long mutateNI() {
-        isSimplePath = false;
         return mNativePath;
     }
 
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index 8ae0d3d..25e1922 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -1557,16 +1557,6 @@
         return Arrays.binarySearch(mSupportedAxes, axis) >= 0;
     }
 
-    /** @hide */
-    public List<FontFamily> getFallback() {
-        ArrayList<FontFamily> families = new ArrayList<>();
-        int familySize = nativeGetFamilySize(native_instance);
-        for (int i = 0; i < familySize; ++i) {
-            families.add(new FontFamily(nativeGetFamily(native_instance, i)));
-        }
-        return families;
-    }
-
     private static native long nativeCreateFromTypeface(long native_instance, int style);
     private static native long nativeCreateFromTypefaceWithExactStyle(
             long native_instance, int weight, boolean italic);
@@ -1592,13 +1582,6 @@
     @CriticalNative
     private static native long nativeGetReleaseFunc();
 
-    @CriticalNative
-    private static native int nativeGetFamilySize(long naitvePtr);
-
-    @CriticalNative
-    private static native long nativeGetFamily(long nativePtr, int index);
-
-
     private static native void nativeRegisterGenericFamily(String str, long nativePtr);
 
     private static native int nativeWriteTypefaces(
diff --git a/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java b/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java
index 6939a72..47de37d 100644
--- a/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java
+++ b/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java
@@ -359,10 +359,10 @@
     /** @hide */
     @TestApi
     public Region getSafeZone() {
-        mMaskMatrix.reset();
+        Path mask = getIconMask();
         mMaskMatrix.setScale(SAFEZONE_SCALE, SAFEZONE_SCALE, getBounds().centerX(), getBounds().centerY());
         Path p = new Path();
-        mMask.transform(mMaskMatrix, p);
+        mask.transform(mMaskMatrix, p);
         Region safezoneRegion = new Region(getBounds());
         safezoneRegion.setPath(p, safezoneRegion);
         return safezoneRegion;
diff --git a/graphics/java/android/view/PixelCopy.java b/graphics/java/android/view/PixelCopy.java
index 2797a4d..0f82c8f 100644
--- a/graphics/java/android/view/PixelCopy.java
+++ b/graphics/java/android/view/PixelCopy.java
@@ -20,12 +20,15 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.graphics.Bitmap;
+import android.graphics.HardwareRenderer;
 import android.graphics.Rect;
 import android.os.Handler;
 import android.view.ViewTreeObserver.OnDrawListener;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
 
 /**
  * Provides a mechanisms to issue pixel copy requests to allow for copy
@@ -183,12 +186,10 @@
         if (srcRect != null && srcRect.isEmpty()) {
             throw new IllegalArgumentException("sourceRect is empty");
         }
-        // TODO: Make this actually async and fast and cool and stuff
-        int result = ThreadedRenderer.copySurfaceInto(source, srcRect, dest);
-        listenerThread.post(new Runnable() {
+        HardwareRenderer.copySurfaceInto(source, new HardwareRenderer.CopyRequest(srcRect, dest) {
             @Override
-            public void run() {
-                listener.onPixelCopyFinished(result);
+            public void onCopyFinished(int result) {
+                listenerThread.post(() -> listener.onPixelCopyFinished(result));
             }
         });
     }
@@ -255,30 +256,10 @@
             @NonNull Bitmap dest, @NonNull OnPixelCopyFinishedListener listener,
             @NonNull Handler listenerThread) {
         validateBitmapDest(dest);
-        if (source == null) {
-            throw new IllegalArgumentException("source is null");
-        }
-        if (source.peekDecorView() == null) {
-            throw new IllegalArgumentException(
-                    "Only able to copy windows with decor views");
-        }
-        Surface surface = null;
-        final ViewRootImpl root = source.peekDecorView().getViewRootImpl();
-        if (root != null) {
-            surface = root.mSurface;
-            final Rect surfaceInsets = root.mWindowAttributes.surfaceInsets;
-            if (srcRect == null) {
-                srcRect = new Rect(surfaceInsets.left, surfaceInsets.top,
-                        root.mWidth + surfaceInsets.left, root.mHeight + surfaceInsets.top);
-            } else {
-                srcRect.offset(surfaceInsets.left, surfaceInsets.top);
-            }
-        }
-        if (surface == null || !surface.isValid()) {
-            throw new IllegalArgumentException(
-                    "Window doesn't have a backing surface!");
-        }
-        request(surface, srcRect, dest, listener, listenerThread);
+        final Rect insets = new Rect();
+        final Surface surface = sourceForWindow(source, insets);
+        request(surface, adjustSourceRectForInsets(insets, srcRect), dest, listener,
+                listenerThread);
     }
 
     private static void validateBitmapDest(Bitmap bitmap) {
@@ -294,5 +275,235 @@
         }
     }
 
+    private static Surface sourceForWindow(Window source, Rect outInsets) {
+        if (source == null) {
+            throw new IllegalArgumentException("source is null");
+        }
+        if (source.peekDecorView() == null) {
+            throw new IllegalArgumentException(
+                    "Only able to copy windows with decor views");
+        }
+        Surface surface = null;
+        final ViewRootImpl root = source.peekDecorView().getViewRootImpl();
+        if (root != null) {
+            surface = root.mSurface;
+            final Rect surfaceInsets = root.mWindowAttributes.surfaceInsets;
+            outInsets.set(surfaceInsets.left, surfaceInsets.top,
+                    root.mWidth + surfaceInsets.left, root.mHeight + surfaceInsets.top);
+        }
+        if (surface == null || !surface.isValid()) {
+            throw new IllegalArgumentException(
+                    "Window doesn't have a backing surface!");
+        }
+        return surface;
+    }
+
+    private static Rect adjustSourceRectForInsets(Rect insets, Rect srcRect) {
+        if (srcRect == null) {
+            return insets;
+        }
+        if (insets != null) {
+            srcRect.offset(insets.left, insets.top);
+        }
+        return srcRect;
+    }
+
+    /**
+     * Contains the result of a PixelCopy request
+     */
+    public static final class CopyResult {
+        private int mStatus;
+        private Bitmap mBitmap;
+
+        private CopyResult(@CopyResultStatus int status, Bitmap bitmap) {
+            mStatus = status;
+            mBitmap = bitmap;
+        }
+
+        /**
+         * Returns the {@link CopyResultStatus} of the copy request.
+         */
+        public @CopyResultStatus int getStatus() {
+            return mStatus;
+        }
+
+        private void validateStatus() {
+            if (mStatus != SUCCESS) {
+                throw new IllegalStateException("Copy request didn't succeed, status = " + mStatus);
+            }
+        }
+
+        /**
+         * If the PixelCopy {@link Request} was given a destination bitmap with
+         * {@link Request#setDestinationBitmap(Bitmap)} then the returned bitmap will be the same
+         * as the one given. If no destination bitmap was provided, then this
+         * will contain the automatically allocated Bitmap to hold the result.
+         *
+         * @return the Bitmap the copy request was stored in.
+         * @throws IllegalStateException if {@link #getStatus()} is not SUCCESS
+         */
+        public @NonNull Bitmap getBitmap() {
+            validateStatus();
+            return mBitmap;
+        }
+    }
+
+    /**
+     * A builder to create the complete PixelCopy request, which is then executed by calling
+     * {@link #request()}
+     */
+    public static final class Request {
+        private Request(Surface source, Rect sourceInsets, Executor listenerThread,
+                        Consumer<CopyResult> listener) {
+            this.mSource = source;
+            this.mSourceInsets = sourceInsets;
+            this.mListenerThread = listenerThread;
+            this.mListener = listener;
+        }
+
+        private final Surface mSource;
+        private final Consumer<CopyResult> mListener;
+        private final Executor mListenerThread;
+        private final Rect mSourceInsets;
+        private Rect mSrcRect;
+        private Bitmap mDest;
+
+        /**
+         * Sets the region of the source to copy from. By default, the entire source is copied to
+         * the output. If only a subset of the source is necessary to be copied, specifying a
+         * srcRect will improve performance by reducing
+         * the amount of data being copied.
+         *
+         * @param srcRect The area of the source to read from. Null or empty will be treated to
+         *                mean the entire source
+         * @return this
+         */
+        public @NonNull Request setSourceRect(@Nullable Rect srcRect) {
+            this.mSrcRect = srcRect;
+            return this;
+        }
+
+        /**
+         * Specifies the output bitmap in which to store the result. By default, a Bitmap of format
+         * {@link android.graphics.Bitmap.Config#ARGB_8888} with a width & height matching that
+         * of the {@link #setSourceRect(Rect) source area} will be created to place the result.
+         *
+         * @param destination The bitmap to store the result, or null to have a bitmap
+         *                    automatically created of the appropriate size. If not null, must not
+         *                    be {@link Bitmap#isRecycled() recycled} and must be
+         *                    {@link Bitmap#isMutable() mutable}.
+         * @return this
+         */
+        public @NonNull Request setDestinationBitmap(@Nullable Bitmap destination) {
+            if (destination != null) {
+                validateBitmapDest(destination);
+            }
+            this.mDest = destination;
+            return this;
+        }
+
+        /**
+         * Executes the request.
+         */
+        public void request() {
+            if (!mSource.isValid()) {
+                mListenerThread.execute(() -> mListener.accept(
+                        new CopyResult(ERROR_SOURCE_INVALID, null)));
+                return;
+            }
+            HardwareRenderer.copySurfaceInto(mSource, new HardwareRenderer.CopyRequest(
+                    adjustSourceRectForInsets(mSourceInsets, mSrcRect), mDest) {
+                @Override
+                public void onCopyFinished(int result) {
+                    mListenerThread.execute(() -> mListener.accept(
+                            new CopyResult(result, mDestinationBitmap)));
+                }
+            });
+        }
+    }
+
+    /**
+     * Creates a PixelCopy request for the given {@link Window}
+     * @param source The Window to copy from
+     * @param callbackExecutor The executor to run the callback on
+     * @param listener The callback for when the copy request is completed
+     * @return A {@link Request} builder to set the optional params & execute the request
+     */
+    public static @NonNull Request ofWindow(@NonNull Window source,
+                                            @NonNull Executor callbackExecutor,
+                                            @NonNull Consumer<CopyResult> listener) {
+        final Rect insets = new Rect();
+        final Surface surface = sourceForWindow(source, insets);
+        return new Request(surface, insets, callbackExecutor, listener);
+    }
+
+    /**
+     * Creates a PixelCopy request for the {@link Window} that the given {@link View} is
+     * attached to.
+     *
+     * Note that this copy request is not cropped to the area the View occupies by default. If that
+     * behavior is desired, use {@link View#getLocationInWindow(int[])} combined with
+     * {@link Request#setSourceRect(Rect)} to set a crop area to restrict the copy operation.
+     *
+     * @param source A View that {@link View#isAttachedToWindow() is attached} to a window that
+     *               will be used to retrieve the window to copy from.
+     * @param callbackExecutor The executor to run the callback on
+     * @param listener The callback for when the copy request is completed
+     * @return A {@link Request} builder to set the optional params & execute the request
+     */
+    public static @NonNull Request ofWindow(@NonNull View source,
+                                            @NonNull Executor callbackExecutor,
+                                            @NonNull Consumer<CopyResult> listener) {
+        if (source == null || !source.isAttachedToWindow()) {
+            throw new IllegalArgumentException(
+                    "View must not be null & must be attached to window");
+        }
+        final Rect insets = new Rect();
+        Surface surface = null;
+        final ViewRootImpl root = source.getViewRootImpl();
+        if (root != null) {
+            surface = root.mSurface;
+            insets.set(root.mWindowAttributes.surfaceInsets);
+        }
+        if (surface == null || !surface.isValid()) {
+            throw new IllegalArgumentException(
+                    "Window doesn't have a backing surface!");
+        }
+        return new Request(surface, insets, callbackExecutor, listener);
+    }
+
+    /**
+     * Creates a PixelCopy request for the given {@link Surface}
+     *
+     * @param source The Surface to copy from. Must be {@link Surface#isValid() valid}.
+     * @param callbackExecutor The executor to run the callback on
+     * @param listener The callback for when the copy request is completed
+     * @return A {@link Request} builder to set the optional params & execute the request
+     */
+    public static @NonNull Request ofSurface(@NonNull Surface source,
+                                             @NonNull Executor callbackExecutor,
+                                             @NonNull Consumer<CopyResult> listener) {
+        if (source == null || !source.isValid()) {
+            throw new IllegalArgumentException("Source must not be null & must be valid");
+        }
+        return new Request(source, null, callbackExecutor, listener);
+    }
+
+    /**
+     * Creates a PixelCopy request for the {@link Surface} belonging to the
+     * given {@link SurfaceView}
+     *
+     * @param source The SurfaceView to copy from. The backing surface must be
+     *               {@link Surface#isValid() valid}
+     * @param callbackExecutor The executor to run the callback on
+     * @param listener The callback for when the copy request is completed
+     * @return A {@link Request} builder to set the optional params & execute the request
+     */
+    public static @NonNull Request ofSurface(@NonNull SurfaceView source,
+                                             @NonNull Executor callbackExecutor,
+                                             @NonNull Consumer<CopyResult> listener) {
+        return ofSurface(source.getHolder().getSurface(), callbackExecutor, listener);
+    }
+
     private PixelCopy() {}
 }
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java
index 0dba6b0..d42fca2 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java
@@ -51,12 +51,6 @@
     @VisibleForTesting
     final Map<IBinder, TaskFragmentInfo> mFragmentInfos = new ArrayMap<>();
 
-    /**
-     * Mapping from the client assigned unique token to the TaskFragment parent
-     * {@link Configuration}.
-     */
-    final Map<IBinder, Configuration> mFragmentParentConfigs = new ArrayMap<>();
-
     private final TaskFragmentCallback mCallback;
     @VisibleForTesting
     TaskFragmentAnimationController mAnimationController;
@@ -68,8 +62,7 @@
         void onTaskFragmentAppeared(@NonNull TaskFragmentInfo taskFragmentInfo);
         void onTaskFragmentInfoChanged(@NonNull TaskFragmentInfo taskFragmentInfo);
         void onTaskFragmentVanished(@NonNull TaskFragmentInfo taskFragmentInfo);
-        void onTaskFragmentParentInfoChanged(@NonNull IBinder fragmentToken,
-                @NonNull Configuration parentConfig);
+        void onTaskFragmentParentInfoChanged(int taskId, @NonNull Configuration parentConfig);
         void onActivityReparentToTask(int taskId, @NonNull Intent activityIntent,
                 @NonNull IBinder activityToken);
         void onTaskFragmentError(@Nullable TaskFragmentInfo taskFragmentInfo, int opType);
@@ -300,7 +293,6 @@
     @Override
     public void onTaskFragmentVanished(@NonNull TaskFragmentInfo taskFragmentInfo) {
         mFragmentInfos.remove(taskFragmentInfo.getFragmentToken());
-        mFragmentParentConfigs.remove(taskFragmentInfo.getFragmentToken());
 
         if (mCallback != null) {
             mCallback.onTaskFragmentVanished(taskFragmentInfo);
@@ -308,12 +300,9 @@
     }
 
     @Override
-    public void onTaskFragmentParentInfoChanged(
-            @NonNull IBinder fragmentToken, @NonNull Configuration parentConfig) {
-        mFragmentParentConfigs.put(fragmentToken, parentConfig);
-
+    public void onTaskFragmentParentInfoChanged(int taskId, @NonNull Configuration parentConfig) {
         if (mCallback != null) {
-            mCallback.onTaskFragmentParentInfoChanged(fragmentToken, parentConfig);
+            mCallback.onTaskFragmentParentInfoChanged(taskId, parentConfig);
         }
     }
 
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 c688080..dad0739 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -155,6 +155,9 @@
             container.setInfo(taskFragmentInfo);
             if (container.isFinished()) {
                 mPresenter.cleanupContainer(container, false /* shouldFinishDependent */);
+            } else {
+                // Update with the latest Task configuration.
+                mPresenter.updateContainer(container);
             }
             updateCallbackIfNecessary();
         }
@@ -233,19 +236,30 @@
     }
 
     @Override
-    public void onTaskFragmentParentInfoChanged(@NonNull IBinder fragmentToken,
-            @NonNull Configuration parentConfig) {
+    public void onTaskFragmentParentInfoChanged(int taskId, @NonNull Configuration parentConfig) {
         synchronized (mLock) {
-            final TaskFragmentContainer container = getContainer(fragmentToken);
-            if (container != null) {
-                onTaskConfigurationChanged(container.getTaskId(), parentConfig);
-                if (isInPictureInPicture(parentConfig)) {
-                    // No need to update presentation in PIP until the Task exit PIP.
-                    return;
-                }
-                mPresenter.updateContainer(container);
-                updateCallbackIfNecessary();
+            onTaskConfigurationChanged(taskId, parentConfig);
+            if (isInPictureInPicture(parentConfig)) {
+                // No need to update presentation in PIP until the Task exit PIP.
+                return;
             }
+            final TaskContainer taskContainer = getTaskContainer(taskId);
+            if (taskContainer == null || taskContainer.isEmpty()) {
+                Log.e(TAG, "onTaskFragmentParentInfoChanged on empty Task id=" + taskId);
+                return;
+            }
+            // Update all TaskFragments in the Task. Make a copy of the list since some may be
+            // removed on updating.
+            final List<TaskFragmentContainer> containers =
+                    new ArrayList<>(taskContainer.mContainers);
+            for (int i = containers.size() - 1; i >= 0; i--) {
+                final TaskFragmentContainer container = containers.get(i);
+                // Wait until onTaskFragmentAppeared to update new container.
+                if (!container.isFinished() && !container.isWaitingActivityAppear()) {
+                    mPresenter.updateContainer(container);
+                }
+            }
+            updateCallbackIfNecessary();
         }
     }
 
diff --git a/libs/WindowManager/Jetpack/window-extensions-release.aar b/libs/WindowManager/Jetpack/window-extensions-release.aar
index f54ab08..918e514 100644
--- a/libs/WindowManager/Jetpack/window-extensions-release.aar
+++ b/libs/WindowManager/Jetpack/window-extensions-release.aar
Binary files differ
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
index 5cba3b4..6ae0f9b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
@@ -55,6 +55,7 @@
 import com.android.wm.shell.compatui.CompatUIController;
 import com.android.wm.shell.recents.RecentTasksController;
 import com.android.wm.shell.startingsurface.StartingWindowController;
+import com.android.wm.shell.sysui.ShellCommandHandler;
 import com.android.wm.shell.sysui.ShellInit;
 import com.android.wm.shell.unfold.UnfoldAnimationController;
 
@@ -72,6 +73,7 @@
  */
 public class ShellTaskOrganizer extends TaskOrganizer implements
         CompatUIController.CompatUICallback {
+    private static final String TAG = "ShellTaskOrganizer";
 
     // Intentionally using negative numbers here so the positive numbers can be used
     // for task id specific listeners that will be added later.
@@ -90,8 +92,6 @@
     })
     public @interface TaskListenerType {}
 
-    private static final String TAG = "ShellTaskOrganizer";
-
     /**
      * Callbacks for when the tasks change in the system.
      */
@@ -177,6 +177,9 @@
     @Nullable
     private final CompatUIController mCompatUI;
 
+    @NonNull
+    private final ShellCommandHandler mShellCommandHandler;
+
     @Nullable
     private final Optional<RecentTasksController> mRecentTasks;
 
@@ -187,29 +190,33 @@
     private RunningTaskInfo mLastFocusedTaskInfo;
 
     public ShellTaskOrganizer(ShellExecutor mainExecutor) {
-        this(null /* shellInit */, null /* taskOrganizerController */, null /* compatUI */,
+        this(null /* shellInit */, null /* shellCommandHandler */,
+                null /* taskOrganizerController */, null /* compatUI */,
                 Optional.empty() /* unfoldAnimationController */,
                 Optional.empty() /* recentTasksController */,
                 mainExecutor);
     }
 
     public ShellTaskOrganizer(ShellInit shellInit,
+            ShellCommandHandler shellCommandHandler,
             @Nullable CompatUIController compatUI,
             Optional<UnfoldAnimationController> unfoldAnimationController,
             Optional<RecentTasksController> recentTasks,
             ShellExecutor mainExecutor) {
-        this(shellInit, null /* taskOrganizerController */, compatUI,
+        this(shellInit, shellCommandHandler, null /* taskOrganizerController */, compatUI,
                 unfoldAnimationController, recentTasks, mainExecutor);
     }
 
     @VisibleForTesting
     protected ShellTaskOrganizer(ShellInit shellInit,
+            ShellCommandHandler shellCommandHandler,
             ITaskOrganizerController taskOrganizerController,
             @Nullable CompatUIController compatUI,
             Optional<UnfoldAnimationController> unfoldAnimationController,
             Optional<RecentTasksController> recentTasks,
             ShellExecutor mainExecutor) {
         super(taskOrganizerController, mainExecutor);
+        mShellCommandHandler = shellCommandHandler;
         mCompatUI = compatUI;
         mRecentTasks = recentTasks;
         mUnfoldAnimationController = unfoldAnimationController.orElse(null);
@@ -219,6 +226,7 @@
     }
 
     private void onInit() {
+        mShellCommandHandler.addDumpCallback(this::dump, this);
         if (mCompatUI != null) {
             mCompatUI.setCompatUICallback(this);
         }
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 d3dc7e8..0283be3 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
@@ -57,6 +57,7 @@
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.annotations.ShellBackgroundThread;
 import com.android.wm.shell.common.annotations.ShellMainThread;
+import com.android.wm.shell.sysui.ShellInit;
 
 import java.util.concurrent.atomic.AtomicBoolean;
 
@@ -91,7 +92,6 @@
      * Raw delta between {@link #mInitTouchLocation} and the last touch location.
      */
     private final Point mTouchEventDelta = new Point();
-    private final ShellExecutor mShellExecutor;
 
     /** True when a back gesture is ongoing */
     private boolean mBackGestureStarted = false;
@@ -106,6 +106,9 @@
     private final SurfaceControl.Transaction mTransaction;
     private final IActivityTaskManager mActivityTaskManager;
     private final Context mContext;
+    private final ContentResolver mContentResolver;
+    private final ShellExecutor mShellExecutor;
+    private final Handler mBgHandler;
     @Nullable
     private IOnBackInvokedCallback mBackToLauncherCallback;
     private float mTriggerThreshold;
@@ -136,16 +139,19 @@
     };
 
     public BackAnimationController(
+            @NonNull ShellInit shellInit,
             @NonNull @ShellMainThread ShellExecutor shellExecutor,
             @NonNull @ShellBackgroundThread Handler backgroundHandler,
             Context context) {
-        this(shellExecutor, backgroundHandler, new SurfaceControl.Transaction(),
+        this(shellInit, shellExecutor, backgroundHandler, new SurfaceControl.Transaction(),
                 ActivityTaskManager.getService(), context, context.getContentResolver());
     }
 
     @VisibleForTesting
-    BackAnimationController(@NonNull @ShellMainThread ShellExecutor shellExecutor,
-            @NonNull @ShellBackgroundThread Handler handler,
+    BackAnimationController(
+            @NonNull ShellInit shellInit,
+            @NonNull @ShellMainThread ShellExecutor shellExecutor,
+            @NonNull @ShellBackgroundThread Handler bgHandler,
             @NonNull SurfaceControl.Transaction transaction,
             @NonNull IActivityTaskManager activityTaskManager,
             Context context, ContentResolver contentResolver) {
@@ -153,7 +159,13 @@
         mTransaction = transaction;
         mActivityTaskManager = activityTaskManager;
         mContext = context;
-        setupAnimationDeveloperSettingsObserver(contentResolver, handler);
+        mContentResolver = contentResolver;
+        mBgHandler = bgHandler;
+        shellInit.addInitCallback(this::onInit, this);
+    }
+
+    private void onInit() {
+        setupAnimationDeveloperSettingsObserver(mContentResolver, mBgHandler);
     }
 
     private void setupAnimationDeveloperSettingsObserver(
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
index 31fc6a5..2c02006 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
@@ -452,6 +452,7 @@
      */
     void setEntry(@NonNull final BubbleEntry entry) {
         Objects.requireNonNull(entry);
+        boolean showingDotPreviously = showDot();
         mLastUpdated = entry.getStatusBarNotification().getPostTime();
         mIsBubble = entry.getStatusBarNotification().getNotification().isBubbleNotification();
         mPackageName = entry.getStatusBarNotification().getPackageName();
@@ -498,6 +499,10 @@
         mShouldSuppressNotificationDot = entry.shouldSuppressNotificationDot();
         mShouldSuppressNotificationList = entry.shouldSuppressNotificationList();
         mShouldSuppressPeek = entry.shouldSuppressPeek();
+        if (showingDotPreviously != showDot()) {
+            // This will update the UI if needed
+            setShowDot(showDot());
+        }
     }
 
     @Nullable
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleBadgeIconFactory.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleBadgeIconFactory.java
index 4eeb207..d6803e8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleBadgeIconFactory.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleBadgeIconFactory.java
@@ -19,14 +19,14 @@
 import android.content.Context;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
-import android.graphics.Paint;
+import android.graphics.Color;
 import android.graphics.Path;
+import android.graphics.Rect;
 import android.graphics.drawable.AdaptiveIconDrawable;
 import android.graphics.drawable.Drawable;
 
 import com.android.launcher3.icons.BaseIconFactory;
 import com.android.launcher3.icons.BitmapInfo;
-import com.android.launcher3.icons.ShadowGenerator;
 import com.android.wm.shell.R;
 
 /**
@@ -44,78 +44,77 @@
      * will include the workprofile indicator on the badge if appropriate.
      */
     BitmapInfo getBadgeBitmap(Drawable userBadgedAppIcon, boolean isImportantConversation) {
-        ShadowGenerator shadowGenerator = new ShadowGenerator(mIconBitmapSize);
-        Bitmap userBadgedBitmap = createIconBitmap(userBadgedAppIcon, 1f, mIconBitmapSize);
-
         if (userBadgedAppIcon instanceof AdaptiveIconDrawable) {
-            userBadgedBitmap = Bitmap.createScaledBitmap(
-                    getCircleBitmap((AdaptiveIconDrawable) userBadgedAppIcon, /* size */
-                            userBadgedAppIcon.getIntrinsicWidth()),
-                    mIconBitmapSize, mIconBitmapSize, /* filter */ true);
+            AdaptiveIconDrawable ad = (AdaptiveIconDrawable) userBadgedAppIcon;
+            userBadgedAppIcon = new CircularAdaptiveIcon(ad.getBackground(), ad.getForeground());
+        }
+        if (isImportantConversation) {
+            userBadgedAppIcon = new CircularRingDrawable(userBadgedAppIcon);
+        }
+        Bitmap userBadgedBitmap = createIconBitmap(
+                userBadgedAppIcon, 1, BITMAP_GENERATION_MODE_WITH_SHADOW);
+        return createIconBitmap(userBadgedBitmap);
+    }
+
+    private class CircularRingDrawable extends CircularAdaptiveIcon {
+
+        final int mImportantConversationColor;
+        final Rect mTempBounds = new Rect();
+
+        final Drawable mDr;
+
+        CircularRingDrawable(Drawable dr) {
+            super(null, null);
+            mDr = dr;
+            mImportantConversationColor = mContext.getResources().getColor(
+                    R.color.important_conversation, null);
         }
 
-        if (isImportantConversation) {
-            final float ringStrokeWidth = mContext.getResources().getDimensionPixelSize(
+        @Override
+        public void draw(Canvas canvas) {
+            int save = canvas.save();
+            canvas.clipPath(getIconMask());
+            canvas.drawColor(mImportantConversationColor);
+            int ringStrokeWidth = mContext.getResources().getDimensionPixelSize(
                     com.android.internal.R.dimen.importance_ring_stroke_width);
-            final int importantConversationColor = mContext.getResources().getColor(
-                    R.color.important_conversation, null);
-            Bitmap badgeAndRing = Bitmap.createBitmap(userBadgedBitmap.getWidth(),
-                    userBadgedBitmap.getHeight(), userBadgedBitmap.getConfig());
-            Canvas c = new Canvas(badgeAndRing);
-
-            Paint ringPaint = new Paint();
-            ringPaint.setStyle(Paint.Style.FILL);
-            ringPaint.setColor(importantConversationColor);
-            ringPaint.setAntiAlias(true);
-            c.drawCircle(c.getWidth() / 2, c.getHeight() / 2, c.getWidth() / 2, ringPaint);
-
-            final int bitmapTop = (int) ringStrokeWidth;
-            final int bitmapLeft = (int) ringStrokeWidth;
-            final int bitmapWidth = c.getWidth() - 2 * (int) ringStrokeWidth;
-            final int bitmapHeight = c.getHeight() - 2 * (int) ringStrokeWidth;
-
-            Bitmap scaledBitmap = Bitmap.createScaledBitmap(userBadgedBitmap, bitmapWidth,
-                    bitmapHeight, /* filter */ true);
-            c.drawBitmap(scaledBitmap, bitmapTop, bitmapLeft, /* paint */null);
-
-            shadowGenerator.recreateIcon(Bitmap.createBitmap(badgeAndRing), c);
-            return createIconBitmap(badgeAndRing);
-        } else {
-            Canvas c = new Canvas();
-            c.setBitmap(userBadgedBitmap);
-            shadowGenerator.recreateIcon(Bitmap.createBitmap(userBadgedBitmap), c);
-            return createIconBitmap(userBadgedBitmap);
+            mTempBounds.set(getBounds());
+            mTempBounds.inset(ringStrokeWidth, ringStrokeWidth);
+            mDr.setBounds(mTempBounds);
+            mDr.draw(canvas);
+            canvas.restoreToCount(save);
         }
     }
 
-    private Bitmap getCircleBitmap(AdaptiveIconDrawable icon, int size) {
-        Drawable foreground = icon.getForeground();
-        Drawable background = icon.getBackground();
-        Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
-        Canvas canvas = new Canvas();
-        canvas.setBitmap(bitmap);
+    private static class CircularAdaptiveIcon extends AdaptiveIconDrawable {
 
-        // Clip canvas to circle.
-        Path circlePath = new Path();
-        circlePath.addCircle(/* x */ size / 2f,
-                /* y */ size / 2f,
-                /* radius */ size / 2f,
-                Path.Direction.CW);
-        canvas.clipPath(circlePath);
+        final Path mPath = new Path();
 
-        // Draw background.
-        background.setBounds(0, 0, size, size);
-        background.draw(canvas);
+        CircularAdaptiveIcon(Drawable bg, Drawable fg) {
+            super(bg, fg);
+        }
 
-        // Draw foreground. The foreground and background drawables are derived from adaptive icons
-        // Some icon shapes fill more space than others, so adaptive icons are normalized to about
-        // the same size. This size is smaller than the original bounds, so we estimate
-        // the difference in this offset.
-        int offset = size / 5;
-        foreground.setBounds(-offset, -offset, size + offset, size + offset);
-        foreground.draw(canvas);
+        @Override
+        public Path getIconMask() {
+            mPath.reset();
+            Rect bounds = getBounds();
+            mPath.addOval(bounds.left, bounds.top, bounds.right, bounds.bottom, Path.Direction.CW);
+            return mPath;
+        }
 
-        canvas.setBitmap(null);
-        return bitmap;
+        @Override
+        public void draw(Canvas canvas) {
+            int save = canvas.save();
+            canvas.clipPath(getIconMask());
+
+            canvas.drawColor(Color.BLACK);
+            Drawable d;
+            if ((d = getBackground()) != null) {
+                d.draw(canvas);
+            }
+            if ((d = getForeground()) != null) {
+                d.draw(canvas);
+            }
+            canvas.restoreToCount(save);
+        }
     }
 }
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 3982616..8771ceb 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
@@ -1055,18 +1055,28 @@
     public void updateBubble(BubbleEntry notif, boolean suppressFlyout, boolean showInShade) {
         // If this is an interruptive notif, mark that it's interrupted
         mSysuiProxy.setNotificationInterruption(notif.getKey());
-        if (!notif.getRanking().isTextChanged()
+        boolean isNonInterruptiveNotExpanding = !notif.getRanking().isTextChanged()
                 && (notif.getBubbleMetadata() != null
-                && !notif.getBubbleMetadata().getAutoExpandBubble())
+                && !notif.getBubbleMetadata().getAutoExpandBubble());
+        if (isNonInterruptiveNotExpanding
                 && mBubbleData.hasOverflowBubbleWithKey(notif.getKey())) {
             // Update the bubble but don't promote it out of overflow
             Bubble b = mBubbleData.getOverflowBubbleWithKey(notif.getKey());
-            b.setEntry(notif);
+            if (notif.isBubble()) {
+                notif.setFlagBubble(false);
+            }
+            updateNotNotifyingEntry(b, notif, showInShade);
+        } else if (mBubbleData.hasAnyBubbleWithKey(notif.getKey())
+                && isNonInterruptiveNotExpanding) {
+            Bubble b = mBubbleData.getAnyBubbleWithkey(notif.getKey());
+            if (b != null) {
+                updateNotNotifyingEntry(b, notif, showInShade);
+            }
         } else if (mBubbleData.isSuppressedWithLocusId(notif.getLocusId())) {
             // Update the bubble but don't promote it out of overflow
             Bubble b = mBubbleData.getSuppressedBubbleWithKey(notif.getKey());
             if (b != null) {
-                b.setEntry(notif);
+                updateNotNotifyingEntry(b, notif, showInShade);
             }
         } else {
             Bubble bubble = mBubbleData.getOrCreateBubble(notif, null /* persistedBubble */);
@@ -1076,13 +1086,25 @@
                 if (bubble.shouldAutoExpand()) {
                     bubble.setShouldAutoExpand(false);
                 }
+                mImpl.mCachedState.updateBubbleSuppressedState(bubble);
             } else {
                 inflateAndAdd(bubble, suppressFlyout, showInShade);
             }
         }
     }
 
-    void inflateAndAdd(Bubble bubble, boolean suppressFlyout, boolean showInShade) {
+    void updateNotNotifyingEntry(Bubble b, BubbleEntry entry, boolean showInShade) {
+        boolean isBubbleSelected = Objects.equals(b, mBubbleData.getSelectedBubble());
+        boolean isBubbleExpandedAndSelected = isStackExpanded() && isBubbleSelected;
+        b.setEntry(entry);
+        boolean suppress = isBubbleExpandedAndSelected || !showInShade || !b.showInShade();
+        b.setSuppressNotification(suppress);
+        b.setShowDot(!isBubbleExpandedAndSelected);
+        mImpl.mCachedState.updateBubbleSuppressedState(b);
+    }
+
+    @VisibleForTesting
+    public void inflateAndAdd(Bubble bubble, boolean suppressFlyout, boolean showInShade) {
         // Lazy init stack view when a bubble is created
         ensureStackViewCreated();
         bubble.setInflateSynchronously(mInflateSynchronously);
@@ -1111,7 +1133,10 @@
     }
 
     @VisibleForTesting
-    public void onEntryUpdated(BubbleEntry entry, boolean shouldBubbleUp) {
+    public void onEntryUpdated(BubbleEntry entry, boolean shouldBubbleUp, boolean fromSystem) {
+        if (!fromSystem) {
+            return;
+        }
         // shouldBubbleUp checks canBubble & for bubble metadata
         boolean shouldBubble = shouldBubbleUp && canLaunchInTaskView(mContext, entry);
         if (!shouldBubble && mBubbleData.hasAnyBubbleWithKey(entry.getKey())) {
@@ -1172,9 +1197,9 @@
                 // notification, so that the bubble will be re-created if shouldBubbleUp returns
                 // true.
                 mBubbleData.dismissBubbleWithKey(key, DISMISS_NO_BUBBLE_UP);
-            } else if (entry != null && mTmpRanking.isBubble() && !isActive) {
+            } else if (entry != null && mTmpRanking.isBubble() && !isActiveOrInOverflow) {
                 entry.setFlagBubble(true);
-                onEntryUpdated(entry, shouldBubbleUp);
+                onEntryUpdated(entry, shouldBubbleUp, /* fromSystem= */ true);
             }
         }
     }
@@ -1773,9 +1798,9 @@
         }
 
         @Override
-        public void onEntryUpdated(BubbleEntry entry, boolean shouldBubbleUp) {
+        public void onEntryUpdated(BubbleEntry entry, boolean shouldBubbleUp, boolean fromSystem) {
             mMainExecutor.execute(() -> {
-                BubbleController.this.onEntryUpdated(entry, shouldBubbleUp);
+                BubbleController.this.onEntryUpdated(entry, shouldBubbleUp, fromSystem);
             });
         }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleIconFactory.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleIconFactory.java
index 9d3bf34..5dab8a0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleIconFactory.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleIconFactory.java
@@ -21,6 +21,7 @@
 import android.content.Intent;
 import android.content.pm.LauncherApps;
 import android.content.pm.ShortcutInfo;
+import android.graphics.Bitmap;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.Icon;
 
@@ -65,4 +66,19 @@
             return null;
         }
     }
+
+    /**
+     * Creates the bitmap for the provided drawable and returns the scale used for
+     * drawing the actual drawable.
+     */
+    public Bitmap createIconBitmap(@NonNull Drawable icon, float[] outScale) {
+        if (outScale == null) {
+            outScale = new float[1];
+        }
+        icon = normalizeAndWrapToAdaptiveIcon(icon,
+                true /* shrinkNonAdaptiveIcons */,
+                null /* outscale */,
+                outScale);
+        return createIconBitmap(icon, outScale[0], BITMAP_GENERATION_MODE_WITH_SHADOW);
+    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java
index 69762c9..f437553 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java
@@ -195,15 +195,18 @@
                     b.isImportantConversation());
             info.badgeBitmap = badgeBitmapInfo.icon;
             // Raw badge bitmap never includes the important conversation ring
-            info.mRawBadgeBitmap = badgeIconFactory.getBadgeBitmap(badgedIcon, false).icon;
-            info.bubbleBitmap = iconFactory.createBadgedIconBitmap(bubbleDrawable).icon;
+            info.mRawBadgeBitmap = b.isImportantConversation()
+                    ? badgeIconFactory.getBadgeBitmap(badgedIcon, false).icon
+                    : badgeBitmapInfo.icon;
+
+            float[] bubbleBitmapScale = new float[1];
+            info.bubbleBitmap = iconFactory.createIconBitmap(bubbleDrawable, bubbleBitmapScale);
 
             // Dot color & placement
             Path iconPath = PathParser.createPathFromPathData(
                     c.getResources().getString(com.android.internal.R.string.config_icon_mask));
             Matrix matrix = new Matrix();
-            float scale = iconFactory.getNormalizer().getScale(bubbleDrawable,
-                    null /* outBounds */, null /* path */, null /* outMaskShape */);
+            float scale = bubbleBitmapScale[0];
             float radius = DEFAULT_PATH_SIZE / 2f;
             matrix.setScale(scale /* x scale */, scale /* y scale */, radius /* pivot x */,
                     radius /* pivot y */);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
index cf792cd..37b96ff 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
@@ -171,8 +171,9 @@
      *
      * @param entry the {@link BubbleEntry} by the notification.
      * @param shouldBubbleUp {@code true} if this notification should bubble up.
+     * @param fromSystem {@code true} if this update is from NotificationManagerService.
      */
-    void onEntryUpdated(BubbleEntry entry, boolean shouldBubbleUp);
+    void onEntryUpdated(BubbleEntry entry, boolean shouldBubbleUp, boolean fromSystem);
 
     /**
      * Called when new notification entry removed.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
index 40cf9a3..85c8ebf 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
@@ -1130,16 +1130,16 @@
             boolean adjusted = false;
             if (mYOffsetForIme != 0) {
                 if (dividerLeash != null) {
-                    mTempRect.set(mDividerBounds);
+                    getRefDividerBounds(mTempRect);
                     mTempRect.offset(0, mYOffsetForIme);
                     t.setPosition(dividerLeash, mTempRect.left, mTempRect.top);
                 }
 
-                mTempRect.set(mBounds1);
+                getRefBounds1(mTempRect);
                 mTempRect.offset(0, mYOffsetForIme);
                 t.setPosition(leash1, mTempRect.left, mTempRect.top);
 
-                mTempRect.set(mBounds2);
+                getRefBounds2(mTempRect);
                 mTempRect.offset(0, mYOffsetForIme);
                 t.setPosition(leash2, mTempRect.left, mTempRect.top);
                 adjusted = true;
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 db8d9d4..235fd9c 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
@@ -43,6 +43,7 @@
 import com.android.wm.shell.compatui.letterboxedu.LetterboxEduWindowManager;
 import com.android.wm.shell.sysui.KeyguardChangeListener;
 import com.android.wm.shell.sysui.ShellController;
+import com.android.wm.shell.sysui.ShellInit;
 import com.android.wm.shell.transition.Transitions;
 
 import java.lang.ref.WeakReference;
@@ -119,6 +120,7 @@
     private boolean mKeyguardShowing;
 
     public CompatUIController(Context context,
+            ShellInit shellInit,
             ShellController shellController,
             DisplayController displayController,
             DisplayInsetsController displayInsetsController,
@@ -134,10 +136,14 @@
         mSyncQueue = syncQueue;
         mMainExecutor = mainExecutor;
         mTransitionsLazy = transitionsLazy;
+        mCompatUIHintsState = new CompatUIHintsState();
+        shellInit.addInitCallback(this::onInit, this);
+    }
+
+    private void onInit() {
+        mShellController.addKeyguardChangeListener(this);
         mDisplayController.addDisplayWindowListener(this);
         mImeController.addPositionProcessor(this);
-        mCompatUIHintsState = new CompatUIHintsState();
-        shellController.addKeyguardChangeListener(this);
     }
 
     /** Sets the callback for UI interactions. */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java
index 81904e2..e22c951 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java
@@ -49,6 +49,7 @@
 import com.android.wm.shell.pip.tv.TvPipTransition;
 import com.android.wm.shell.splitscreen.SplitScreenController;
 import com.android.wm.shell.sysui.ShellController;
+import com.android.wm.shell.sysui.ShellInit;
 import com.android.wm.shell.transition.Transitions;
 
 import java.util.Optional;
@@ -138,12 +139,14 @@
     @WMSingleton
     @Provides
     static PipTransitionController provideTvPipTransition(
-            Transitions transitions, ShellTaskOrganizer shellTaskOrganizer,
+            ShellInit shellInit,
+            ShellTaskOrganizer shellTaskOrganizer,
+            Transitions transitions,
             PipAnimationController pipAnimationController,
             TvPipBoundsAlgorithm tvPipBoundsAlgorithm,
             TvPipBoundsState tvPipBoundsState, TvPipMenuController pipMenuController) {
-        return new TvPipTransition(tvPipBoundsState, pipMenuController,
-                tvPipBoundsAlgorithm, pipAnimationController, transitions, shellTaskOrganizer);
+        return new TvPipTransition(shellInit, shellTaskOrganizer, transitions, tvPipBoundsState,
+                pipMenuController, tvPipBoundsAlgorithm, pipAnimationController);
     }
 
     @WMSingleton
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 3add417..bbaf51f 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
@@ -174,13 +174,14 @@
     @Provides
     static ShellTaskOrganizer provideShellTaskOrganizer(
             ShellInit shellInit,
+            ShellCommandHandler shellCommandHandler,
             CompatUIController compatUI,
             Optional<UnfoldAnimationController> unfoldAnimationController,
             Optional<RecentTasksController> recentTasksOptional,
             @ShellMainThread ShellExecutor mainExecutor
     ) {
-        return new ShellTaskOrganizer(shellInit, compatUI, unfoldAnimationController,
-                recentTasksOptional, mainExecutor);
+        return new ShellTaskOrganizer(shellInit, shellCommandHandler, compatUI,
+                unfoldAnimationController, recentTasksOptional, mainExecutor);
     }
 
     @WMSingleton
@@ -188,6 +189,7 @@
     static KidsModeTaskOrganizer provideKidsModeTaskOrganizer(
             Context context,
             ShellInit shellInit,
+            ShellCommandHandler shellCommandHandler,
             SyncTransactionQueue syncTransactionQueue,
             DisplayController displayController,
             DisplayInsetsController displayInsetsController,
@@ -196,19 +198,20 @@
             @ShellMainThread ShellExecutor mainExecutor,
             @ShellMainThread Handler mainHandler
     ) {
-        return new KidsModeTaskOrganizer(context, shellInit, syncTransactionQueue,
-                displayController, displayInsetsController, unfoldAnimationController,
-                recentTasksOptional, mainExecutor, mainHandler);
+        return new KidsModeTaskOrganizer(context, shellInit, shellCommandHandler,
+                syncTransactionQueue, displayController, displayInsetsController,
+                unfoldAnimationController, recentTasksOptional, mainExecutor, mainHandler);
     }
 
     @WMSingleton
     @Provides
     static CompatUIController provideCompatUIController(Context context,
+            ShellInit shellInit,
             ShellController shellController,
             DisplayController displayController, DisplayInsetsController displayInsetsController,
             DisplayImeController imeController, SyncTransactionQueue syncQueue,
             @ShellMainThread ShellExecutor mainExecutor, Lazy<Transitions> transitionsLazy) {
-        return new CompatUIController(context, shellController, displayController,
+        return new CompatUIController(context, shellInit, shellController, displayController,
                 displayInsetsController, imeController, syncQueue, mainExecutor, transitionsLazy);
     }
 
@@ -268,12 +271,14 @@
     @Provides
     static Optional<BackAnimationController> provideBackAnimationController(
             Context context,
+            ShellInit shellInit,
             @ShellMainThread ShellExecutor shellExecutor,
             @ShellBackgroundThread Handler backgroundHandler
     ) {
         if (BackAnimationController.IS_ENABLED) {
             return Optional.of(
-                    new BackAnimationController(shellExecutor, backgroundHandler, context));
+                    new BackAnimationController(shellInit, shellExecutor, backgroundHandler,
+                            context));
         }
         return Optional.empty();
     }
@@ -384,11 +389,14 @@
     @WMSingleton
     @Provides
     static Optional<HideDisplayCutoutController> provideHideDisplayCutoutController(Context context,
-            ShellController shellController, DisplayController displayController,
+            ShellInit shellInit,
+            ShellCommandHandler shellCommandHandler,
+            ShellController shellController,
+            DisplayController displayController,
             @ShellMainThread ShellExecutor mainExecutor) {
         return Optional.ofNullable(
-                HideDisplayCutoutController.create(context, shellController, displayController,
-                        mainExecutor));
+                HideDisplayCutoutController.create(context, shellInit, shellCommandHandler,
+                        shellController, displayController, mainExecutor));
     }
 
     //
@@ -466,12 +474,13 @@
     static Optional<RecentTasksController> provideRecentTasksController(
             Context context,
             ShellInit shellInit,
+            ShellCommandHandler shellCommandHandler,
             TaskStackListenerImpl taskStackListener,
             @ShellMainThread ShellExecutor mainExecutor
     ) {
         return Optional.ofNullable(
-                RecentTasksController.create(context, shellInit, taskStackListener,
-                        mainExecutor));
+                RecentTasksController.create(context, shellInit, shellCommandHandler,
+                        taskStackListener, mainExecutor));
     }
 
     //
@@ -649,8 +658,9 @@
     @WMSingleton
     @Provides
     static ShellController provideShellController(ShellInit shellInit,
+            ShellCommandHandler shellCommandHandler,
             @ShellMainThread ShellExecutor mainExecutor) {
-        return new ShellController(shellInit, mainExecutor);
+        return new ShellController(shellInit, shellCommandHandler, mainExecutor);
     }
 
     //
@@ -676,12 +686,15 @@
             KidsModeTaskOrganizer kidsModeTaskOrganizer,
             Optional<BubbleController> bubblesOptional,
             Optional<SplitScreenController> splitScreenOptional,
+            Optional<Pip> pipOptional,
             Optional<PipTouchHandler> pipTouchHandlerOptional,
             FullscreenTaskListener fullscreenTaskListener,
             Optional<UnfoldAnimationController> unfoldAnimationController,
             Optional<UnfoldTransitionHandler> unfoldTransitionHandler,
             Optional<FreeformComponents> freeformComponents,
             Optional<RecentTasksController> recentTasksOptional,
+            Optional<OneHandedController> oneHandedControllerOptional,
+            Optional<HideDisplayCutoutController> hideDisplayCutoutControllerOptional,
             ActivityEmbeddingController activityEmbeddingOptional,
             Transitions transitions,
             StartingWindowController startingWindow,
@@ -697,18 +710,7 @@
 
     @WMSingleton
     @Provides
-    static ShellCommandHandler provideShellCommandHandlerImpl(
-            ShellController shellController,
-            ShellTaskOrganizer shellTaskOrganizer,
-            KidsModeTaskOrganizer kidsModeTaskOrganizer,
-            Optional<SplitScreenController> splitScreenOptional,
-            Optional<Pip> pipOptional,
-            Optional<OneHandedController> oneHandedOptional,
-            Optional<HideDisplayCutoutController> hideDisplayCutout,
-            Optional<RecentTasksController> recentTasksOptional,
-            @ShellMainThread ShellExecutor mainExecutor) {
-        return new ShellCommandHandler(shellController, shellTaskOrganizer,
-                kidsModeTaskOrganizer, splitScreenOptional, pipOptional, oneHandedOptional,
-                hideDisplayCutout, recentTasksOptional, mainExecutor);
+    static ShellCommandHandler provideShellCommandHandler() {
+        return new ShellCommandHandler();
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index e2bf767..2ca9c3b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -74,6 +74,7 @@
 import com.android.wm.shell.pip.phone.PipTouchHandler;
 import com.android.wm.shell.recents.RecentTasksController;
 import com.android.wm.shell.splitscreen.SplitScreenController;
+import com.android.wm.shell.sysui.ShellCommandHandler;
 import com.android.wm.shell.sysui.ShellController;
 import com.android.wm.shell.sysui.ShellInit;
 import com.android.wm.shell.transition.SplitscreenPipMixedHandler;
@@ -248,14 +249,20 @@
     @Provides
     @DynamicOverride
     static OneHandedController provideOneHandedController(Context context,
+            ShellInit shellInit,
+            ShellCommandHandler shellCommandHandler,
             ShellController shellController,
-            WindowManager windowManager, DisplayController displayController,
-            DisplayLayout displayLayout, TaskStackListenerImpl taskStackListener,
-            UiEventLogger uiEventLogger, InteractionJankMonitor jankMonitor,
-            @ShellMainThread ShellExecutor mainExecutor, @ShellMainThread Handler mainHandler) {
-        return OneHandedController.create(context, shellController, windowManager,
-                displayController, displayLayout, taskStackListener, jankMonitor, uiEventLogger,
-                mainExecutor, mainHandler);
+            WindowManager windowManager,
+            DisplayController displayController,
+            DisplayLayout displayLayout,
+            TaskStackListenerImpl taskStackListener,
+            UiEventLogger uiEventLogger,
+            InteractionJankMonitor jankMonitor,
+            @ShellMainThread ShellExecutor mainExecutor,
+            @ShellMainThread Handler mainHandler) {
+        return OneHandedController.create(context, shellInit, shellCommandHandler, shellController,
+                windowManager, displayController, displayLayout, taskStackListener, jankMonitor,
+                uiEventLogger, mainExecutor, mainHandler);
     }
 
     //
@@ -268,6 +275,7 @@
     static SplitScreenController provideSplitScreenController(
             Context context,
             ShellInit shellInit,
+            ShellCommandHandler shellCommandHandler,
             ShellController shellController,
             ShellTaskOrganizer shellTaskOrganizer,
             SyncTransactionQueue syncQueue,
@@ -281,10 +289,10 @@
             IconProvider iconProvider,
             Optional<RecentTasksController> recentTasks,
             @ShellMainThread ShellExecutor mainExecutor) {
-        return new SplitScreenController(context, shellInit, shellController, shellTaskOrganizer,
-                syncQueue, rootTaskDisplayAreaOrganizer, displayController, displayImeController,
-                displayInsetsController, dragAndDropController, transitions, transactionPool,
-                iconProvider, recentTasks, mainExecutor);
+        return new SplitScreenController(context, shellInit, shellCommandHandler, shellController,
+                shellTaskOrganizer, syncQueue, rootTaskDisplayAreaOrganizer, displayController,
+                displayImeController, displayInsetsController, dragAndDropController, transitions,
+                transactionPool, iconProvider, recentTasks, mainExecutor);
     }
 
     //
@@ -294,24 +302,33 @@
     @WMSingleton
     @Provides
     static Optional<Pip> providePip(Context context,
-            ShellController shellController, DisplayController displayController,
-            PipAppOpsListener pipAppOpsListener, PipBoundsAlgorithm pipBoundsAlgorithm,
-            PipKeepClearAlgorithm pipKeepClearAlgorithm, PipBoundsState pipBoundsState,
-            PipMotionHelper pipMotionHelper, PipMediaController pipMediaController,
-            PhonePipMenuController phonePipMenuController, PipTaskOrganizer pipTaskOrganizer,
+            ShellInit shellInit,
+            ShellCommandHandler shellCommandHandler,
+            ShellController shellController,
+            DisplayController displayController,
+            PipAppOpsListener pipAppOpsListener,
+            PipBoundsAlgorithm pipBoundsAlgorithm,
+            PipKeepClearAlgorithm pipKeepClearAlgorithm,
+            PipBoundsState pipBoundsState,
+            PipMotionHelper pipMotionHelper,
+            PipMediaController pipMediaController,
+            PhonePipMenuController phonePipMenuController,
+            PipTaskOrganizer pipTaskOrganizer,
             PipTransitionState pipTransitionState,
-            PipTouchHandler pipTouchHandler, PipTransitionController pipTransitionController,
+            PipTouchHandler pipTouchHandler,
+            PipTransitionController pipTransitionController,
             WindowManagerShellWrapper windowManagerShellWrapper,
             TaskStackListenerImpl taskStackListener,
             PipParamsChangedForwarder pipParamsChangedForwarder,
             Optional<OneHandedController> oneHandedController,
             @ShellMainThread ShellExecutor mainExecutor) {
-        return Optional.ofNullable(PipController.create(context, shellController, displayController,
-                pipAppOpsListener, pipBoundsAlgorithm, pipKeepClearAlgorithm, pipBoundsState,
-                pipMotionHelper,
-                pipMediaController, phonePipMenuController, pipTaskOrganizer, pipTransitionState,
-                pipTouchHandler, pipTransitionController, windowManagerShellWrapper,
-                taskStackListener, pipParamsChangedForwarder, oneHandedController, mainExecutor));
+        return Optional.ofNullable(PipController.create(
+                context, shellInit, shellCommandHandler, shellController,
+                displayController, pipAppOpsListener, pipBoundsAlgorithm, pipKeepClearAlgorithm,
+                pipBoundsState, pipMotionHelper, pipMediaController, phonePipMenuController,
+                pipTaskOrganizer, pipTransitionState, pipTouchHandler, pipTransitionController,
+                windowManagerShellWrapper, taskStackListener, pipParamsChangedForwarder,
+                oneHandedController, mainExecutor));
     }
 
     @WMSingleton
@@ -409,15 +426,15 @@
     @WMSingleton
     @Provides
     static PipTransitionController providePipTransitionController(Context context,
-            Transitions transitions, ShellTaskOrganizer shellTaskOrganizer,
+            ShellInit shellInit, ShellTaskOrganizer shellTaskOrganizer, Transitions transitions,
             PipAnimationController pipAnimationController, PipBoundsAlgorithm pipBoundsAlgorithm,
             PipBoundsState pipBoundsState, PipTransitionState pipTransitionState,
             PhonePipMenuController pipMenuController,
             PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
             Optional<SplitScreenController> splitScreenOptional) {
-        return new PipTransition(context, pipBoundsState, pipTransitionState, pipMenuController,
-                pipBoundsAlgorithm, pipAnimationController, transitions, shellTaskOrganizer,
-                pipSurfaceTransactionHelper, splitScreenOptional);
+        return new PipTransition(context, shellInit, shellTaskOrganizer, transitions,
+                pipBoundsState, pipTransitionState, pipMenuController, pipBoundsAlgorithm,
+                pipAnimationController, pipSurfaceTransactionHelper, splitScreenOptional);
     }
 
     @WMSingleton
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/docs/debugging.md b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/debugging.md
index 52f0c42..99922fb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/docs/debugging.md
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/debugging.md
@@ -59,9 +59,9 @@
 adb shell dumpsys activity service SystemUIService WMShell
 ```
 
-If information should be added to the dump, make updates to:
-- `WMShell` if you are dumping SysUI state
-- `ShellCommandHandler` if you are dumping Shell state
+If information should be added to the dump, either:
+- Update `WMShell` if you are dumping SysUI state
+- Inject `ShellCommandHandler` into your Shell class, and add a dump callback
 
 ## Debugging in Android Studio
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/docs/sysui.md b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/sysui.md
index 0dd50b1..d6302e6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/docs/sysui.md
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/sysui.md
@@ -32,7 +32,7 @@
 
 More detail can be found in [go/wm-sysui-dagger](http://go/wm-sysui-dagger).
 
-## Interfaces to Shell components
+## Interfaces from SysUI to Shell components
 
 Within the same process, the WM Shell components can be running on a different thread than the main
 SysUI thread (disabled on certain products).  This introduces challenges where we have to be
@@ -54,12 +54,30 @@
 Adding an interface to a Shell component may seem like a lot of boiler plate, but is currently
 necessary to maintain proper threading and logic isolation.
 
-## Configuration changes & other SysUI events
+## Listening for Configuration changes & other SysUI events
 
-Aside from direct calls into Shell controllers for exposed features, the Shell also receives 
+Aside from direct calls into Shell controllers for exposed features, the Shell also receives
 common event callbacks from SysUI via the `ShellController`.  This includes things like:
 
 - Configuration changes
-- TODO: Shell init
-- TODO: Shell command
-- TODO: Keyguard events
\ No newline at end of file
+- Keyguard events
+- Shell init
+- Shell dumps & commands
+
+For other events which are specific to the Shell feature, then you can add callback methods on
+the Shell feature interface.  Any such calls should <u>**never**</u> be synchronous calls as
+they will need to post to the Shell main thread to run.
+
+## Shell commands & Dumps
+
+Since the Shell library is a part of the SysUI process, it relies on SysUI to trigger commands
+on individual Shell components, or to dump individual shell components.
+
+```shell
+# Dump everything
+adb shell dumpsys activity service SystemUIService WMShell
+
+# Run a specific command
+adb shell dumpsys activity service SystemUIService WMShell help
+adb shell dumpsys activity service SystemUIService WMShell <cmd> <args> ...
+```
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java
index 20d7725..af205ed 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java
@@ -122,7 +122,6 @@
                     break;
                 case WindowManager.TRANSIT_TO_BACK:
                 case WindowManager.TRANSIT_TO_FRONT:
-                    transitionHandled = true;
                     break;
             }
         }
@@ -147,7 +146,9 @@
             return false;
         }
         mFreeformTaskListener.createWindowDecoration(change, startT, finishT);
-        return true;
+
+        // Intercepted transition to manage the window decorations. Let other handlers animate.
+        return false;
     }
 
     private boolean startCloseTransition(
@@ -164,7 +165,8 @@
             windowDecors.add(windowDecor);
         }
 
-        return true;
+        // Intercepted transition to manage the window decorations. Let other handlers animate.
+        return false;
     }
 
     private boolean startChangeTransition(
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutController.java
index 665b035..32125fa 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutController.java
@@ -27,7 +27,9 @@
 import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.sysui.ConfigurationChangeListener;
+import com.android.wm.shell.sysui.ShellCommandHandler;
 import com.android.wm.shell.sysui.ShellController;
+import com.android.wm.shell.sysui.ShellInit;
 
 import java.io.PrintWriter;
 
@@ -38,6 +40,7 @@
     private static final String TAG = "HideDisplayCutoutController";
 
     private final Context mContext;
+    private final ShellCommandHandler mShellCommandHandler;
     private final ShellController mShellController;
     private final HideDisplayCutoutOrganizer mOrganizer;
     @VisibleForTesting
@@ -49,7 +52,10 @@
      */
     @Nullable
     public static HideDisplayCutoutController create(Context context,
-            ShellController shellController, DisplayController displayController,
+            ShellInit shellInit,
+            ShellCommandHandler shellCommandHandler,
+            ShellController shellController,
+            DisplayController displayController,
             ShellExecutor mainExecutor) {
         // The SystemProperty is set for devices that support this feature and is used to control
         // whether to create the HideDisplayCutout instance.
@@ -60,14 +66,24 @@
 
         HideDisplayCutoutOrganizer organizer =
                 new HideDisplayCutoutOrganizer(context, displayController, mainExecutor);
-        return new HideDisplayCutoutController(context, shellController, organizer);
+        return new HideDisplayCutoutController(context, shellInit, shellCommandHandler,
+                shellController, organizer);
     }
 
-    HideDisplayCutoutController(Context context, ShellController shellController,
+    HideDisplayCutoutController(Context context,
+            ShellInit shellInit,
+            ShellCommandHandler shellCommandHandler,
+            ShellController shellController,
             HideDisplayCutoutOrganizer organizer) {
         mContext = context;
+        mShellCommandHandler = shellCommandHandler;
         mShellController = shellController;
         mOrganizer = organizer;
+        shellInit.addInitCallback(this::onInit, this);
+    }
+
+    private void onInit() {
+        mShellCommandHandler.addDumpCallback(this::dump, this);
         updateStatus();
         mShellController.addConfigurationChangeListener(this);
     }
@@ -96,11 +112,11 @@
         updateStatus();
     }
 
-    public void dump(@NonNull PrintWriter pw) {
-        final String prefix = "  ";
+    public void dump(@NonNull PrintWriter pw, String prefix) {
+        final String innerPrefix = "  ";
         pw.print(TAG);
         pw.println(" states: ");
-        pw.print(prefix);
+        pw.print(innerPrefix);
         pw.print("mEnabled=");
         pw.println(mEnabled);
         mOrganizer.dump(pw);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizer.java
index 73b9b89..2fdd121 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizer.java
@@ -50,6 +50,7 @@
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.SyncTransactionQueue;
 import com.android.wm.shell.recents.RecentTasksController;
+import com.android.wm.shell.sysui.ShellCommandHandler;
 import com.android.wm.shell.sysui.ShellInit;
 import com.android.wm.shell.unfold.UnfoldAnimationController;
 
@@ -73,6 +74,7 @@
 
     private final Handler mMainHandler;
     private final Context mContext;
+    private final ShellCommandHandler mShellCommandHandler;
     private final SyncTransactionQueue mSyncQueue;
     private final DisplayController mDisplayController;
     private final DisplayInsetsController mDisplayInsetsController;
@@ -141,6 +143,8 @@
     @VisibleForTesting
     KidsModeTaskOrganizer(
             Context context,
+            ShellInit shellInit,
+            ShellCommandHandler shellCommandHandler,
             ITaskOrganizerController taskOrganizerController,
             SyncTransactionQueue syncTransactionQueue,
             DisplayController displayController,
@@ -150,19 +154,23 @@
             KidsModeSettingsObserver kidsModeSettingsObserver,
             ShellExecutor mainExecutor,
             Handler mainHandler) {
-        super(/* shellInit= */ null, taskOrganizerController, /* compatUI= */ null,
-                unfoldAnimationController, recentTasks, mainExecutor);
+        // Note: we don't call super with the shell init because we will be initializing manually
+        super(/* shellInit= */ null, /* shellCommandHandler= */ null, taskOrganizerController,
+                /* compatUI= */ null, unfoldAnimationController, recentTasks, mainExecutor);
         mContext = context;
+        mShellCommandHandler = shellCommandHandler;
         mMainHandler = mainHandler;
         mSyncQueue = syncTransactionQueue;
         mDisplayController = displayController;
         mDisplayInsetsController = displayInsetsController;
         mKidsModeSettingsObserver = kidsModeSettingsObserver;
+        shellInit.addInitCallback(this::onInit, this);
     }
 
     public KidsModeTaskOrganizer(
             Context context,
             ShellInit shellInit,
+            ShellCommandHandler shellCommandHandler,
             SyncTransactionQueue syncTransactionQueue,
             DisplayController displayController,
             DisplayInsetsController displayInsetsController,
@@ -171,9 +179,10 @@
             ShellExecutor mainExecutor,
             Handler mainHandler) {
         // Note: we don't call super with the shell init because we will be initializing manually
-        super(/* shellInit= */ null, /* compatUI= */ null, unfoldAnimationController, recentTasks,
-                mainExecutor);
+        super(/* shellInit= */ null, /* taskOrganizerController= */ null, /* compatUI= */ null,
+                unfoldAnimationController, recentTasks, mainExecutor);
         mContext = context;
+        mShellCommandHandler = shellCommandHandler;
         mMainHandler = mainHandler;
         mSyncQueue = syncTransactionQueue;
         mDisplayController = displayController;
@@ -185,6 +194,9 @@
      * Initializes kids mode status.
      */
     public void onInit() {
+        if (mShellCommandHandler != null) {
+            mShellCommandHandler.addDumpCallback(this::dump, this);
+        }
         if (mKidsModeSettingsObserver == null) {
             mKidsModeSettingsObserver = new KidsModeSettingsObserver(mMainHandler, mContext);
         }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
index 24f02ac..9149204 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
@@ -56,7 +56,9 @@
 import com.android.wm.shell.common.annotations.ExternalThread;
 import com.android.wm.shell.sysui.ConfigurationChangeListener;
 import com.android.wm.shell.sysui.KeyguardChangeListener;
+import com.android.wm.shell.sysui.ShellCommandHandler;
 import com.android.wm.shell.sysui.ShellController;
+import com.android.wm.shell.sysui.ShellInit;
 
 import java.io.PrintWriter;
 
@@ -85,6 +87,7 @@
 
     private Context mContext;
 
+    private final ShellCommandHandler mShellCommandHandler;
     private final ShellController mShellController;
     private final AccessibilityManager mAccessibilityManager;
     private final DisplayController mDisplayController;
@@ -192,8 +195,9 @@
     /**
      * Creates {@link OneHandedController}, returns {@code null} if the feature is not supported.
      */
-    public static OneHandedController create(
-            Context context, ShellController shellController, WindowManager windowManager,
+    public static OneHandedController create(Context context,
+            ShellInit shellInit, ShellCommandHandler shellCommandHandler,
+            ShellController shellController, WindowManager windowManager,
             DisplayController displayController, DisplayLayout displayLayout,
             TaskStackListenerImpl taskStackListener,
             InteractionJankMonitor jankMonitor, UiEventLogger uiEventLogger,
@@ -213,14 +217,16 @@
                 context, displayLayout, settingsUtil, animationController, tutorialHandler,
                 jankMonitor, mainExecutor);
         OneHandedUiEventLogger oneHandedUiEventsLogger = new OneHandedUiEventLogger(uiEventLogger);
-        return new OneHandedController(context, shellController, displayController, organizer,
-                touchHandler, tutorialHandler, settingsUtil, accessibilityUtil, timeoutHandler,
-                oneHandedState, oneHandedUiEventsLogger, taskStackListener,
-                mainExecutor, mainHandler);
+        return new OneHandedController(context, shellInit, shellCommandHandler, shellController,
+                displayController, organizer, touchHandler, tutorialHandler, settingsUtil,
+                accessibilityUtil, timeoutHandler, oneHandedState, oneHandedUiEventsLogger,
+                taskStackListener, mainExecutor, mainHandler);
     }
 
     @VisibleForTesting
     OneHandedController(Context context,
+            ShellInit shellInit,
+            ShellCommandHandler shellCommandHandler,
             ShellController shellController,
             DisplayController displayController,
             OneHandedDisplayAreaOrganizer displayAreaOrganizer,
@@ -235,6 +241,7 @@
             ShellExecutor mainExecutor,
             Handler mainHandler) {
         mContext = context;
+        mShellCommandHandler = shellCommandHandler;
         mShellController = shellController;
         mOneHandedSettingsUtil = settingsUtil;
         mOneHandedAccessibilityUtil = oneHandedAccessibilityUtil;
@@ -247,8 +254,8 @@
         mMainHandler = mainHandler;
         mOneHandedUiEventLogger = uiEventsLogger;
         mTaskStackListener = taskStackListener;
+        mAccessibilityManager = AccessibilityManager.getInstance(mContext);
 
-        mDisplayController.addDisplayWindowListener(mDisplaysChangedListener);
         final float offsetPercentageConfig = context.getResources().getFraction(
                 R.fraction.config_one_handed_offset, 1, 1);
         final int sysPropPercentageConfig = SystemProperties.getInt(
@@ -268,6 +275,12 @@
                 getObserver(this::onSwipeToNotificationEnabledChanged);
         mShortcutEnabledObserver = getObserver(this::onShortcutEnabledChanged);
 
+        shellInit.addInitCallback(this::onInit, this);
+    }
+
+    private void onInit() {
+        mShellCommandHandler.addDumpCallback(this::dump, this);
+        mDisplayController.addDisplayWindowListener(mDisplaysChangedListener);
         mDisplayController.addDisplayChangingController(this);
         setupCallback();
         registerSettingObservers(mUserId);
@@ -275,7 +288,6 @@
         updateSettings();
         updateDisplayLayout(mContext.getDisplayId());
 
-        mAccessibilityManager = AccessibilityManager.getInstance(context);
         mAccessibilityManager.addAccessibilityStateChangeListener(
                 mAccessibilityStateChangeListener);
 
@@ -623,7 +635,7 @@
         updateOneHandedEnabled();
     }
 
-    public void dump(@NonNull PrintWriter pw) {
+    public void dump(@NonNull PrintWriter pw, String prefix) {
         final String innerPrefix = "  ";
         pw.println();
         pw.println(TAG);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java
index 38631ce..93172f8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java
@@ -20,7 +20,6 @@
 
 import com.android.wm.shell.common.annotations.ExternalThread;
 
-import java.io.PrintWriter;
 import java.util.function.Consumer;
 
 /**
@@ -99,12 +98,4 @@
      * view hierarchy or destroyed.
      */
     default void removePipExclusionBoundsChangeListener(Consumer<Rect> listener) { }
-
-    /**
-     * Dump the current state and information if need.
-     *
-     * @param pw The stream to dump information to.
-     */
-    default void dump(PrintWriter pw) {
-    }
 }
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 cf2734c..81e49f8 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
@@ -183,7 +183,7 @@
         return mCurrentAnimator;
     }
 
-    PipTransitionAnimator getCurrentAnimator() {
+    public PipTransitionAnimator getCurrentAnimator() {
         return mCurrentAnimator;
     }
 
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 1155ea1..f747b5e 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
@@ -127,7 +127,7 @@
     private final PipBoundsAlgorithm mPipBoundsAlgorithm;
     private final @NonNull PipMenuController mPipMenuController;
     private final PipAnimationController mPipAnimationController;
-    private final PipTransitionController mPipTransitionController;
+    protected final PipTransitionController mPipTransitionController;
     protected final PipParamsChangedForwarder mPipParamsChangedForwarder;
     private final PipUiEventLogger mPipUiEventLoggerLogger;
     private final int mEnterAnimationDuration;
@@ -196,6 +196,26 @@
         }
     };
 
+    @VisibleForTesting
+    final PipTransitionController.PipTransitionCallback mPipTransitionCallback =
+            new PipTransitionController.PipTransitionCallback() {
+                @Override
+                public void onPipTransitionStarted(int direction, Rect pipBounds) {}
+
+                @Override
+                public void onPipTransitionFinished(int direction) {
+                    // Apply the deferred RunningTaskInfo if applicable after all proper callbacks
+                    // are sent.
+                    if (direction == TRANSITION_DIRECTION_TO_PIP && mDeferredTaskInfo != null) {
+                        onTaskInfoChanged(mDeferredTaskInfo);
+                        mDeferredTaskInfo = null;
+                    }
+                }
+
+                @Override
+                public void onPipTransitionCanceled(int direction) {}
+            };
+
     private final PipAnimationController.PipTransactionHandler mPipTransactionHandler =
             new PipAnimationController.PipTransactionHandler() {
                 @Override
@@ -216,7 +236,7 @@
     private ActivityManager.RunningTaskInfo mDeferredTaskInfo;
     private WindowContainerToken mToken;
     private SurfaceControl mLeash;
-    private PipTransitionState mPipTransitionState;
+    protected PipTransitionState mPipTransitionState;
     private @PipAnimationController.AnimationType int mOneShotAnimationType = ANIM_TYPE_BOUNDS;
     private long mLastOneShotAlphaAnimationTime;
     private PipSurfaceTransactionHelper.SurfaceControlTransactionFactory
@@ -296,6 +316,7 @@
         mTaskOrganizer.addFocusListener(this);
         mPipTransitionController.setPipOrganizer(this);
         displayController.addDisplayWindowListener(this);
+        pipTransitionController.registerPipTransitionCallback(mPipTransitionCallback);
     }
 
     public PipTransitionController getTransitionController() {
@@ -773,11 +794,6 @@
             mPipTransitionState.setTransitionState(PipTransitionState.ENTERED_PIP);
         }
         mPipTransitionController.sendOnPipTransitionFinished(direction);
-        // Apply the deferred RunningTaskInfo if applicable after all proper callbacks are sent.
-        if (direction == TRANSITION_DIRECTION_TO_PIP && mDeferredTaskInfo != null) {
-            onTaskInfoChanged(mDeferredTaskInfo);
-            mDeferredTaskInfo = null;
-        }
     }
 
     private void sendOnPipTransitionCancelled(
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 51be2a5..33761d2 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
@@ -66,6 +66,7 @@
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
 import com.android.wm.shell.splitscreen.SplitScreenController;
+import com.android.wm.shell.sysui.ShellInit;
 import com.android.wm.shell.transition.CounterRotatorHelper;
 import com.android.wm.shell.transition.Transitions;
 
@@ -107,17 +108,18 @@
     private boolean mHasFadeOut;
 
     public PipTransition(Context context,
+            @NonNull ShellInit shellInit,
+            @NonNull ShellTaskOrganizer shellTaskOrganizer,
+            @NonNull Transitions transitions,
             PipBoundsState pipBoundsState,
             PipTransitionState pipTransitionState,
             PipMenuController pipMenuController,
             PipBoundsAlgorithm pipBoundsAlgorithm,
             PipAnimationController pipAnimationController,
-            Transitions transitions,
-            @NonNull ShellTaskOrganizer shellTaskOrganizer,
             PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
             Optional<SplitScreenController> splitScreenOptional) {
-        super(pipBoundsState, pipMenuController, pipBoundsAlgorithm,
-                pipAnimationController, transitions, shellTaskOrganizer);
+        super(shellInit, shellTaskOrganizer, transitions, pipBoundsState, pipMenuController,
+                pipBoundsAlgorithm, pipAnimationController);
         mContext = context;
         mPipTransitionState = pipTransitionState;
         mEnterExitAnimationDuration = context.getResources()
@@ -315,7 +317,8 @@
     }
 
     @Override
-    public void onTransitionConsumed(@NonNull IBinder transition, boolean aborted) {
+    public void onTransitionConsumed(@NonNull IBinder transition, boolean aborted,
+            @Nullable SurfaceControl.Transaction finishT) {
         if (transition != mExitTransition) {
             return;
         }
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 90a2695..f51e247 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
@@ -38,6 +38,7 @@
 import androidx.annotation.NonNull;
 
 import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.sysui.ShellInit;
 import com.android.wm.shell.transition.Transitions;
 
 import java.util.ArrayList;
@@ -131,10 +132,13 @@
     public void onFixedRotationStarted() {
     }
 
-    public PipTransitionController(PipBoundsState pipBoundsState,
+    public PipTransitionController(
+            @NonNull ShellInit shellInit,
+            @NonNull ShellTaskOrganizer shellTaskOrganizer,
+            @NonNull Transitions transitions,
+            PipBoundsState pipBoundsState,
             PipMenuController pipMenuController, PipBoundsAlgorithm pipBoundsAlgorithm,
-            PipAnimationController pipAnimationController, Transitions transitions,
-            @android.annotation.NonNull ShellTaskOrganizer shellTaskOrganizer) {
+            PipAnimationController pipAnimationController) {
         mPipBoundsState = pipBoundsState;
         mPipMenuController = pipMenuController;
         mShellTaskOrganizer = shellTaskOrganizer;
@@ -142,10 +146,14 @@
         mPipAnimationController = pipAnimationController;
         mTransitions = transitions;
         if (Transitions.ENABLE_SHELL_TRANSITIONS) {
-            transitions.addHandler(this);
+            shellInit.addInitCallback(this::onInit, this);
         }
     }
 
+    private void onInit() {
+        mTransitions.addHandler(this);
+    }
+
     void setPipOrganizer(PipTaskOrganizer pto) {
         mPipOrganizer = pto;
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index 586e3a0..fc97f31 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -89,7 +89,9 @@
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
 import com.android.wm.shell.sysui.ConfigurationChangeListener;
 import com.android.wm.shell.sysui.KeyguardChangeListener;
+import com.android.wm.shell.sysui.ShellCommandHandler;
 import com.android.wm.shell.sysui.ShellController;
+import com.android.wm.shell.sysui.ShellInit;
 import com.android.wm.shell.transition.Transitions;
 
 import java.io.PrintWriter;
@@ -122,6 +124,7 @@
     private TaskStackListenerImpl mTaskStackListener;
     private PipParamsChangedForwarder mPipParamsChangedForwarder;
     private Optional<OneHandedController> mOneHandedController;
+    private final ShellCommandHandler mShellCommandHandler;
     private final ShellController mShellController;
     protected final PipImpl mImpl;
 
@@ -295,6 +298,8 @@
      */
     @Nullable
     public static Pip create(Context context,
+            ShellInit shellInit,
+            ShellCommandHandler shellCommandHandler,
             ShellController shellController,
             DisplayController displayController,
             PipAppOpsListener pipAppOpsListener,
@@ -319,16 +324,18 @@
             return null;
         }
 
-        return new PipController(context, shellController, displayController, pipAppOpsListener,
-                pipBoundsAlgorithm, pipKeepClearAlgorithm, pipBoundsState, pipMotionHelper,
-                pipMediaController, phonePipMenuController, pipTaskOrganizer, pipTransitionState,
-                pipTouchHandler, pipTransitionController,
+        return new PipController(context, shellInit, shellCommandHandler, shellController,
+                displayController, pipAppOpsListener, pipBoundsAlgorithm, pipKeepClearAlgorithm,
+                pipBoundsState, pipMotionHelper, pipMediaController, phonePipMenuController,
+                pipTaskOrganizer, pipTransitionState, pipTouchHandler, pipTransitionController,
                 windowManagerShellWrapper, taskStackListener, pipParamsChangedForwarder,
                 oneHandedController, mainExecutor)
                 .mImpl;
     }
 
     protected PipController(Context context,
+            ShellInit shellInit,
+            ShellCommandHandler shellCommandHandler,
             ShellController shellController,
             DisplayController displayController,
             PipAppOpsListener pipAppOpsListener,
@@ -355,6 +362,7 @@
         }
 
         mContext = context;
+        mShellCommandHandler = shellCommandHandler;
         mShellController = shellController;
         mImpl = new PipImpl();
         mWindowManagerShellWrapper = windowManagerShellWrapper;
@@ -378,11 +386,11 @@
                 .getInteger(R.integer.config_pipEnterAnimationDuration);
         mPipParamsChangedForwarder = pipParamsChangedForwarder;
 
-        //TODO: move this to ShellInit when PipController can be injected
-        mMainExecutor.execute(this::init);
+        shellInit.addInitCallback(this::onInit, this);
     }
 
-    public void init() {
+    private void onInit() {
+        mShellCommandHandler.addDumpCallback(this::dump, this);
         mPipInputConsumer = new PipInputConsumer(WindowManagerGlobal.getWindowManagerService(),
                 INPUT_CONSUMER_PIP, mMainExecutor);
         mPipTransitionController.registerPipTransitionCallback(this);
@@ -503,6 +511,12 @@
                             updateMovementBounds(null /* toBounds */, false /* fromRotation */,
                                     false /* fromImeAdjustment */, false /* fromShelfAdjustment */,
                                     null /* windowContainerTransaction */);
+                        } else {
+                            // when we enter pip for the first time, the destination bounds and pip
+                            // bounds will already match, since they are calculated prior to
+                            // starting the animation, so we only need to update the min/max size
+                            // that is used for e.g. double tap to maximized state
+                            mTouchHandler.updateMinMaxSize(ratio);
                         }
                     }
 
@@ -622,7 +636,8 @@
                 mPipTaskOrganizer.scheduleAnimateResizePip(
                         postChangeBounds, duration, null /* updateBoundsCallback */);
             } else {
-                mTouchHandler.getMotionHelper().movePip(postChangeBounds);
+                // Directly move PiP to its final destination bounds without animation.
+                mPipTaskOrganizer.scheduleFinishResizePip(postChangeBounds);
             }
         } else {
             updateDisplayLayout.run();
@@ -912,7 +927,7 @@
         return true;
     }
 
-    private void dump(PrintWriter pw) {
+    private void dump(PrintWriter pw, String prefix) {
         final String innerPrefix = "  ";
         pw.println(TAG);
         mMenuController.dump(pw, innerPrefix);
@@ -1000,18 +1015,6 @@
                 PipController.this.showPictureInPictureMenu();
             });
         }
-
-        @Override
-        public void dump(PrintWriter pw) {
-            try {
-                mMainExecutor.executeBlocking(() -> {
-                    PipController.this.dump(pw);
-                });
-            } catch (InterruptedException e) {
-                ProtoLog.e(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
-                        "%s: Failed to dump PipController in 2s", TAG);
-            }
-        }
     }
 
     /**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
index c86c136..a2fa058 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
@@ -412,13 +412,7 @@
                 mPipBoundsState.getExpandedBounds(), insetBounds, expandedMovementBounds,
                 bottomOffset);
 
-        if (mPipResizeGestureHandler.isUsingPinchToZoom()) {
-            updatePinchResizeSizeConstraints(insetBounds, normalBounds, aspectRatio);
-        } else {
-            mPipResizeGestureHandler.updateMinSize(normalBounds.width(), normalBounds.height());
-            mPipResizeGestureHandler.updateMaxSize(mPipBoundsState.getExpandedBounds().width(),
-                    mPipBoundsState.getExpandedBounds().height());
-        }
+        updatePipSizeConstraints(insetBounds, normalBounds, aspectRatio);
 
         // The extra offset does not really affect the movement bounds, but are applied based on the
         // current state (ime showing, or shelf offset) when we need to actually shift
@@ -487,6 +481,27 @@
         }
     }
 
+    /**
+     * Update the values for min/max allowed size of picture in picture window based on the aspect
+     * ratio.
+     * @param aspectRatio aspect ratio to use for the calculation of min/max size
+     */
+    public void updateMinMaxSize(float aspectRatio) {
+        updatePipSizeConstraints(mInsetBounds, mPipBoundsState.getNormalBounds(),
+                aspectRatio);
+    }
+
+    private void updatePipSizeConstraints(Rect insetBounds, Rect normalBounds,
+            float aspectRatio) {
+        if (mPipResizeGestureHandler.isUsingPinchToZoom()) {
+            updatePinchResizeSizeConstraints(insetBounds, normalBounds, aspectRatio);
+        } else {
+            mPipResizeGestureHandler.updateMinSize(normalBounds.width(), normalBounds.height());
+            mPipResizeGestureHandler.updateMaxSize(mPipBoundsState.getExpandedBounds().width(),
+                    mPipBoundsState.getExpandedBounds().height());
+        }
+    }
+
     private void updatePinchResizeSizeConstraints(Rect insetBounds, Rect normalBounds,
             float aspectRatio) {
         final int shorterLength = Math.min(mPipBoundsState.getDisplayBounds().width(),
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsController.java
index 3a6ce81..b212ea9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsController.java
@@ -122,9 +122,9 @@
             cancelScheduledPlacement();
             applyPlacementBounds(placement.getUnstashedBounds(), animationDuration);
         } else if (immediate) {
+            boolean shouldStash = mUnstashRunnable != null || placement.getTriggerStash();
             cancelScheduledPlacement();
-            applyPlacementBounds(placement.getBounds(), animationDuration);
-            scheduleUnstashIfNeeded(placement);
+            applyPlacement(placement, shouldStash, animationDuration);
         } else {
             applyPlacementBounds(mCurrentPlacementBounds, animationDuration);
             schedulePinnedStackPlacement(placement, animationDuration);
@@ -176,22 +176,21 @@
                     "%s: applyPendingPlacement()", TAG);
         }
         if (mPendingPlacement != null) {
-            if (mPendingStash) {
-                mPendingStash = false;
-                scheduleUnstashIfNeeded(mPendingPlacement);
-            }
+            applyPlacement(mPendingPlacement, mPendingStash, mPendingPlacementAnimationDuration);
+            mPendingStash = false;
+            mPendingPlacement = null;
+        }
+    }
 
-            if (mUnstashRunnable != null) {
-                // currently stashed, use stashed pos
-                applyPlacementBounds(mPendingPlacement.getBounds(),
-                        mPendingPlacementAnimationDuration);
-            } else {
-                applyPlacementBounds(mPendingPlacement.getUnstashedBounds(),
-                        mPendingPlacementAnimationDuration);
-            }
+    private void applyPlacement(@NonNull final Placement placement, boolean shouldStash,
+            int animationDuration) {
+        if (placement.getStashType() != STASH_TYPE_NONE && shouldStash) {
+            scheduleUnstashIfNeeded(placement);
         }
 
-        mPendingPlacement = null;
+        Rect bounds =
+                mUnstashRunnable != null ? placement.getBounds() : placement.getUnstashedBounds();
+        applyPlacementBounds(bounds, animationDuration);
     }
 
     void onPipDismissed() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java
index 5062cc4..8ebcf63 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java
@@ -32,6 +32,7 @@
 import com.android.wm.shell.pip.PipBoundsState;
 import com.android.wm.shell.pip.PipMenuController;
 import com.android.wm.shell.pip.PipTransitionController;
+import com.android.wm.shell.sysui.ShellInit;
 import com.android.wm.shell.transition.Transitions;
 
 /**
@@ -39,14 +40,16 @@
  * TODO: Implement animation once TV is using Transitions.
  */
 public class TvPipTransition extends PipTransitionController {
-    public TvPipTransition(PipBoundsState pipBoundsState,
+    public TvPipTransition(
+            @NonNull ShellInit shellInit,
+            @NonNull ShellTaskOrganizer shellTaskOrganizer,
+            @NonNull Transitions transitions,
+            PipBoundsState pipBoundsState,
             PipMenuController pipMenuController,
             TvPipBoundsAlgorithm tvPipBoundsAlgorithm,
-            PipAnimationController pipAnimationController,
-            Transitions transitions,
-            @NonNull ShellTaskOrganizer shellTaskOrganizer) {
-        super(pipBoundsState, pipMenuController, tvPipBoundsAlgorithm, pipAnimationController,
-                transitions, shellTaskOrganizer);
+            PipAnimationController pipAnimationController) {
+        super(shellInit, shellTaskOrganizer, transitions, pipBoundsState, pipMenuController,
+                tvPipBoundsAlgorithm, pipAnimationController);
     }
 
     @Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
index 3d1a7e9..7b42350 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
@@ -44,6 +44,7 @@
 import com.android.wm.shell.common.annotations.ExternalThread;
 import com.android.wm.shell.common.annotations.ShellMainThread;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
+import com.android.wm.shell.sysui.ShellCommandHandler;
 import com.android.wm.shell.sysui.ShellInit;
 import com.android.wm.shell.util.GroupedRecentTaskInfo;
 import com.android.wm.shell.util.SplitBounds;
@@ -62,6 +63,7 @@
     private static final String TAG = RecentTasksController.class.getSimpleName();
 
     private final Context mContext;
+    private final ShellCommandHandler mShellCommandHandler;
     private final ShellExecutor mMainExecutor;
     private final TaskStackListenerImpl mTaskStackListener;
     private final RecentTasks mImpl = new RecentTasksImpl();
@@ -87,20 +89,24 @@
     public static RecentTasksController create(
             Context context,
             ShellInit shellInit,
+            ShellCommandHandler shellCommandHandler,
             TaskStackListenerImpl taskStackListener,
             @ShellMainThread ShellExecutor mainExecutor
     ) {
         if (!context.getResources().getBoolean(com.android.internal.R.bool.config_hasRecents)) {
             return null;
         }
-        return new RecentTasksController(context, shellInit, taskStackListener, mainExecutor);
+        return new RecentTasksController(context, shellInit, shellCommandHandler, taskStackListener,
+                mainExecutor);
     }
 
     RecentTasksController(Context context,
             ShellInit shellInit,
+            ShellCommandHandler shellCommandHandler,
             TaskStackListenerImpl taskStackListener,
             ShellExecutor mainExecutor) {
         mContext = context;
+        mShellCommandHandler = shellCommandHandler;
         mIsDesktopMode = mContext.getPackageManager().hasSystemFeature(FEATURE_PC);
         mTaskStackListener = taskStackListener;
         mMainExecutor = mainExecutor;
@@ -112,6 +118,7 @@
     }
 
     private void onInit() {
+        mShellCommandHandler.addDumpCallback(this::dump, this);
         mTaskStackListener.addListener(this);
     }
 
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 de7e7bd..2117b69 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
@@ -21,6 +21,7 @@
 import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
 import static android.content.Intent.FLAG_ACTIVITY_NO_USER_ACTION;
 import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.RemoteAnimationTarget.MODE_OPENING;
 
 import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission;
 import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
@@ -83,6 +84,7 @@
 import com.android.wm.shell.recents.RecentTasksController;
 import com.android.wm.shell.splitscreen.SplitScreen.StageType;
 import com.android.wm.shell.sysui.KeyguardChangeListener;
+import com.android.wm.shell.sysui.ShellCommandHandler;
 import com.android.wm.shell.sysui.ShellController;
 import com.android.wm.shell.sysui.ShellInit;
 import com.android.wm.shell.transition.Transitions;
@@ -90,7 +92,6 @@
 import java.io.PrintWriter;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-import java.util.Arrays;
 import java.util.Objects;
 import java.util.Optional;
 import java.util.concurrent.Executor;
@@ -131,6 +132,7 @@
     @Retention(RetentionPolicy.SOURCE)
     @interface ExitReason{}
 
+    private final ShellCommandHandler mShellCommandHandler;
     private final ShellController mShellController;
     private final ShellTaskOrganizer mTaskOrganizer;
     private final SyncTransactionQueue mSyncQueue;
@@ -147,6 +149,7 @@
     private final SplitscreenEventLogger mLogger;
     private final IconProvider mIconProvider;
     private final Optional<RecentTasksController> mRecentTasksOptional;
+    private final SplitScreenShellCommandHandler mSplitScreenShellCommandHandler;
 
     private StageCoordinator mStageCoordinator;
     // Only used for the legacy recents animation from splitscreen to allow the tasks to be animated
@@ -155,6 +158,7 @@
 
     public SplitScreenController(Context context,
             ShellInit shellInit,
+            ShellCommandHandler shellCommandHandler,
             ShellController shellController,
             ShellTaskOrganizer shellTaskOrganizer,
             SyncTransactionQueue syncQueue,
@@ -168,6 +172,7 @@
             IconProvider iconProvider,
             Optional<RecentTasksController> recentTasks,
             ShellExecutor mainExecutor) {
+        mShellCommandHandler = shellCommandHandler;
         mShellController = shellController;
         mTaskOrganizer = shellTaskOrganizer;
         mSyncQueue = syncQueue;
@@ -183,6 +188,7 @@
         mLogger = new SplitscreenEventLogger();
         mIconProvider = iconProvider;
         mRecentTasksOptional = recentTasks;
+        mSplitScreenShellCommandHandler = new SplitScreenShellCommandHandler(this);
         // TODO(b/238217847): Temporarily add this check here until we can remove the dynamic
         //                    override for this controller from the base module
         if (ActivityTaskManager.supportsSplitScreenMultiWindow(context)) {
@@ -200,6 +206,9 @@
      */
     @VisibleForTesting
     void onInit() {
+        mShellCommandHandler.addDumpCallback(this::dump, this);
+        mShellCommandHandler.addCommandCallback("splitscreen", mSplitScreenShellCommandHandler,
+                this);
         mShellController.addKeyguardChangeListener(this);
         if (mStageCoordinator == null) {
             // TODO: Multi-display
@@ -337,17 +346,39 @@
     }
 
     public void startTask(int taskId, @SplitPosition int position, @Nullable Bundle options) {
+        final int[] result = new int[1];
+        IRemoteAnimationRunner wrapper = new IRemoteAnimationRunner.Stub() {
+            @Override
+            public void onAnimationStart(@WindowManager.TransitionOldType int transit,
+                    RemoteAnimationTarget[] apps,
+                    RemoteAnimationTarget[] wallpapers,
+                    RemoteAnimationTarget[] nonApps,
+                    final IRemoteAnimationFinishedCallback finishedCallback) {
+                try {
+                    finishedCallback.onAnimationFinished();
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Failed to invoke onAnimationFinished", e);
+                }
+                if (result[0] == START_SUCCESS || result[0] == START_TASK_TO_FRONT) {
+                    final WindowContainerTransaction evictWct = new WindowContainerTransaction();
+                    mStageCoordinator.prepareEvictNonOpeningChildTasks(position, apps, evictWct);
+                    mSyncQueue.queue(evictWct);
+                }
+            }
+            @Override
+            public void onAnimationCancelled(boolean isKeyguardOccluded) {
+            }
+        };
         options = mStageCoordinator.resolveStartStage(STAGE_TYPE_UNDEFINED, position, options,
                 null /* wct */);
+        RemoteAnimationAdapter wrappedAdapter = new RemoteAnimationAdapter(wrapper,
+                0 /* duration */, 0 /* statusBarTransitionDelay */);
+        ActivityOptions activityOptions = ActivityOptions.fromBundle(options);
+        activityOptions.update(ActivityOptions.makeRemoteAnimation(wrappedAdapter));
 
         try {
-            final WindowContainerTransaction evictWct = new WindowContainerTransaction();
-            mStageCoordinator.prepareEvictChildTasks(position, evictWct);
-            final int result =
-                    ActivityTaskManager.getService().startActivityFromRecents(taskId, options);
-            if (result == START_SUCCESS || result == START_TASK_TO_FRONT) {
-                mSyncQueue.queue(evictWct);
-            }
+            result[0] = ActivityTaskManager.getService().startActivityFromRecents(taskId,
+                    activityOptions.toBundle());
         } catch (RemoteException e) {
             Slog.e(TAG, "Failed to launch task", e);
         }
@@ -403,7 +434,7 @@
 
         // Flag with MULTIPLE_TASK if this is launching the same activity into both sides of the
         // split.
-        if (isLaunchingAdjacently(intent.getIntent(), position)) {
+        if (shouldAddMultipleTaskFlag(intent.getIntent(), position)) {
             fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
             ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
         }
@@ -418,8 +449,7 @@
 
     /** Returns {@code true} if it's launching the same component on both sides of the split. */
     @VisibleForTesting
-    boolean isLaunchingAdjacently(@Nullable Intent startIntent,
-            @SplitPosition int position) {
+    boolean shouldAddMultipleTaskFlag(@Nullable Intent startIntent, @SplitPosition int position) {
         if (startIntent == null) {
             return false;
         }
@@ -430,6 +460,16 @@
         }
 
         if (isSplitScreenVisible()) {
+            // To prevent users from constantly dropping the same app to the same side resulting in
+            // a large number of instances in the background.
+            final ActivityManager.RunningTaskInfo targetTaskInfo = getTaskInfo(position);
+            final ComponentName targetActivity = targetTaskInfo != null
+                    ? targetTaskInfo.baseIntent.getComponent() : null;
+            if (Objects.equals(launchingActivity, targetActivity)) {
+                return false;
+            }
+
+            // Allow users to start a new instance the same to adjacent side.
             final ActivityManager.RunningTaskInfo pairedTaskInfo =
                     getTaskInfo(SplitLayout.reversePosition(position));
             final ComponentName pairedActivity = pairedTaskInfo != null
@@ -453,12 +493,12 @@
             mStageCoordinator.prepareEvictInvisibleChildTasks(wct);
             mSyncQueue.queue(wct);
         }
-        return reparentSplitTasksForAnimation(apps, true /*splitExpectedToBeVisible*/);
+        return reparentSplitTasksForAnimation(apps, false /* enterSplitScreen */);
     }
 
     RemoteAnimationTarget[] onStartingSplitLegacy(RemoteAnimationTarget[] apps) {
         try {
-            return reparentSplitTasksForAnimation(apps, false /*splitExpectedToBeVisible*/);
+            return reparentSplitTasksForAnimation(apps, true /* enterSplitScreen */);
         } finally {
             for (RemoteAnimationTarget appTarget : apps) {
                 if (appTarget.leash != null) {
@@ -469,14 +509,23 @@
     }
 
     private RemoteAnimationTarget[] reparentSplitTasksForAnimation(RemoteAnimationTarget[] apps,
-            boolean splitExpectedToBeVisible) {
+            boolean enterSplitScreen) {
         if (ENABLE_SHELL_TRANSITIONS) return null;
-        // TODO(b/206487881): Integrate this with shell transition.
-        if (splitExpectedToBeVisible && !isSplitScreenVisible()) return null;
-        // Split not visible, but not enough apps to have split, also return null
-        if (!splitExpectedToBeVisible && apps.length < 2) return null;
 
-        SurfaceControl.Transaction transaction = new SurfaceControl.Transaction();
+        if (enterSplitScreen) {
+            int openingApps = 0;
+            for (int i = 0; i < apps.length; ++i) {
+                if (apps[i].mode == MODE_OPENING) openingApps++;
+            }
+            if (openingApps < 2) {
+                // Not having enough apps to enter split screen
+                return null;
+            }
+        } else if (!isSplitScreenVisible()) {
+            return null;
+        }
+
+        final SurfaceControl.Transaction transaction = mTransactionPool.acquire();
         if (mSplitTasksContainerLayer != null) {
             // Remove the previous layer before recreating
             transaction.remove(mSplitTasksContainerLayer);
@@ -489,17 +538,14 @@
         mRootTDAOrganizer.attachToDisplayArea(DEFAULT_DISPLAY, builder);
         mSplitTasksContainerLayer = builder.build();
 
-        // Ensure that we order these in the parent in the right z-order as their previous order
-        Arrays.sort(apps, (a1, a2) -> a1.prefixOrderIndex - a2.prefixOrderIndex);
-        int layer = 1;
-        for (RemoteAnimationTarget appTarget : apps) {
+        for (int i = 0; i < apps.length; ++i) {
+            final RemoteAnimationTarget appTarget = apps[i];
             transaction.reparent(appTarget.leash, mSplitTasksContainerLayer);
             transaction.setPosition(appTarget.leash, appTarget.screenSpaceBounds.left,
                     appTarget.screenSpaceBounds.top);
-            transaction.setLayer(appTarget.leash, layer++);
         }
         transaction.apply();
-        transaction.close();
+        mTransactionPool.release(transaction);
         return new RemoteAnimationTarget[]{mStageCoordinator.getDividerBarLegacyTarget()};
     }
     /**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenShellCommandHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenShellCommandHandler.java
new file mode 100644
index 0000000..681d964
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenShellCommandHandler.java
@@ -0,0 +1,96 @@
+/*
+ * 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.splitscreen;
+
+import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
+
+import com.android.wm.shell.sysui.ShellCommandHandler;
+
+import java.io.PrintWriter;
+
+/**
+ * Handles the shell commands for the SplitscreenController.
+ */
+public class SplitScreenShellCommandHandler implements
+        ShellCommandHandler.ShellCommandActionHandler {
+
+    private final SplitScreenController mController;
+
+    public SplitScreenShellCommandHandler(SplitScreenController controller) {
+        mController = controller;
+    }
+
+    @Override
+    public boolean onShellCommand(String[] args, PrintWriter pw) {
+        switch (args[0]) {
+            case "moveToSideStage":
+                return runMoveToSideStage(args, pw);
+            case "removeFromSideStage":
+                return runRemoveFromSideStage(args, pw);
+            case "setSideStagePosition":
+                return runSetSideStagePosition(args, pw);
+            default:
+                pw.println("Invalid command: " + args[0]);
+                return false;
+        }
+    }
+
+    private boolean runMoveToSideStage(String[] args, PrintWriter pw) {
+        if (args.length < 3) {
+            // First argument is the action name.
+            pw.println("Error: task id should be provided as arguments");
+            return false;
+        }
+        final int taskId = new Integer(args[1]);
+        final int sideStagePosition = args.length > 3
+                ? new Integer(args[2]) : SPLIT_POSITION_BOTTOM_OR_RIGHT;
+        mController.moveToSideStage(taskId, sideStagePosition);
+        return true;
+    }
+
+    private boolean runRemoveFromSideStage(String[] args, PrintWriter pw) {
+        if (args.length < 2) {
+            // First argument is the action name.
+            pw.println("Error: task id should be provided as arguments");
+            return false;
+        }
+        final int taskId = new Integer(args[1]);
+        mController.removeFromSideStage(taskId);
+        return true;
+    }
+
+    private boolean runSetSideStagePosition(String[] args, PrintWriter pw) {
+        if (args.length < 2) {
+            // First argument is the action name.
+            pw.println("Error: side stage position should be provided as arguments");
+            return false;
+        }
+        final int position = new Integer(args[1]);
+        mController.setSideStagePosition(position);
+        return true;
+    }
+
+    @Override
+    public void printShellCommandHelp(PrintWriter pw, String prefix) {
+        pw.println(prefix + "moveToSideStage <taskId> <SideStagePosition>");
+        pw.println(prefix + "  Move a task with given id in split-screen mode.");
+        pw.println(prefix + "removeFromSideStage <taskId>");
+        pw.println(prefix + "  Remove a task with given id in split-screen mode.");
+        pw.println(prefix + "setSideStagePosition <SideStagePosition>");
+        pw.println(prefix + "  Sets the position of the side-stage.");
+    }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
index 83bdf8b..d7ca791 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
@@ -94,6 +94,7 @@
             @NonNull WindowContainerToken topRoot) {
         mFinishCallback = finishCallback;
         mAnimatingTransition = transition;
+        mFinishTransaction = finishTransaction;
         if (mPendingRemoteHandler != null) {
             mPendingRemoteHandler.startAnimation(transition, info, startTransaction,
                     finishTransaction, mRemoteFinishCB);
@@ -107,8 +108,6 @@
     private void playInternalAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
             @NonNull SurfaceControl.Transaction t, @NonNull WindowContainerToken mainRoot,
             @NonNull WindowContainerToken sideRoot, @NonNull WindowContainerToken topRoot) {
-        mFinishTransaction = mTransactionPool.acquire();
-
         // Play some place-holder fade animations
         for (int i = info.getChanges().size() - 1; i >= 0; --i) {
             final TransitionInfo.Change change = info.getChanges().get(i);
@@ -287,16 +286,14 @@
         return true;
     }
 
-    void onTransitionConsumed(@NonNull IBinder transition, boolean aborted) {
+    void onTransitionConsumed(@NonNull IBinder transition, boolean aborted,
+            @Nullable SurfaceControl.Transaction finishT) {
         if (isPendingEnter(transition)) {
             if (!aborted) {
                 // An enter transition got merged, appends the rest operations to finish entering
                 // split screen.
-                // TODO (b/238856352): Passed-in the proper finish transition to merge instead.
-                if (mFinishTransaction == null) {
-                    mFinishTransaction = mTransactionPool.acquire();
-                }
-                mStageCoordinator.finishEnterSplitScreen(mFinishTransaction);
+                mStageCoordinator.finishEnterSplitScreen(finishT);
+                mPendingRemoteHandler = null;
             }
 
             mPendingEnter.mCallback.onTransitionConsumed(aborted);
@@ -339,11 +336,6 @@
         mAnimatingTransition = null;
 
         mOnFinish.run();
-        if (mFinishTransaction != null) {
-            mFinishTransaction.apply();
-            mTransactionPool.release(mFinishTransaction);
-            mFinishTransaction = null;
-        }
         if (mFinishCallback != null) {
             mFinishCallback.onTransitionFinished(wct /* wct */, wctCB /* wctCB */);
             mFinishCallback = null;
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 f2340d5..8e1ae39 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
@@ -233,6 +233,7 @@
                 if (op.getType() == HIERARCHY_OP_TYPE_REORDER && op.getToTop()
                         && (mMainStage.containsContainer(container)
                         || mSideStage.containsContainer(container))) {
+                    updateSurfaceBounds(mSplitLayout, finishT, false /* applyResizingOffset */);
                     setDividerVisibility(true, finishT);
                     return;
                 }
@@ -451,10 +452,16 @@
                     IRemoteAnimationFinishedCallback finishedCallback,
                     SurfaceControl.Transaction t) {
                 if (apps == null || apps.length == 0) {
-                    // Switch the split position if launching as MULTIPLE_TASK failed.
-                    if ((fillInIntent.getFlags() & FLAG_ACTIVITY_MULTIPLE_TASK) != 0) {
-                        setSideStagePosition(SplitLayout.reversePosition(
-                                getSideStagePosition()), null);
+                    if (mMainStage.getChildCount() == 0 || mSideStage.getChildCount() == 0) {
+                        mMainExecutor.execute(() ->
+                                exitSplitScreen(mMainStage.getChildCount() == 0
+                                        ? mSideStage : mMainStage, EXIT_REASON_UNKNOWN));
+                    } else {
+                        // Switch the split position if launching as MULTIPLE_TASK failed.
+                        if ((fillInIntent.getFlags() & FLAG_ACTIVITY_MULTIPLE_TASK) != 0) {
+                            setSideStagePosition(SplitLayout.reversePosition(
+                                    getSideStagePosition()), null);
+                        }
                     }
 
                     // Do nothing when the animation was cancelled.
@@ -650,7 +657,7 @@
         mShouldUpdateRecents = true;
         // If any stage has no child after animation finished, it means that split will display
         // nothing, such status will happen if task and intent is same app but not support
-        // multi-instagce, we should exit split and expand that app as full screen.
+        // multi-instance, we should exit split and expand that app as full screen.
         if (!cancel && (mMainStage.getChildCount() == 0 || mSideStage.getChildCount() == 0)) {
             mMainExecutor.execute(() ->
                     exitSplitScreen(mMainStage.getChildCount() == 0
@@ -1742,8 +1749,9 @@
     }
 
     @Override
-    public void onTransitionConsumed(@NonNull IBinder transition, boolean aborted) {
-        mSplitTransitions.onTransitionConsumed(transition, aborted);
+    public void onTransitionConsumed(@NonNull IBinder transition, boolean aborted,
+            @Nullable SurfaceControl.Transaction finishT) {
+        mSplitTransitions.onTransitionConsumed(transition, aborted, finishT);
     }
 
     @Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
index f414d69..1af9415 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
@@ -33,6 +33,7 @@
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.os.IBinder;
+import android.util.Slog;
 import android.util.SparseArray;
 import android.view.RemoteAnimationTarget;
 import android.view.SurfaceControl;
@@ -376,7 +377,13 @@
             SurfaceControl leash, boolean firstAppeared) {
         final Point taskPositionInParent = taskInfo.positionInParent;
         mSyncQueue.runInSync(t -> {
-            t.setWindowCrop(leash, null);
+            // The task surface might be released before running in the sync queue for the case like
+            // trampoline launch, so check if the surface is valid before processing it.
+            if (!leash.isValid()) {
+                Slog.w(TAG, "Skip updating invalid child task surface of task#" + taskInfo.taskId);
+                return;
+            }
+            t.setCrop(leash, null);
             t.setPosition(leash, taskPositionInParent.x, taskPositionInParent.y);
             if (firstAppeared && !ENABLE_SHELL_TRANSITIONS) {
                 t.setAlpha(leash, 1f);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellCommandHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellCommandHandler.java
index f4fc0c4..2e6ddc3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellCommandHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellCommandHandler.java
@@ -16,19 +16,14 @@
 
 package com.android.wm.shell.sysui;
 
-import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
+import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_INIT;
 
-import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.common.ShellExecutor;
-import com.android.wm.shell.hidedisplaycutout.HideDisplayCutoutController;
-import com.android.wm.shell.kidsmode.KidsModeTaskOrganizer;
-import com.android.wm.shell.onehanded.OneHandedController;
-import com.android.wm.shell.pip.Pip;
-import com.android.wm.shell.recents.RecentTasksController;
-import com.android.wm.shell.splitscreen.SplitScreenController;
+import com.android.internal.protolog.common.ProtoLog;
 
 import java.io.PrintWriter;
-import java.util.Optional;
+import java.util.Arrays;
+import java.util.TreeMap;
+import java.util.function.BiConsumer;
 
 /**
  * An entry point into the shell for dumping shell internal state and running adb commands.
@@ -38,52 +33,61 @@
 public final class ShellCommandHandler {
     private static final String TAG = ShellCommandHandler.class.getSimpleName();
 
-    private final Optional<SplitScreenController> mSplitScreenOptional;
-    private final Optional<Pip> mPipOptional;
-    private final Optional<OneHandedController> mOneHandedOptional;
-    private final Optional<HideDisplayCutoutController> mHideDisplayCutout;
-    private final Optional<RecentTasksController> mRecentTasks;
-    private final ShellTaskOrganizer mShellTaskOrganizer;
-    private final KidsModeTaskOrganizer mKidsModeTaskOrganizer;
+    // We're using a TreeMap to keep them sorted by command name
+    private final TreeMap<String, BiConsumer<PrintWriter, String>> mDumpables = new TreeMap<>();
+    private final TreeMap<String, ShellCommandActionHandler> mCommands = new TreeMap<>();
 
-    public ShellCommandHandler(
-            ShellController shellController,
-            ShellTaskOrganizer shellTaskOrganizer,
-            KidsModeTaskOrganizer kidsModeTaskOrganizer,
-            Optional<SplitScreenController> splitScreenOptional,
-            Optional<Pip> pipOptional,
-            Optional<OneHandedController> oneHandedOptional,
-            Optional<HideDisplayCutoutController> hideDisplayCutout,
-            Optional<RecentTasksController> recentTasks,
-            ShellExecutor mainExecutor) {
-        mShellTaskOrganizer = shellTaskOrganizer;
-        mKidsModeTaskOrganizer = kidsModeTaskOrganizer;
-        mRecentTasks = recentTasks;
-        mSplitScreenOptional = splitScreenOptional;
-        mPipOptional = pipOptional;
-        mOneHandedOptional = oneHandedOptional;
-        mHideDisplayCutout = hideDisplayCutout;
-        // TODO(238217847): To be removed once the command handler dependencies are inverted
-        shellController.setShellCommandHandler(this);
+    public interface ShellCommandActionHandler {
+        /**
+         * Handles the given command.
+         *
+         * @param args the arguments starting with the action name, then the action arguments
+         * @param pw the write to print output to
+         */
+        boolean onShellCommand(String[] args, PrintWriter pw);
+
+        /**
+         * Prints the help for this class of commands.  Implementations do not need to print the
+         * command class.
+         */
+        void printShellCommandHelp(PrintWriter pw, String prefix);
+    }
+
+
+    /**
+     * Adds a callback to run when the Shell is being dumped.
+     *
+     * @param callback the callback to be made when Shell is dumped, takes the print writer and
+     *                 a prefix
+     * @param instance used for debugging only
+     */
+    public <T> void addDumpCallback(BiConsumer<PrintWriter, String> callback, T instance) {
+        mDumpables.put(instance.getClass().getSimpleName(), callback);
+        ProtoLog.v(WM_SHELL_INIT, "Adding dump callback for %s",
+                instance.getClass().getSimpleName());
+    }
+
+    /**
+     * Adds an action callback to be invoked when the user runs that particular command from adb.
+     *
+     * @param commandClass the top level class of command to invoke
+     * @param actions the interface to callback when an action of this class is invoked
+     * @param instance used for debugging only
+     */
+    public <T> void addCommandCallback(String commandClass, ShellCommandActionHandler actions,
+            T instance) {
+        mCommands.put(commandClass, actions);
+        ProtoLog.v(WM_SHELL_INIT, "Adding command class %s for %s", commandClass,
+                instance.getClass().getSimpleName());
     }
 
     /** Dumps WM Shell internal state. */
     public void dump(PrintWriter pw) {
-        mShellTaskOrganizer.dump(pw, "");
-        pw.println();
-        pw.println();
-        mPipOptional.ifPresent(pip -> pip.dump(pw));
-        mOneHandedOptional.ifPresent(oneHanded -> oneHanded.dump(pw));
-        mHideDisplayCutout.ifPresent(hideDisplayCutout -> hideDisplayCutout.dump(pw));
-        pw.println();
-        pw.println();
-        mSplitScreenOptional.ifPresent(splitScreen -> splitScreen.dump(pw, ""));
-        pw.println();
-        pw.println();
-        mRecentTasks.ifPresent(recentTasks -> recentTasks.dump(pw, ""));
-        pw.println();
-        pw.println();
-        mKidsModeTaskOrganizer.dump(pw, "");
+        for (String key : mDumpables.keySet()) {
+            final BiConsumer<PrintWriter, String> r = mDumpables.get(key);
+            r.accept(pw, "");
+            pw.println();
+        }
     }
 
 
@@ -93,72 +97,32 @@
             // Argument at position 0 is "WMShell".
             return false;
         }
-        switch (args[1]) {
-            case "moveToSideStage":
-                return runMoveToSideStage(args, pw);
-            case "removeFromSideStage":
-                return runRemoveFromSideStage(args, pw);
-            case "setSideStagePosition":
-                return runSetSideStagePosition(args, pw);
-            case "help":
-                return runHelp(pw);
-            default:
-                return false;
-        }
-    }
 
-    private boolean runMoveToSideStage(String[] args, PrintWriter pw) {
-        if (args.length < 3) {
-            // First arguments are "WMShell" and command name.
-            pw.println("Error: task id should be provided as arguments");
+        final String cmdClass = args[1];
+        if (cmdClass.toLowerCase().equals("help")) {
+            return runHelp(pw);
+        }
+        if (!mCommands.containsKey(cmdClass)) {
             return false;
         }
-        final int taskId = new Integer(args[2]);
-        final int sideStagePosition = args.length > 3
-                ? new Integer(args[3]) : SPLIT_POSITION_BOTTOM_OR_RIGHT;
-        mSplitScreenOptional.ifPresent(split -> split.moveToSideStage(taskId, sideStagePosition));
-        return true;
-    }
 
-    private boolean runRemoveFromSideStage(String[] args, PrintWriter pw) {
-        if (args.length < 3) {
-            // First arguments are "WMShell" and command name.
-            pw.println("Error: task id should be provided as arguments");
-            return false;
-        }
-        final int taskId = new Integer(args[2]);
-        mSplitScreenOptional.ifPresent(split -> split.removeFromSideStage(taskId));
-        return true;
-    }
-
-    private boolean runSetSideStagePosition(String[] args, PrintWriter pw) {
-        if (args.length < 3) {
-            // First arguments are "WMShell" and command name.
-            pw.println("Error: side stage position should be provided as arguments");
-            return false;
-        }
-        final int position = new Integer(args[2]);
-        mSplitScreenOptional.ifPresent(split -> split.setSideStagePosition(position));
+        // Only pass the actions onwards as arguments to the callback
+        final ShellCommandActionHandler actions = mCommands.get(args[1]);
+        final String[] cmdClassArgs = Arrays.copyOfRange(args, 2, args.length);
+        actions.onShellCommand(cmdClassArgs, pw);
         return true;
     }
 
     private boolean runHelp(PrintWriter pw) {
         pw.println("Window Manager Shell commands:");
+        for (String commandClass : mCommands.keySet()) {
+            pw.println("  " + commandClass);
+            mCommands.get(commandClass).printShellCommandHelp(pw, "    ");
+        }
         pw.println("  help");
         pw.println("      Print this help text.");
         pw.println("  <no arguments provided>");
-        pw.println("    Dump Window Manager Shell internal state");
-        pw.println("  pair <taskId1> <taskId2>");
-        pw.println("  unpair <taskId>");
-        pw.println("    Pairs/unpairs tasks with given ids.");
-        pw.println("  moveToSideStage <taskId> <SideStagePosition>");
-        pw.println("    Move a task with given id in split-screen mode.");
-        pw.println("  removeFromSideStage <taskId>");
-        pw.println("    Remove a task with given id in split-screen mode.");
-        pw.println("  setSideStageOutline <true/false>");
-        pw.println("    Enable/Disable outline on the side-stage.");
-        pw.println("  setSideStagePosition <SideStagePosition>");
-        pw.println("    Sets the position of the side-stage.");
+        pw.println("    Dump all Window Manager Shell internal state");
         return true;
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellController.java
index f1f317f..52ffb46 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellController.java
@@ -45,11 +45,10 @@
     private static final String TAG = ShellController.class.getSimpleName();
 
     private final ShellInit mShellInit;
+    private final ShellCommandHandler mShellCommandHandler;
     private final ShellExecutor mMainExecutor;
     private final ShellInterfaceImpl mImpl = new ShellInterfaceImpl();
 
-    private ShellCommandHandler mShellCommandHandler;
-
     private final CopyOnWriteArrayList<ConfigurationChangeListener> mConfigChangeListeners =
             new CopyOnWriteArrayList<>();
     private final CopyOnWriteArrayList<KeyguardChangeListener> mKeyguardChangeListeners =
@@ -57,8 +56,10 @@
     private Configuration mLastConfiguration;
 
 
-    public ShellController(ShellInit shellInit, ShellExecutor mainExecutor) {
+    public ShellController(ShellInit shellInit, ShellCommandHandler shellCommandHandler,
+            ShellExecutor mainExecutor) {
         mShellInit = shellInit;
+        mShellCommandHandler = shellCommandHandler;
         mMainExecutor = mainExecutor;
     }
 
@@ -70,15 +71,6 @@
     }
 
     /**
-     * Sets the command handler to call back to.
-     * TODO(238217847): This is only exposed this way until we can remove the dependencies from the
-     *                  command handler to other classes.
-     */
-    public void setShellCommandHandler(ShellCommandHandler shellCommandHandler) {
-        mShellCommandHandler = shellCommandHandler;
-    }
-
-    /**
      * Adds a new configuration listener. The configuration change callbacks are not made in any
      * particular order.
      */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellInit.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellInit.java
index c250e03..ac52235 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellInit.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellInit.java
@@ -52,6 +52,9 @@
      * Adds a callback to the ordered list of callbacks be made when Shell is first started.  This
      * can be used in class constructors when dagger is used to ensure that the initialization order
      * matches the dependency order.
+     *
+     * @param r the callback to be made when Shell is initialized
+     * @param instance used for debugging only
      */
     public <T extends Object> void addInitCallback(Runnable r, T instance) {
         if (mHasInitialized) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
index 11b453c..5cce6b9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
@@ -274,7 +274,8 @@
     }
 
     @Override
-    public void onTransitionConsumed(@NonNull IBinder transition, boolean aborted) {
+    public void onTransitionConsumed(@NonNull IBinder transition, boolean aborted,
+            @Nullable SurfaceControl.Transaction finishT) {
         MixedTransition mixed = null;
         for (int i = mActiveTransitions.size() - 1; i >= 0; --i) {
             if (mActiveTransitions.get(i).mTransition != transition) continue;
@@ -283,7 +284,7 @@
         }
         if (mixed == null) return;
         if (mixed.mType == MixedTransition.TYPE_ENTER_PIP_FROM_SPLIT) {
-            mPipHandler.onTransitionConsumed(transition, aborted);
+            mPipHandler.onTransitionConsumed(transition, aborted, finishT);
         }
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
index cedb340..9469529 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
@@ -82,7 +82,8 @@
     }
 
     @Override
-    public void onTransitionConsumed(@NonNull IBinder transition, boolean aborted) {
+    public void onTransitionConsumed(@NonNull IBinder transition, boolean aborted,
+            @Nullable SurfaceControl.Transaction finishT) {
         mRequestedRemotes.remove(transition);
     }
 
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 cd29741..279d57a 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
@@ -159,8 +159,10 @@
     private void onInit() {
         // The very last handler (0 in the list) should be the default one.
         mHandlers.add(mDefaultTransitionHandler);
+        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "addHandler: Default");
         // Next lowest priority is remote transitions.
         mHandlers.add(mRemoteTransitionHandler);
+        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "addHandler: Remote");
 
         ContentResolver resolver = mContext.getContentResolver();
         mTransitionAnimationScaleSetting = Settings.Global.getFloat(resolver,
@@ -206,7 +208,13 @@
      * @see TransitionHandler
      */
     public void addHandler(@NonNull TransitionHandler handler) {
+        if (mHandlers.isEmpty()) {
+            throw new RuntimeException("Unexpected handler added prior to initialization, please "
+                    + "use ShellInit callbacks to ensure proper ordering");
+        }
         mHandlers.add(handler);
+        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "addHandler: %s",
+                handler.getClass().getSimpleName());
     }
 
     public ShellExecutor getMainExecutor() {
@@ -535,7 +543,8 @@
             active.mMerged = true;
             active.mAborted = abort;
             if (active.mHandler != null) {
-                active.mHandler.onTransitionConsumed(active.mToken, abort);
+                active.mHandler.onTransitionConsumed(
+                        active.mToken, abort, abort ? null : active.mFinishT);
             }
             return;
         }
@@ -543,7 +552,8 @@
         active.mAborted = abort;
         if (active.mAborted && active.mHandler != null) {
             // Notifies to clean-up the aborted transition.
-            active.mHandler.onTransitionConsumed(transition, true /* aborted */);
+            active.mHandler.onTransitionConsumed(
+                    transition, true /* aborted */, null /* finishTransaction */);
         }
         ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
                 "Transition animation finished (abort=%b), notifying core %s", abort, transition);
@@ -579,7 +589,8 @@
             ActiveTransition aborted = mActiveTransitions.remove(activeIdx);
             // Notifies to clean-up the aborted transition.
             if (aborted.mHandler != null) {
-                aborted.mHandler.onTransitionConsumed(transition, true /* aborted */);
+                aborted.mHandler.onTransitionConsumed(
+                        transition, true /* aborted */, null /* finishTransaction */);
             }
             mOrganizer.finishTransition(aborted.mToken, null /* wct */, null /* wctCB */);
         }
@@ -765,8 +776,13 @@
          * Called when a transition which was already "claimed" by this handler has been merged
          * into another animation or has been aborted. Gives this handler a chance to clean-up any
          * expectations.
+         *
+         * @param transition The transition been consumed.
+         * @param aborted Whether the transition is aborted or not.
+         * @param finishTransaction The transaction to be applied after the transition animated.
          */
-        default void onTransitionConsumed(@NonNull IBinder transition, boolean aborted) { }
+        default void onTransitionConsumed(@NonNull IBinder transition, boolean aborted,
+                @Nullable SurfaceControl.Transaction finishTransaction) { }
 
         /**
          * Sets transition animation scale settings value to handler.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
index 4380bdc..506a4c0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
@@ -66,6 +66,7 @@
     final DisplayController mDisplayController;
     final ShellTaskOrganizer mTaskOrganizer;
     final Supplier<SurfaceControl.Builder> mSurfaceControlBuilderSupplier;
+    final Supplier<WindowContainerTransaction> mWindowContainerTransactionSupplier;
     final SurfaceControlViewHostFactory mSurfaceControlViewHostFactory;
     private final DisplayController.OnDisplaysChangedListener mOnDisplaysChangedListener =
             new DisplayController.OnDisplaysChangedListener() {
@@ -102,7 +103,8 @@
             RunningTaskInfo taskInfo,
             SurfaceControl taskSurface) {
         this(context, displayController, taskOrganizer, taskInfo, taskSurface,
-                SurfaceControl.Builder::new, new SurfaceControlViewHostFactory() {});
+                SurfaceControl.Builder::new, WindowContainerTransaction::new,
+                new SurfaceControlViewHostFactory() {});
     }
 
     WindowDecoration(
@@ -112,6 +114,7 @@
             RunningTaskInfo taskInfo,
             SurfaceControl taskSurface,
             Supplier<SurfaceControl.Builder> surfaceControlBuilderSupplier,
+            Supplier<WindowContainerTransaction> windowContainerTransactionSupplier,
             SurfaceControlViewHostFactory surfaceControlViewHostFactory) {
         mContext = context;
         mDisplayController = displayController;
@@ -119,6 +122,7 @@
         mTaskInfo = taskInfo;
         mTaskSurface = taskSurface;
         mSurfaceControlBuilderSupplier = surfaceControlBuilderSupplier;
+        mWindowContainerTransactionSupplier = windowContainerTransactionSupplier;
         mSurfaceControlViewHostFactory = surfaceControlViewHostFactory;
 
         mDisplay = mDisplayController.getDisplay(mTaskInfo.displayId);
@@ -229,7 +233,9 @@
         mTmpColor[2] = (float) Color.blue(backgroundColorInt) / 255.f;
         startT.setWindowCrop(mTaskBackgroundSurface, taskBounds.width(), taskBounds.height())
                 .setShadowRadius(mTaskBackgroundSurface, shadowRadius)
-                .setColor(mTaskBackgroundSurface, mTmpColor);
+                .setColor(mTaskBackgroundSurface, mTmpColor)
+                .setLayer(mTaskBackgroundSurface, -1)
+                .show(mTaskBackgroundSurface);
 
         // Caption view
         mCaptionWindowManager.setConfiguration(taskConfig);
@@ -301,6 +307,10 @@
             mTaskBackgroundSurface.release();
             mTaskBackgroundSurface = null;
         }
+
+        final WindowContainerTransaction wct = mWindowContainerTransactionSupplier.get();
+        wct.removeInsetsProvider(mTaskInfo.token, CAPTION_INSETS_TYPES);
+        mTaskOrganizer.applyTransaction(wct);
     }
 
     @Override
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt
index 07e19be..4877442 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt
@@ -229,7 +229,7 @@
                 ?.layerStackSpace
                 ?: error("Display not found")
             val dividerBar = device.wait(Until.findObject(dividerBarSelector), TIMEOUT_MS)
-            dividerBar.drag(Point(displayBounds.width, displayBounds.width))
+            dividerBar.drag(Point(displayBounds.width * 4 / 5, displayBounds.height * 4 / 5))
         }
 
         fun doubleTapDividerToSwitch(device: UiDevice) {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
index 7517e8a..f865649 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
@@ -59,6 +59,7 @@
 
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.compatui.CompatUIController;
+import com.android.wm.shell.sysui.ShellCommandHandler;
 import com.android.wm.shell.sysui.ShellInit;
 
 import org.junit.Before;
@@ -85,10 +86,12 @@
     @Mock
     private CompatUIController mCompatUI;
     @Mock
-    private ShellInit mShellInit;
+    private ShellExecutor mTestExecutor;
+    @Mock
+    private ShellCommandHandler mShellCommandHandler;
 
-    ShellTaskOrganizer mOrganizer;
-    private final ShellExecutor mTestExecutor = mock(ShellExecutor.class);
+    private ShellTaskOrganizer mOrganizer;
+    private ShellInit mShellInit;
 
     private class TrackingTaskListener implements ShellTaskOrganizer.TaskListener {
         final ArrayList<RunningTaskInfo> appeared = new ArrayList<>();
@@ -132,8 +135,11 @@
             doReturn(ParceledListSlice.<TaskAppearedInfo>emptyList())
                     .when(mTaskOrganizerController).registerTaskOrganizer(any());
         } catch (RemoteException e) {}
-        mOrganizer = spy(new ShellTaskOrganizer(mShellInit, mTaskOrganizerController,
-                mCompatUI, Optional.empty(), Optional.empty(), mTestExecutor));
+        mShellInit = spy(new ShellInit(mTestExecutor));
+        mOrganizer = spy(new ShellTaskOrganizer(mShellInit, mShellCommandHandler,
+                mTaskOrganizerController, mCompatUI, Optional.empty(), Optional.empty(),
+                mTestExecutor));
+        mShellInit.init();
     }
 
     @Test
@@ -142,9 +148,12 @@
     }
 
     @Test
-    public void testRegisterOrganizer_sendRegisterTaskOrganizer() throws RemoteException {
-        mOrganizer.registerOrganizer();
+    public void instantiate_addDumpCallback() {
+        verify(mShellCommandHandler, times(1)).addDumpCallback(any(), any());
+    }
 
+    @Test
+    public void testInit_sendRegisterTaskOrganizer() throws RemoteException {
         verify(mTaskOrganizerController).registerTaskOrganizer(any(ITaskOrganizer.class));
     }
 
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
index ba81602..b0dd781 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
@@ -29,6 +29,7 @@
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
@@ -61,6 +62,7 @@
 import com.android.internal.util.test.FakeSettingsProvider;
 import com.android.wm.shell.ShellTestCase;
 import com.android.wm.shell.TestShellExecutor;
+import com.android.wm.shell.sysui.ShellInit;
 
 import org.junit.Before;
 import org.junit.Ignore;
@@ -81,6 +83,7 @@
 
     private static final String ANIMATION_ENABLED = "1";
     private final TestShellExecutor mShellExecutor = new TestShellExecutor();
+    private ShellInit mShellInit;
 
     @Rule
     public TestableContext mContext =
@@ -110,10 +113,12 @@
         Settings.Global.putString(mContentResolver, Settings.Global.ENABLE_BACK_ANIMATION,
                 ANIMATION_ENABLED);
         mTestableLooper = TestableLooper.get(this);
-        mController = new BackAnimationController(
+        mShellInit = spy(new ShellInit(mShellExecutor));
+        mController = new BackAnimationController(mShellInit,
                 mShellExecutor, new Handler(mTestableLooper.getLooper()), mTransaction,
                 mActivityTaskManager, mContext,
                 mContentResolver);
+        mShellInit.init();
         mEventTime = 0;
         mShellExecutor.flushAll();
     }
@@ -160,6 +165,11 @@
     }
 
     @Test
+    public void instantiateController_addInitCallback() {
+        verify(mShellInit, times(1)).addInitCallback(any(), any());
+    }
+
+    @Test
     @Ignore("b/207481538")
     public void crossActivity_screenshotAttachedAndVisible() {
         SurfaceControl screenshotSurface = new SurfaceControl();
@@ -233,10 +243,12 @@
     public void animationDisabledFromSettings() throws RemoteException {
         // Toggle the setting off
         Settings.Global.putString(mContentResolver, Settings.Global.ENABLE_BACK_ANIMATION, "0");
-        mController = new BackAnimationController(
+        ShellInit shellInit = new ShellInit(mShellExecutor);
+        mController = new BackAnimationController(shellInit,
                 mShellExecutor, new Handler(mTestableLooper.getLooper()), mTransaction,
                 mActivityTaskManager, mContext,
                 mContentResolver);
+        shellInit.init();
         mController.setBackToLauncherCallback(mIOnBackInvokedCallback);
 
         RemoteAnimationTarget animationTarget = createAnimationTarget();
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 828c13e..6292130 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
@@ -29,6 +29,7 @@
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
@@ -54,6 +55,7 @@
 import com.android.wm.shell.common.SyncTransactionQueue;
 import com.android.wm.shell.compatui.letterboxedu.LetterboxEduWindowManager;
 import com.android.wm.shell.sysui.ShellController;
+import com.android.wm.shell.sysui.ShellInit;
 import com.android.wm.shell.transition.Transitions;
 
 import org.junit.Before;
@@ -79,6 +81,7 @@
     private static final int TASK_ID = 12;
 
     private CompatUIController mController;
+    private ShellInit mShellInit;
     private @Mock ShellController mMockShellController;
     private @Mock DisplayController mMockDisplayController;
     private @Mock DisplayInsetsController mMockDisplayInsetsController;
@@ -107,9 +110,10 @@
         doReturn(TASK_ID).when(mMockLetterboxEduLayout).getTaskId();
         doReturn(true).when(mMockLetterboxEduLayout).createLayout(anyBoolean());
         doReturn(true).when(mMockLetterboxEduLayout).updateCompatInfo(any(), any(), anyBoolean());
-        mController = new CompatUIController(mContext, mMockShellController, mMockDisplayController,
-                mMockDisplayInsetsController, mMockImeController, mMockSyncQueue, mMockExecutor,
-                mMockTransitionsLazy) {
+        mShellInit = spy(new ShellInit(mMockExecutor));
+        mController = new CompatUIController(mContext, mShellInit, mMockShellController,
+                mMockDisplayController, mMockDisplayInsetsController, mMockImeController,
+                mMockSyncQueue, mMockExecutor, mMockTransitionsLazy) {
             @Override
             CompatUIWindowManager createCompatUiWindowManager(Context context, TaskInfo taskInfo,
                     ShellTaskOrganizer.TaskListener taskListener) {
@@ -122,10 +126,16 @@
                 return mMockLetterboxEduLayout;
             }
         };
+        mShellInit.init();
         spyOn(mController);
     }
 
     @Test
+    public void instantiateController_addInitCallback() {
+        verify(mShellInit, times(1)).addInitCallback(any(), any());
+    }
+
+    @Test
     public void instantiateController_registerKeyguardChangeListener() {
         verify(mMockShellController, times(1)).addKeyguardChangeListener(any());
     }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutControllerTest.java
index dcc504a..6c301bb 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutControllerTest.java
@@ -17,7 +17,9 @@
 package com.android.wm.shell.hidedisplaycutout;
 
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
@@ -29,7 +31,10 @@
 import androidx.test.platform.app.InstrumentationRegistry;
 
 import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.sysui.ShellCommandHandler;
 import com.android.wm.shell.sysui.ShellController;
+import com.android.wm.shell.sysui.ShellInit;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -45,17 +50,32 @@
             InstrumentationRegistry.getInstrumentation().getTargetContext(), null);
 
     @Mock
+    private ShellCommandHandler mShellCommandHandler;
+    @Mock
     private ShellController mShellController;
     @Mock
     private HideDisplayCutoutOrganizer mMockDisplayAreaOrganizer;
 
     private HideDisplayCutoutController mHideDisplayCutoutController;
+    private ShellInit mShellInit;
 
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
-        mHideDisplayCutoutController = new HideDisplayCutoutController(
-                mContext, mShellController, mMockDisplayAreaOrganizer);
+        mShellInit = spy(new ShellInit(mock(ShellExecutor.class)));
+        mHideDisplayCutoutController = new HideDisplayCutoutController(mContext, mShellInit,
+                mShellCommandHandler, mShellController, mMockDisplayAreaOrganizer);
+        mShellInit.init();
+    }
+
+    @Test
+    public void instantiateController_addInitCallback() {
+        verify(mShellInit, times(1)).addInitCallback(any(), any());
+    }
+
+    @Test
+    public void instantiateController_registerDumpCallback() {
+        verify(mShellCommandHandler, times(1)).addDumpCallback(any(), any());
     }
 
     @Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizerTest.java
index a919ad0..ecfb427 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizerTest.java
@@ -49,6 +49,7 @@
 import com.android.wm.shell.common.DisplayInsetsController;
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.SyncTransactionQueue;
+import com.android.wm.shell.sysui.ShellCommandHandler;
 import com.android.wm.shell.sysui.ShellInit;
 
 import org.junit.Before;
@@ -74,6 +75,7 @@
     @Mock private WindowContainerTransaction mTransaction;
     @Mock private KidsModeSettingsObserver mObserver;
     @Mock private ShellInit mShellInit;
+    @Mock private ShellCommandHandler mShellCommandHandler;
     @Mock private DisplayInsetsController mDisplayInsetsController;
 
     KidsModeTaskOrganizer mOrganizer;
@@ -87,14 +89,20 @@
         } catch (RemoteException e) {
         }
         // NOTE: KidsModeTaskOrganizer should have a null CompatUIController.
-        mOrganizer = spy(new KidsModeTaskOrganizer(mContext, mTaskOrganizerController,
-                mSyncTransactionQueue, mDisplayController, mDisplayInsetsController,
-                Optional.empty(), Optional.empty(), mObserver, mTestExecutor, mHandler));
+        mOrganizer = spy(new KidsModeTaskOrganizer(mContext, mShellInit, mShellCommandHandler,
+                mTaskOrganizerController, mSyncTransactionQueue, mDisplayController,
+                mDisplayInsetsController, Optional.empty(), Optional.empty(), mObserver,
+                mTestExecutor, mHandler));
         doReturn(mTransaction).when(mOrganizer).getWindowContainerTransaction();
         doReturn(new InsetsState()).when(mDisplayController).getInsetsState(DEFAULT_DISPLAY);
     }
 
     @Test
+    public void instantiateController_addInitCallback() {
+        verify(mShellInit, times(1)).addInitCallback(any(), any());
+    }
+
+    @Test
     public void testKidsModeOn() {
         doReturn(true).when(mObserver).isEnabled();
 
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
index dbf93ae..90645ce 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
@@ -36,7 +36,6 @@
 
 import android.graphics.Rect;
 import android.os.Handler;
-import android.os.UserHandle;
 import android.testing.AndroidTestingRunner;
 import android.util.ArrayMap;
 import android.view.Display;
@@ -49,7 +48,9 @@
 import com.android.wm.shell.common.DisplayLayout;
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.TaskStackListenerImpl;
+import com.android.wm.shell.sysui.ShellCommandHandler;
 import com.android.wm.shell.sysui.ShellController;
+import com.android.wm.shell.sysui.ShellInit;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -61,18 +62,20 @@
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 public class OneHandedControllerTest extends OneHandedTestCase {
-    private int mCurrentUser = UserHandle.myUserId();
 
     Display mDisplay;
     OneHandedAccessibilityUtil mOneHandedAccessibilityUtil;
     OneHandedController mSpiedOneHandedController;
     OneHandedTimeoutHandler mSpiedTimeoutHandler;
     OneHandedState mSpiedTransitionState;
+    ShellInit mShellInit;
 
     @Mock
+    ShellCommandHandler mMockShellCommandHandler;
+    @Mock
     ShellController mMockShellController;
     @Mock
-    DisplayLayout mDisplayLayout;
+    DisplayLayout mMockDisplayLayout;
     @Mock
     DisplayController mMockDisplayController;
     @Mock
@@ -102,7 +105,7 @@
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         mDisplay = mContext.getDisplay();
-        mDisplayLayout = Mockito.mock(DisplayLayout.class);
+        mMockDisplayLayout = Mockito.mock(DisplayLayout.class);
         mSpiedTimeoutHandler = spy(new OneHandedTimeoutHandler(mMockShellMainExecutor));
         mSpiedTransitionState = spy(new OneHandedState());
 
@@ -122,11 +125,14 @@
 
         when(mMockDisplayAreaOrganizer.getLastDisplayBounds()).thenReturn(
                 new Rect(0, 0, 1080, 2400));
-        when(mMockDisplayAreaOrganizer.getDisplayLayout()).thenReturn(mDisplayLayout);
+        when(mMockDisplayAreaOrganizer.getDisplayLayout()).thenReturn(mMockDisplayLayout);
 
+        mShellInit = spy(new ShellInit(mMockShellMainExecutor));
         mOneHandedAccessibilityUtil = new OneHandedAccessibilityUtil(mContext);
         mSpiedOneHandedController = spy(new OneHandedController(
                 mContext,
+                mShellInit,
+                mMockShellCommandHandler,
                 mMockShellController,
                 mMockDisplayController,
                 mMockDisplayAreaOrganizer,
@@ -141,6 +147,17 @@
                 mMockShellMainExecutor,
                 mMockShellMainHandler)
         );
+        mShellInit.init();
+    }
+
+    @Test
+    public void instantiateController_addInitCallback() {
+        verify(mShellInit, times(1)).addInitCallback(any(), any());
+    }
+
+    @Test
+    public void instantiateController_registerDumpCallback() {
+        verify(mMockShellCommandHandler, times(1)).addDumpCallback(any(), any());
     }
 
     @Test
@@ -308,9 +325,9 @@
 
     @Test
     public void testRotation90CanNotStartOneHanded() {
-        mDisplayLayout.rotateTo(mContext.getResources(), Surface.ROTATION_90);
+        mMockDisplayLayout.rotateTo(mContext.getResources(), Surface.ROTATION_90);
         mSpiedTransitionState.setState(STATE_NONE);
-        when(mDisplayLayout.isLandscape()).thenReturn(true);
+        when(mMockDisplayLayout.isLandscape()).thenReturn(true);
         mSpiedOneHandedController.setOneHandedEnabled(true);
         mSpiedOneHandedController.setLockedDisabled(false /* locked */, false /* enabled */);
         mSpiedOneHandedController.startOneHanded();
@@ -320,10 +337,10 @@
 
     @Test
     public void testRotation180CanStartOneHanded() {
-        mDisplayLayout.rotateTo(mContext.getResources(), Surface.ROTATION_180);
+        mMockDisplayLayout.rotateTo(mContext.getResources(), Surface.ROTATION_180);
         mSpiedTransitionState.setState(STATE_NONE);
         when(mMockDisplayAreaOrganizer.isReady()).thenReturn(true);
-        when(mDisplayLayout.isLandscape()).thenReturn(false);
+        when(mMockDisplayLayout.isLandscape()).thenReturn(false);
         mSpiedOneHandedController.setOneHandedEnabled(true);
         mSpiedOneHandedController.setLockedDisabled(false /* locked */, false /* enabled */);
         mSpiedOneHandedController.startOneHanded();
@@ -333,9 +350,9 @@
 
     @Test
     public void testRotation270CanNotStartOneHanded() {
-        mDisplayLayout.rotateTo(mContext.getResources(), Surface.ROTATION_270);
+        mMockDisplayLayout.rotateTo(mContext.getResources(), Surface.ROTATION_270);
         mSpiedTransitionState.setState(STATE_NONE);
-        when(mDisplayLayout.isLandscape()).thenReturn(true);
+        when(mMockDisplayLayout.isLandscape()).thenReturn(true);
         mSpiedOneHandedController.setOneHandedEnabled(true);
         mSpiedOneHandedController.setLockedDisabled(false /* locked */, false /* enabled */);
         mSpiedOneHandedController.startOneHanded();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedStateTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedStateTest.java
index e6a8220..a39bdf0 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedStateTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedStateTest.java
@@ -41,7 +41,9 @@
 import com.android.wm.shell.common.DisplayLayout;
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.TaskStackListenerImpl;
+import com.android.wm.shell.sysui.ShellCommandHandler;
 import com.android.wm.shell.sysui.ShellController;
+import com.android.wm.shell.sysui.ShellInit;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -61,6 +63,10 @@
     OneHandedState mSpiedState;
 
     @Mock
+    ShellInit mMockShellInit;
+    @Mock
+    ShellCommandHandler mMockShellCommandHandler;
+    @Mock
     ShellController mMockShellController;
     @Mock
     DisplayController mMockDisplayController;
@@ -111,6 +117,8 @@
         mOneHandedAccessibilityUtil = new OneHandedAccessibilityUtil(mContext);
         mSpiedOneHandedController = spy(new OneHandedController(
                 mContext,
+                mMockShellInit,
+                mMockShellCommandHandler,
                 mMockShellController,
                 mMockDisplayController,
                 mMockDisplayAreaOrganizer,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
index 857f578..579638d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
@@ -21,13 +21,13 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyFloat;
 import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.Mockito.doNothing;
+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.verify;
 
 import android.app.ActivityManager;
@@ -70,7 +70,7 @@
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper
 public class PipTaskOrganizerTest extends ShellTestCase {
-    private PipTaskOrganizer mSpiedPipTaskOrganizer;
+    private PipTaskOrganizer mPipTaskOrganizer;
 
     @Mock private DisplayController mMockDisplayController;
     @Mock private SyncTransactionQueue mMockSyncTransactionQueue;
@@ -100,14 +100,15 @@
         mPipBoundsAlgorithm = new PipBoundsAlgorithm(mContext, mPipBoundsState,
                 new PipSnapAlgorithm());
         mMainExecutor = new TestShellExecutor();
-        mSpiedPipTaskOrganizer = spy(new PipTaskOrganizer(mContext,
+        mPipTaskOrganizer = new PipTaskOrganizer(mContext,
                 mMockSyncTransactionQueue, mPipTransitionState, mPipBoundsState,
                 mPipBoundsAlgorithm, mMockPhonePipMenuController, mMockPipAnimationController,
                 mMockPipSurfaceTransactionHelper, mMockPipTransitionController,
                 mMockPipParamsChangedForwarder, mMockOptionalSplitScreen, mMockDisplayController,
-                mMockPipUiEventLogger, mMockShellTaskOrganizer, mMainExecutor));
+                mMockPipUiEventLogger, mMockShellTaskOrganizer, mMainExecutor);
         mMainExecutor.flushAll();
         preparePipTaskOrg();
+        preparePipSurfaceTransactionHelper();
     }
 
     @Test
@@ -124,14 +125,14 @@
     public void startSwipePipToHome_updatesAspectRatio() {
         final Rational aspectRatio = new Rational(2, 1);
 
-        mSpiedPipTaskOrganizer.startSwipePipToHome(mComponent1, null, createPipParams(aspectRatio));
+        mPipTaskOrganizer.startSwipePipToHome(mComponent1, null, createPipParams(aspectRatio));
 
         assertEquals(aspectRatio.floatValue(), mPipBoundsState.getAspectRatio(), 0.01f);
     }
 
     @Test
     public void startSwipePipToHome_updatesLastPipComponentName() {
-        mSpiedPipTaskOrganizer.startSwipePipToHome(mComponent1, null, createPipParams(null));
+        mPipTaskOrganizer.startSwipePipToHome(mComponent1, null, createPipParams(null));
 
         assertEquals(mComponent1, mPipBoundsState.getLastPipComponentName());
     }
@@ -140,7 +141,7 @@
     public void startSwipePipToHome_updatesOverrideMinSize() {
         final Size minSize = new Size(400, 320);
 
-        mSpiedPipTaskOrganizer.startSwipePipToHome(mComponent1, createActivityInfo(minSize),
+        mPipTaskOrganizer.startSwipePipToHome(mComponent1, createActivityInfo(minSize),
                 createPipParams(null));
 
         assertEquals(minSize, mPipBoundsState.getOverrideMinSize());
@@ -150,7 +151,7 @@
     public void onTaskAppeared_updatesAspectRatio() {
         final Rational aspectRatio = new Rational(2, 1);
 
-        mSpiedPipTaskOrganizer.onTaskAppeared(createTaskInfo(mComponent1,
+        mPipTaskOrganizer.onTaskAppeared(createTaskInfo(mComponent1,
                 createPipParams(aspectRatio)), mock(SurfaceControl.class));
 
         assertEquals(aspectRatio.floatValue(), mPipBoundsState.getAspectRatio(), 0.01f);
@@ -158,7 +159,7 @@
 
     @Test
     public void onTaskAppeared_updatesLastPipComponentName() {
-        mSpiedPipTaskOrganizer.onTaskAppeared(createTaskInfo(mComponent1, createPipParams(null)),
+        mPipTaskOrganizer.onTaskAppeared(createTaskInfo(mComponent1, createPipParams(null)),
                 mock(SurfaceControl.class));
 
         assertEquals(mComponent1, mPipBoundsState.getLastPipComponentName());
@@ -168,7 +169,7 @@
     public void onTaskAppeared_updatesOverrideMinSize() {
         final Size minSize = new Size(400, 320);
 
-        mSpiedPipTaskOrganizer.onTaskAppeared(
+        mPipTaskOrganizer.onTaskAppeared(
                 createTaskInfo(mComponent1, createPipParams(null), minSize),
                 mock(SurfaceControl.class));
 
@@ -179,16 +180,16 @@
     public void onTaskInfoChanged_notInPip_deferUpdatesAspectRatio() {
         final Rational startAspectRatio = new Rational(2, 1);
         final Rational newAspectRatio = new Rational(1, 2);
-        mSpiedPipTaskOrganizer.onTaskAppeared(createTaskInfo(mComponent1,
+        mPipTaskOrganizer.onTaskAppeared(createTaskInfo(mComponent1,
                 createPipParams(startAspectRatio)), mock(SurfaceControl.class));
 
         // It is in entering transition, should defer onTaskInfoChanged callback in this case.
-        mSpiedPipTaskOrganizer.onTaskInfoChanged(createTaskInfo(mComponent1,
+        mPipTaskOrganizer.onTaskInfoChanged(createTaskInfo(mComponent1,
                 createPipParams(newAspectRatio)));
         verify(mMockPipParamsChangedForwarder, never()).notifyAspectRatioChanged(anyFloat());
 
         // Once the entering transition finishes, the new aspect ratio applies in a deferred manner
-        mSpiedPipTaskOrganizer.sendOnPipTransitionFinished(TRANSITION_DIRECTION_TO_PIP);
+        sendOnPipTransitionFinished(TRANSITION_DIRECTION_TO_PIP);
         verify(mMockPipParamsChangedForwarder)
                 .notifyAspectRatioChanged(newAspectRatio.floatValue());
     }
@@ -197,11 +198,11 @@
     public void onTaskInfoChanged_inPip_updatesAspectRatioIfChanged() {
         final Rational startAspectRatio = new Rational(2, 1);
         final Rational newAspectRatio = new Rational(1, 2);
-        mSpiedPipTaskOrganizer.onTaskAppeared(createTaskInfo(mComponent1,
+        mPipTaskOrganizer.onTaskAppeared(createTaskInfo(mComponent1,
                 createPipParams(startAspectRatio)), mock(SurfaceControl.class));
-        mSpiedPipTaskOrganizer.sendOnPipTransitionFinished(TRANSITION_DIRECTION_TO_PIP);
+        sendOnPipTransitionFinished(TRANSITION_DIRECTION_TO_PIP);
 
-        mSpiedPipTaskOrganizer.onTaskInfoChanged(createTaskInfo(mComponent1,
+        mPipTaskOrganizer.onTaskInfoChanged(createTaskInfo(mComponent1,
                 createPipParams(newAspectRatio)));
 
         verify(mMockPipParamsChangedForwarder)
@@ -210,11 +211,11 @@
 
     @Test
     public void onTaskInfoChanged_inPip_updatesLastPipComponentName() {
-        mSpiedPipTaskOrganizer.onTaskAppeared(createTaskInfo(mComponent1,
+        mPipTaskOrganizer.onTaskAppeared(createTaskInfo(mComponent1,
                 createPipParams(null)), mock(SurfaceControl.class));
-        mSpiedPipTaskOrganizer.sendOnPipTransitionFinished(TRANSITION_DIRECTION_TO_PIP);
+        sendOnPipTransitionFinished(TRANSITION_DIRECTION_TO_PIP);
 
-        mSpiedPipTaskOrganizer.onTaskInfoChanged(createTaskInfo(mComponent2,
+        mPipTaskOrganizer.onTaskInfoChanged(createTaskInfo(mComponent2,
                 createPipParams(null)));
 
         assertEquals(mComponent2, mPipBoundsState.getLastPipComponentName());
@@ -222,12 +223,12 @@
 
     @Test
     public void onTaskInfoChanged_inPip_updatesOverrideMinSize() {
-        mSpiedPipTaskOrganizer.onTaskAppeared(createTaskInfo(mComponent1,
+        mPipTaskOrganizer.onTaskAppeared(createTaskInfo(mComponent1,
                 createPipParams(null)), mock(SurfaceControl.class));
-        mSpiedPipTaskOrganizer.sendOnPipTransitionFinished(TRANSITION_DIRECTION_TO_PIP);
+        sendOnPipTransitionFinished(TRANSITION_DIRECTION_TO_PIP);
 
         final Size minSize = new Size(400, 320);
-        mSpiedPipTaskOrganizer.onTaskInfoChanged(createTaskInfo(mComponent2,
+        mPipTaskOrganizer.onTaskInfoChanged(createTaskInfo(mComponent2,
                 createPipParams(null), minSize));
 
         assertEquals(minSize, mPipBoundsState.getOverrideMinSize());
@@ -235,23 +236,42 @@
 
     @Test
     public void onTaskVanished_clearsPipBounds() {
-        mSpiedPipTaskOrganizer.onTaskAppeared(createTaskInfo(mComponent1,
+        mPipTaskOrganizer.onTaskAppeared(createTaskInfo(mComponent1,
                 createPipParams(null)), mock(SurfaceControl.class));
         mPipBoundsState.setBounds(new Rect(100, 100, 200, 150));
 
-        mSpiedPipTaskOrganizer.onTaskVanished(createTaskInfo(mComponent1, createPipParams(null)));
+        mPipTaskOrganizer.onTaskVanished(createTaskInfo(mComponent1, createPipParams(null)));
         assertTrue(mPipBoundsState.getBounds().isEmpty());
     }
 
+    private void sendOnPipTransitionFinished(
+            @PipAnimationController.TransitionDirection int direction) {
+        mPipTaskOrganizer.sendOnPipTransitionFinished(direction);
+        // PipTransitionController will call back into PipTaskOrganizer.
+        mPipTaskOrganizer.mPipTransitionCallback.onPipTransitionFinished(direction);
+    }
+
     private void preparePipTaskOrg() {
         final DisplayInfo info = new DisplayInfo();
         mPipBoundsState.setDisplayLayout(new DisplayLayout(info,
                 mContext.getResources(), true, true));
-        mSpiedPipTaskOrganizer.setOneShotAnimationType(PipAnimationController.ANIM_TYPE_ALPHA);
-        mSpiedPipTaskOrganizer.setSurfaceControlTransactionFactory(
+        mPipTaskOrganizer.setOneShotAnimationType(PipAnimationController.ANIM_TYPE_ALPHA);
+        mPipTaskOrganizer.setSurfaceControlTransactionFactory(
                 MockSurfaceControlHelper::createMockSurfaceControlTransaction);
-        doNothing().when(mSpiedPipTaskOrganizer).enterPipWithAlphaAnimation(any(), anyLong());
-        doNothing().when(mSpiedPipTaskOrganizer).scheduleAnimateResizePip(any(), anyInt(), any());
+    }
+
+    private void preparePipSurfaceTransactionHelper() {
+        doReturn(mMockPipSurfaceTransactionHelper).when(mMockPipSurfaceTransactionHelper)
+                .crop(any(), any(), any());
+        doReturn(mMockPipSurfaceTransactionHelper).when(mMockPipSurfaceTransactionHelper)
+                .resetScale(any(), any(), any());
+        doReturn(mMockPipSurfaceTransactionHelper).when(mMockPipSurfaceTransactionHelper)
+                .round(any(), any(), anyBoolean());
+        doReturn(mMockPipSurfaceTransactionHelper).when(mMockPipSurfaceTransactionHelper)
+                .scale(any(), any(), any(), any(), anyFloat());
+        doReturn(mMockPipSurfaceTransactionHelper).when(mMockPipSurfaceTransactionHelper)
+                .alpha(any(), any(), anyFloat());
+        doNothing().when(mMockPipSurfaceTransactionHelper).onDensityOrFontScaleChanged(any());
     }
 
     private static ActivityManager.RunningTaskInfo createTaskInfo(
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
index f192514..9ed8d84 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
@@ -55,7 +55,9 @@
 import com.android.wm.shell.pip.PipTaskOrganizer;
 import com.android.wm.shell.pip.PipTransitionController;
 import com.android.wm.shell.pip.PipTransitionState;
+import com.android.wm.shell.sysui.ShellCommandHandler;
 import com.android.wm.shell.sysui.ShellController;
+import com.android.wm.shell.sysui.ShellInit;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -74,7 +76,9 @@
 @TestableLooper.RunWithLooper
 public class PipControllerTest extends ShellTestCase {
     private PipController mPipController;
+    private ShellInit mShellInit;
 
+    @Mock private ShellCommandHandler mMockShellCommandHandler;
     @Mock private ShellController mMockShellController;
     @Mock private DisplayController mMockDisplayController;
     @Mock private PhonePipMenuController mMockPhonePipMenuController;
@@ -105,19 +109,31 @@
             ((Runnable) invocation.getArgument(0)).run();
             return null;
         }).when(mMockExecutor).execute(any());
-        mPipController = new PipController(mContext, mMockShellController, mMockDisplayController,
-                mMockPipAppOpsListener, mMockPipBoundsAlgorithm,
-                mMockPipKeepClearAlgorithm,
+        mShellInit = spy(new ShellInit(mMockExecutor));
+        mPipController = new PipController(mContext, mShellInit, mMockShellCommandHandler,
+                mMockShellController, mMockDisplayController, mMockPipAppOpsListener,
+                mMockPipBoundsAlgorithm, mMockPipKeepClearAlgorithm,
                 mMockPipBoundsState, mMockPipMotionHelper, mMockPipMediaController,
                 mMockPhonePipMenuController, mMockPipTaskOrganizer, mMockPipTransitionState,
                 mMockPipTouchHandler, mMockPipTransitionController, mMockWindowManagerShellWrapper,
                 mMockTaskStackListener, mPipParamsChangedForwarder,
                 mMockOneHandedController, mMockExecutor);
+        mShellInit.init();
         when(mMockPipBoundsAlgorithm.getSnapAlgorithm()).thenReturn(mMockPipSnapAlgorithm);
         when(mMockPipTouchHandler.getMotionHelper()).thenReturn(mMockPipMotionHelper);
     }
 
     @Test
+    public void instantiatePipController_addInitCallback() {
+        verify(mShellInit, times(1)).addInitCallback(any(), any());
+    }
+
+    @Test
+    public void instantiateController_registerDumpCallback() {
+        verify(mMockShellCommandHandler, times(1)).addDumpCallback(any(), any());
+    }
+
+    @Test
     public void instantiatePipController_registerConfigChangeListener() {
         verify(mMockShellController, times(1)).addConfigurationChangeListener(any());
     }
@@ -149,9 +165,10 @@
         when(mockPackageManager.hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)).thenReturn(false);
         when(spyContext.getPackageManager()).thenReturn(mockPackageManager);
 
-        assertNull(PipController.create(spyContext, mMockShellController, mMockDisplayController,
-                mMockPipAppOpsListener, mMockPipBoundsAlgorithm,
-                mMockPipKeepClearAlgorithm,
+        ShellInit shellInit = new ShellInit(mMockExecutor);
+        assertNull(PipController.create(spyContext, shellInit, mMockShellCommandHandler,
+                mMockShellController, mMockDisplayController, mMockPipAppOpsListener,
+                mMockPipBoundsAlgorithm, mMockPipKeepClearAlgorithm,
                 mMockPipBoundsState, mMockPipMotionHelper, mMockPipMediaController,
                 mMockPhonePipMenuController, mMockPipTaskOrganizer, mMockPipTransitionState,
                 mMockPipTouchHandler, mMockPipTransitionController, mMockWindowManagerShellWrapper,
@@ -217,7 +234,7 @@
         mPipController.mDisplaysChangedListener.onDisplayConfigurationChanged(
                 displayId, new Configuration());
 
-        verify(mMockPipMotionHelper).movePip(any(Rect.class));
+        verify(mMockPipTaskOrganizer).scheduleFinishResizePip(any(Rect.class));
     }
 
     @Test
@@ -233,7 +250,7 @@
         mPipController.mDisplaysChangedListener.onDisplayConfigurationChanged(
                 displayId, new Configuration());
 
-        verify(mMockPipMotionHelper, never()).movePip(any(Rect.class));
+        verify(mMockPipTaskOrganizer, never()).scheduleFinishResizePip(any(Rect.class));
     }
 
     @Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipBoundsControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipBoundsControllerTest.kt
index 05e4722..cc51efd 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipBoundsControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipBoundsControllerTest.kt
@@ -179,6 +179,16 @@
     }
 
     @Test
+    fun testImmediatePlacement_DoNotStashIfAlreadyUnstashed() {
+        triggerImmediatePlacement(STASHED_PLACEMENT_RESTASH)
+        assertMovement(STASHED_BOUNDS)
+        assertMovementAt(time + STASH_DURATION, ANCHOR_BOUNDS)
+
+        triggerImmediatePlacement(STASHED_PLACEMENT)
+        assertNoMovementUpTo(time + FAR_FUTURE)
+    }
+
+    @Test
     fun testInMoveMode_KeepAtAnchor() {
         startMoveMode()
         triggerImmediatePlacement(STASHED_MOVED_PLACEMENT_RESTASH)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
index d406a4e..81bb609 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
@@ -23,7 +23,9 @@
 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.anyInt;
+import static org.mockito.ArgumentMatchers.isA;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.reset;
@@ -48,6 +50,7 @@
 import com.android.wm.shell.TestShellExecutor;
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.TaskStackListenerImpl;
+import com.android.wm.shell.sysui.ShellCommandHandler;
 import com.android.wm.shell.sysui.ShellInit;
 import com.android.wm.shell.util.GroupedRecentTaskInfo;
 import com.android.wm.shell.util.SplitBounds;
@@ -73,21 +76,35 @@
     @Mock
     private TaskStackListenerImpl mTaskStackListener;
     @Mock
-    private ShellInit mShellInit;
+    private ShellCommandHandler mShellCommandHandler;
 
     private ShellTaskOrganizer mShellTaskOrganizer;
     private RecentTasksController mRecentTasksController;
+    private ShellInit mShellInit;
     private ShellExecutor mMainExecutor;
 
     @Before
     public void setUp() {
         mMainExecutor = new TestShellExecutor();
         when(mContext.getPackageManager()).thenReturn(mock(PackageManager.class));
+        mShellInit = spy(new ShellInit(mMainExecutor));
         mRecentTasksController = spy(new RecentTasksController(mContext, mShellInit,
-                mTaskStackListener, mMainExecutor));
-        mShellTaskOrganizer = new ShellTaskOrganizer(mShellInit,
+                mShellCommandHandler, mTaskStackListener, mMainExecutor));
+        mShellTaskOrganizer = new ShellTaskOrganizer(mShellInit, mShellCommandHandler,
                 null /* sizeCompatUI */, Optional.empty(), Optional.of(mRecentTasksController),
                 mMainExecutor);
+        mShellInit.init();
+    }
+
+    @Test
+    public void instantiateController_addInitCallback() {
+        verify(mShellInit, times(1)).addInitCallback(any(), isA(RecentTasksController.class));
+    }
+
+    @Test
+    public void instantiateController_addDumpCallback() {
+        verify(mShellCommandHandler, times(1)).addDumpCallback(any(),
+                isA(RecentTasksController.class));
     }
 
     @Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
index 10788f9..5a68361 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
@@ -20,12 +20,14 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
 
+import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
 import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
 
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
@@ -53,6 +55,7 @@
 import com.android.wm.shell.common.TransactionPool;
 import com.android.wm.shell.draganddrop.DragAndDropController;
 import com.android.wm.shell.recents.RecentTasksController;
+import com.android.wm.shell.sysui.ShellCommandHandler;
 import com.android.wm.shell.sysui.ShellController;
 import com.android.wm.shell.sysui.ShellInit;
 import com.android.wm.shell.transition.Transitions;
@@ -72,8 +75,9 @@
 @RunWith(AndroidJUnit4.class)
 public class SplitScreenControllerTests extends ShellTestCase {
 
-    @Mock ShellController mShellController;
     @Mock ShellInit mShellInit;
+    @Mock ShellController mShellController;
+    @Mock ShellCommandHandler mShellCommandHandler;
     @Mock ShellTaskOrganizer mTaskOrganizer;
     @Mock SyncTransactionQueue mSyncQueue;
     @Mock RootTaskDisplayAreaOrganizer mRootTDAOrganizer;
@@ -93,9 +97,10 @@
     public void setup() {
         MockitoAnnotations.initMocks(this);
         mSplitScreenController = spy(new SplitScreenController(mContext, mShellInit,
-                mShellController, mTaskOrganizer, mSyncQueue, mRootTDAOrganizer, mDisplayController,
-                mDisplayImeController, mDisplayInsetsController, mDragAndDropController,
-                mTransitions, mTransactionPool, mIconProvider, mRecentTasks, mMainExecutor));
+                mShellCommandHandler, mShellController, mTaskOrganizer, mSyncQueue,
+                mRootTDAOrganizer, mDisplayController, mDisplayImeController,
+                mDisplayInsetsController, mDragAndDropController, mTransitions, mTransactionPool,
+                mIconProvider, mRecentTasks, mMainExecutor));
     }
 
     @Test
@@ -104,14 +109,31 @@
     }
 
     @Test
+    public void instantiateController_registerDumpCallback() {
+        doReturn(mMainExecutor).when(mTaskOrganizer).getExecutor();
+        when(mDisplayController.getDisplayLayout(anyInt())).thenReturn(new DisplayLayout());
+        mSplitScreenController.onInit();
+        verify(mShellCommandHandler, times(1)).addDumpCallback(any(), any());
+    }
+
+    @Test
+    public void instantiateController_registerCommandCallback() {
+        doReturn(mMainExecutor).when(mTaskOrganizer).getExecutor();
+        when(mDisplayController.getDisplayLayout(anyInt())).thenReturn(new DisplayLayout());
+        mSplitScreenController.onInit();
+        verify(mShellCommandHandler, times(1)).addCommandCallback(eq("splitscreen"), any(), any());
+    }
+
+    @Test
     public void testControllerRegistersKeyguardChangeListener() {
+        doReturn(mMainExecutor).when(mTaskOrganizer).getExecutor();
         when(mDisplayController.getDisplayLayout(anyInt())).thenReturn(new DisplayLayout());
         mSplitScreenController.onInit();
         verify(mShellController, times(1)).addKeyguardChangeListener(any());
     }
 
     @Test
-    public void testIsLaunchingAdjacently_notInSplitScreen() {
+    public void testShouldAddMultipleTaskFlag_notInSplitScreen() {
         doReturn(false).when(mSplitScreenController).isSplitScreenVisible();
         doReturn(true).when(mSplitScreenController).isValidToEnterSplitScreen(any());
 
@@ -120,7 +142,7 @@
         ActivityManager.RunningTaskInfo focusTaskInfo =
                 createTaskInfo(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, startIntent);
         doReturn(focusTaskInfo).when(mSplitScreenController).getFocusingTaskInfo();
-        assertTrue(mSplitScreenController.isLaunchingAdjacently(
+        assertTrue(mSplitScreenController.shouldAddMultipleTaskFlag(
                 startIntent, SPLIT_POSITION_TOP_OR_LEFT));
 
         // Verify launching different activity returns false.
@@ -128,28 +150,40 @@
         focusTaskInfo =
                 createTaskInfo(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, diffIntent);
         doReturn(focusTaskInfo).when(mSplitScreenController).getFocusingTaskInfo();
-        assertFalse(mSplitScreenController.isLaunchingAdjacently(
+        assertFalse(mSplitScreenController.shouldAddMultipleTaskFlag(
                 startIntent, SPLIT_POSITION_TOP_OR_LEFT));
     }
 
     @Test
-    public void testIsLaunchingAdjacently_inSplitScreen() {
+    public void testShouldAddMultipleTaskFlag_inSplitScreen() {
         doReturn(true).when(mSplitScreenController).isSplitScreenVisible();
-
-        // Verify launching the same activity returns true.
         Intent startIntent = createStartIntent("startActivity");
-        ActivityManager.RunningTaskInfo pairingTaskInfo =
+        ActivityManager.RunningTaskInfo sameTaskInfo =
                 createTaskInfo(WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD, startIntent);
-        doReturn(pairingTaskInfo).when(mSplitScreenController).getTaskInfo(anyInt());
-        assertTrue(mSplitScreenController.isLaunchingAdjacently(
+        Intent diffIntent = createStartIntent("diffActivity");
+        ActivityManager.RunningTaskInfo differentTaskInfo =
+                createTaskInfo(WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD, diffIntent);
+
+        // Verify launching the same activity return false.
+        doReturn(sameTaskInfo).when(mSplitScreenController)
+                .getTaskInfo(SPLIT_POSITION_TOP_OR_LEFT);
+        assertFalse(mSplitScreenController.shouldAddMultipleTaskFlag(
                 startIntent, SPLIT_POSITION_TOP_OR_LEFT));
 
-        // Verify launching different activity returns false.
-        Intent diffIntent = createStartIntent("diffActivity");
-        pairingTaskInfo =
-                createTaskInfo(WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD, diffIntent);
-        doReturn(pairingTaskInfo).when(mSplitScreenController).getTaskInfo(anyInt());
-        assertFalse(mSplitScreenController.isLaunchingAdjacently(
+        // Verify launching the same activity as adjacent returns true.
+        doReturn(differentTaskInfo).when(mSplitScreenController)
+                .getTaskInfo(SPLIT_POSITION_TOP_OR_LEFT);
+        doReturn(sameTaskInfo).when(mSplitScreenController)
+                .getTaskInfo(SPLIT_POSITION_BOTTOM_OR_RIGHT);
+        assertTrue(mSplitScreenController.shouldAddMultipleTaskFlag(
+                startIntent, SPLIT_POSITION_TOP_OR_LEFT));
+
+        // Verify launching different activity from adjacent returns false.
+        doReturn(differentTaskInfo).when(mSplitScreenController)
+                .getTaskInfo(SPLIT_POSITION_TOP_OR_LEFT);
+        doReturn(differentTaskInfo).when(mSplitScreenController)
+                .getTaskInfo(SPLIT_POSITION_BOTTOM_OR_RIGHT);
+        assertFalse(mSplitScreenController.shouldAddMultipleTaskFlag(
                 startIntent, SPLIT_POSITION_TOP_OR_LEFT));
     }
 
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sysui/ShellControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sysui/ShellControllerTest.java
index 02311ba..39e58ff 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sysui/ShellControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sysui/ShellControllerTest.java
@@ -45,6 +45,8 @@
     @Mock
     private ShellInit mShellInit;
     @Mock
+    private ShellCommandHandler mShellCommandHandler;
+    @Mock
     private ShellExecutor mExecutor;
 
     private ShellController mController;
@@ -56,7 +58,7 @@
         MockitoAnnotations.initMocks(this);
         mKeyguardChangeListener = new TestKeyguardChangeListener();
         mConfigChangeListener = new TestConfigurationChangeListener();
-        mController = new ShellController(mShellInit, mExecutor);
+        mController = new ShellController(mShellInit, mShellCommandHandler, mExecutor);
         mController.onConfigurationChanged(getConfigurationCopy());
     }
 
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
index b318bb2..226843e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
@@ -204,6 +204,8 @@
         verify(mMockSurfaceControlStartT)
                 .setColor(taskBackgroundSurface, new float[] {1.f, 1.f, 0.f});
         verify(mMockSurfaceControlStartT).setShadowRadius(taskBackgroundSurface, 10);
+        verify(mMockSurfaceControlStartT).setLayer(taskBackgroundSurface, -1);
+        verify(mMockSurfaceControlStartT).show(taskBackgroundSurface);
 
         verify(mMockSurfaceControlViewHostFactory)
                 .create(any(), eq(defaultDisplay), any(), anyBoolean());
@@ -233,6 +235,57 @@
     }
 
     @Test
+    public void testLayoutResultCalculation_visibleFocusedTaskToInvisible() {
+        final Display defaultDisplay = mock(Display.class);
+        doReturn(defaultDisplay).when(mMockDisplayController)
+                .getDisplay(Display.DEFAULT_DISPLAY);
+
+        final SurfaceControl decorContainerSurface = mock(SurfaceControl.class);
+        final SurfaceControl.Builder decorContainerSurfaceBuilder =
+                createMockSurfaceControlBuilder(decorContainerSurface);
+        mMockSurfaceControlBuilders.add(decorContainerSurfaceBuilder);
+        final SurfaceControl taskBackgroundSurface = mock(SurfaceControl.class);
+        final SurfaceControl.Builder taskBackgroundSurfaceBuilder =
+                createMockSurfaceControlBuilder(taskBackgroundSurface);
+        mMockSurfaceControlBuilders.add(taskBackgroundSurfaceBuilder);
+
+        final ActivityManager.TaskDescription.Builder taskDescriptionBuilder =
+                new ActivityManager.TaskDescription.Builder()
+                        .setBackgroundColor(Color.YELLOW);
+        final ActivityManager.RunningTaskInfo taskInfo = new TestRunningTaskInfoBuilder()
+                .setDisplayId(Display.DEFAULT_DISPLAY)
+                .setTaskDescriptionBuilder(taskDescriptionBuilder)
+                .setBounds(TASK_BOUNDS)
+                .setPositionInParent(TASK_POSITION_IN_PARENT.x, TASK_POSITION_IN_PARENT.y)
+                .setVisible(true)
+                .build();
+        taskInfo.isFocused = true;
+        // Density is 2. Outsets are (20, 40, 60, 80) px. Shadow radius is 10px. Caption height is
+        // 64px.
+        taskInfo.configuration.densityDpi = DisplayMetrics.DENSITY_DEFAULT * 2;
+        mOutsetsDp.set(10, 20, 30, 40);
+
+        final SurfaceControl taskSurface = mock(SurfaceControl.class);
+        final TestWindowDecoration windowDecor = createWindowDecoration(taskInfo, taskSurface);
+
+        windowDecor.relayout(taskInfo);
+
+        verify(mMockSurfaceControlViewHost, never()).release();
+        verify(decorContainerSurface, never()).release();
+        verify(taskBackgroundSurface, never()).release();
+        verify(mMockWindowContainerTransaction, never())
+                .removeInsetsProvider(eq(taskInfo.token), any());
+
+        taskInfo.isVisible = false;
+        windowDecor.relayout(taskInfo);
+
+        verify(mMockSurfaceControlViewHost).release();
+        verify(decorContainerSurface).release();
+        verify(taskBackgroundSurface).release();
+        verify(mMockWindowContainerTransaction).removeInsetsProvider(eq(taskInfo.token), any());
+    }
+
+    @Test
     public void testNotCrashWhenDisplayAppearsAfterTask() {
         doReturn(mock(Display.class)).when(mMockDisplayController)
                 .getDisplay(Display.DEFAULT_DISPLAY);
@@ -282,7 +335,7 @@
             ActivityManager.RunningTaskInfo taskInfo, SurfaceControl testSurface) {
         return new TestWindowDecoration(mContext, mMockDisplayController, mMockShellTaskOrganizer,
                 taskInfo, testSurface, new MockSurfaceControlBuilderSupplier(),
-                mMockSurfaceControlViewHostFactory);
+                () -> mMockWindowContainerTransaction, mMockSurfaceControlViewHostFactory);
     }
 
     private class MockSurfaceControlBuilderSupplier implements Supplier<SurfaceControl.Builder> {
@@ -313,9 +366,11 @@
                 ShellTaskOrganizer taskOrganizer, ActivityManager.RunningTaskInfo taskInfo,
                 SurfaceControl taskSurface,
                 Supplier<SurfaceControl.Builder> surfaceControlBuilderSupplier,
+                Supplier<WindowContainerTransaction> windowContainerTransactionSupplier,
                 SurfaceControlViewHostFactory surfaceControlViewHostFactory) {
             super(context, displayController, taskOrganizer, taskInfo, taskSurface,
-                    surfaceControlBuilderSupplier, surfaceControlViewHostFactory);
+                    surfaceControlBuilderSupplier, windowContainerTransactionSupplier,
+                    surfaceControlViewHostFactory);
         }
 
         @Override
diff --git a/libs/hwui/CopyRequest.h b/libs/hwui/CopyRequest.h
new file mode 100644
index 0000000..5fbd5f9
--- /dev/null
+++ b/libs/hwui/CopyRequest.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "Rect.h"
+#include "hwui/Bitmap.h"
+
+namespace android::uirenderer {
+
+// Keep in sync with PixelCopy.java codes
+enum class CopyResult {
+    Success = 0,
+    UnknownError = 1,
+    Timeout = 2,
+    SourceEmpty = 3,
+    SourceInvalid = 4,
+    DestinationInvalid = 5,
+};
+
+struct CopyRequest {
+    Rect srcRect;
+    CopyRequest(Rect srcRect) : srcRect(srcRect) {}
+    virtual ~CopyRequest() {}
+    virtual SkBitmap getDestinationBitmap(int srcWidth, int srcHeight) = 0;
+    virtual void onCopyFinished(CopyResult result) = 0;
+};
+
+}  // namespace android::uirenderer
diff --git a/libs/hwui/Readback.cpp b/libs/hwui/Readback.cpp
index 1191b92..02c2e67 100644
--- a/libs/hwui/Readback.cpp
+++ b/libs/hwui/Readback.cpp
@@ -49,8 +49,7 @@
 
 #define ARECT_ARGS(r) float((r).left), float((r).top), float((r).right), float((r).bottom)
 
-CopyResult Readback::copySurfaceInto(ANativeWindow* window, const Rect& inSrcRect,
-                                     SkBitmap* bitmap) {
+void Readback::copySurfaceInto(ANativeWindow* window, const std::shared_ptr<CopyRequest>& request) {
     ATRACE_CALL();
     // Setup the source
     AHardwareBuffer* rawSourceBuffer;
@@ -63,30 +62,33 @@
     // Really this shouldn't ever happen, but better safe than sorry.
     if (err == UNKNOWN_TRANSACTION) {
         ALOGW("Readback failed to ANativeWindow_getLastQueuedBuffer2 - who are we talking to?");
-        return copySurfaceIntoLegacy(window, inSrcRect, bitmap);
+        return request->onCopyFinished(CopyResult::SourceInvalid);
     }
     ALOGV("Using new path, cropRect=" RECT_STRING ", transform=%x", ARECT_ARGS(cropRect),
           windowTransform);
 
     if (err != NO_ERROR) {
         ALOGW("Failed to get last queued buffer, error = %d", err);
-        return CopyResult::UnknownError;
+        return request->onCopyFinished(CopyResult::SourceInvalid);
     }
     if (rawSourceBuffer == nullptr) {
         ALOGW("Surface doesn't have any previously queued frames, nothing to readback from");
-        return CopyResult::SourceEmpty;
+        return request->onCopyFinished(CopyResult::SourceEmpty);
     }
     UniqueAHardwareBuffer sourceBuffer{rawSourceBuffer};
     AHardwareBuffer_Desc description;
     AHardwareBuffer_describe(sourceBuffer.get(), &description);
     if (description.usage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT) {
         ALOGW("Surface is protected, unable to copy from it");
-        return CopyResult::SourceInvalid;
+        return request->onCopyFinished(CopyResult::SourceInvalid);
     }
 
-    if (sourceFence != -1 && sync_wait(sourceFence.get(), 500 /* ms */) != NO_ERROR) {
-        ALOGE("Timeout (500ms) exceeded waiting for buffer fence, abandoning readback attempt");
-        return CopyResult::Timeout;
+    {
+        ATRACE_NAME("sync_wait");
+        if (sourceFence != -1 && sync_wait(sourceFence.get(), 500 /* ms */) != NO_ERROR) {
+            ALOGE("Timeout (500ms) exceeded waiting for buffer fence, abandoning readback attempt");
+            return request->onCopyFinished(CopyResult::Timeout);
+        }
     }
 
     sk_sp<SkColorSpace> colorSpace = DataSpaceToColorSpace(
@@ -95,12 +97,12 @@
             SkImage::MakeFromAHardwareBuffer(sourceBuffer.get(), kPremul_SkAlphaType, colorSpace);
 
     if (!image.get()) {
-        return CopyResult::UnknownError;
+        return request->onCopyFinished(CopyResult::UnknownError);
     }
 
     sk_sp<GrDirectContext> grContext = mRenderThread.requireGrContext();
 
-    SkRect srcRect = inSrcRect.toSkRect();
+    SkRect srcRect = request->srcRect.toSkRect();
 
     SkRect imageSrcRect = SkRect::MakeIWH(description.width, description.height);
     SkISize imageWH = SkISize::Make(description.width, description.height);
@@ -148,10 +150,12 @@
         ALOGV("intersecting " RECT_STRING " with " RECT_STRING, SK_RECT_ARGS(srcRect),
               SK_RECT_ARGS(textureRect));
         if (!srcRect.intersect(textureRect)) {
-            return CopyResult::UnknownError;
+            return request->onCopyFinished(CopyResult::UnknownError);
         }
     }
 
+    SkBitmap skBitmap = request->getDestinationBitmap(srcRect.width(), srcRect.height());
+    SkBitmap* bitmap = &skBitmap;
     sk_sp<SkSurface> tmpSurface =
             SkSurface::MakeRenderTarget(mRenderThread.getGrContext(), SkBudgeted::kYes,
                                         bitmap->info(), 0, kTopLeft_GrSurfaceOrigin, nullptr);
@@ -164,7 +168,7 @@
                                                  tmpInfo, 0, kTopLeft_GrSurfaceOrigin, nullptr);
         if (!tmpSurface.get()) {
             ALOGW("Unable to generate GPU buffer in a format compatible with the provided bitmap");
-            return CopyResult::UnknownError;
+            return request->onCopyFinished(CopyResult::UnknownError);
         }
     }
 
@@ -235,52 +239,13 @@
             !tmpBitmap.tryAllocPixels(tmpInfo) || !tmpSurface->readPixels(tmpBitmap, 0, 0) ||
             !tmpBitmap.readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(), 0, 0)) {
             ALOGW("Unable to convert content into the provided bitmap");
-            return CopyResult::UnknownError;
+            return request->onCopyFinished(CopyResult::UnknownError);
         }
     }
 
     bitmap->notifyPixelsChanged();
 
-    return CopyResult::Success;
-}
-
-CopyResult Readback::copySurfaceIntoLegacy(ANativeWindow* window, const Rect& srcRect,
-                                           SkBitmap* bitmap) {
-    // Setup the source
-    AHardwareBuffer* rawSourceBuffer;
-    int rawSourceFence;
-    Matrix4 texTransform;
-    status_t err = ANativeWindow_getLastQueuedBuffer(window, &rawSourceBuffer, &rawSourceFence,
-                                                     texTransform.data);
-    base::unique_fd sourceFence(rawSourceFence);
-    texTransform.invalidateType();
-    if (err != NO_ERROR) {
-        ALOGW("Failed to get last queued buffer, error = %d", err);
-        return CopyResult::UnknownError;
-    }
-    if (rawSourceBuffer == nullptr) {
-        ALOGW("Surface doesn't have any previously queued frames, nothing to readback from");
-        return CopyResult::SourceEmpty;
-    }
-
-    UniqueAHardwareBuffer sourceBuffer{rawSourceBuffer};
-    AHardwareBuffer_Desc description;
-    AHardwareBuffer_describe(sourceBuffer.get(), &description);
-    if (description.usage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT) {
-        ALOGW("Surface is protected, unable to copy from it");
-        return CopyResult::SourceInvalid;
-    }
-
-    if (sourceFence != -1 && sync_wait(sourceFence.get(), 500 /* ms */) != NO_ERROR) {
-        ALOGE("Timeout (500ms) exceeded waiting for buffer fence, abandoning readback attempt");
-        return CopyResult::Timeout;
-    }
-
-    sk_sp<SkColorSpace> colorSpace = DataSpaceToColorSpace(
-            static_cast<android_dataspace>(ANativeWindow_getBuffersDataSpace(window)));
-    sk_sp<SkImage> image =
-            SkImage::MakeFromAHardwareBuffer(sourceBuffer.get(), kPremul_SkAlphaType, colorSpace);
-    return copyImageInto(image, srcRect, bitmap);
+    return request->onCopyFinished(CopyResult::Success);
 }
 
 CopyResult Readback::copyHWBitmapInto(Bitmap* hwBitmap, SkBitmap* bitmap) {
@@ -318,14 +283,14 @@
 CopyResult Readback::copyImageInto(const sk_sp<SkImage>& image, const Rect& srcRect,
                                    SkBitmap* bitmap) {
     ATRACE_CALL();
+    if (!image.get()) {
+        return CopyResult::UnknownError;
+    }
     if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaGL) {
         mRenderThread.requireGlContext();
     } else {
         mRenderThread.requireVkContext();
     }
-    if (!image.get()) {
-        return CopyResult::UnknownError;
-    }
     int imgWidth = image->width();
     int imgHeight = image->height();
     sk_sp<GrDirectContext> grContext = sk_ref_sp(mRenderThread.getGrContext());
diff --git a/libs/hwui/Readback.h b/libs/hwui/Readback.h
index aa6e43c..a092d47 100644
--- a/libs/hwui/Readback.h
+++ b/libs/hwui/Readback.h
@@ -16,12 +16,13 @@
 
 #pragma once
 
+#include <SkRefCnt.h>
+
+#include "CopyRequest.h"
 #include "Matrix.h"
 #include "Rect.h"
 #include "renderthread/RenderThread.h"
 
-#include <SkRefCnt.h>
-
 class SkBitmap;
 class SkImage;
 struct SkRect;
@@ -35,23 +36,13 @@
 class DeferredLayerUpdater;
 class Layer;
 
-// Keep in sync with PixelCopy.java codes
-enum class CopyResult {
-    Success = 0,
-    UnknownError = 1,
-    Timeout = 2,
-    SourceEmpty = 3,
-    SourceInvalid = 4,
-    DestinationInvalid = 5,
-};
-
 class Readback {
 public:
     explicit Readback(renderthread::RenderThread& thread) : mRenderThread(thread) {}
     /**
      * Copies the surface's most recently queued buffer into the provided bitmap.
      */
-    CopyResult copySurfaceInto(ANativeWindow* window, const Rect& srcRect, SkBitmap* bitmap);
+    void copySurfaceInto(ANativeWindow* window, const std::shared_ptr<CopyRequest>& request);
 
     CopyResult copyHWBitmapInto(Bitmap* hwBitmap, SkBitmap* bitmap);
     CopyResult copyImageInto(const sk_sp<SkImage>& image, SkBitmap* bitmap);
@@ -59,7 +50,6 @@
     CopyResult copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap);
 
 private:
-    CopyResult copySurfaceIntoLegacy(ANativeWindow* window, const Rect& srcRect, SkBitmap* bitmap);
     CopyResult copyImageInto(const sk_sp<SkImage>& image, const Rect& srcRect, SkBitmap* bitmap);
 
     bool copyLayerInto(Layer* layer, const SkRect* srcRect, const SkRect* dstRect,
diff --git a/libs/hwui/jni/Typeface.cpp b/libs/hwui/jni/Typeface.cpp
index 6325226..f5ed568 100644
--- a/libs/hwui/jni/Typeface.cpp
+++ b/libs/hwui/jni/Typeface.cpp
@@ -140,15 +140,13 @@
 
 static jobject Typeface_getSupportedAxes(JNIEnv *env, jobject, jlong faceHandle) {
     Typeface* face = toTypeface(faceHandle);
-    const std::unordered_set<minikin::AxisTag>& tagSet = face->fFontCollection->getSupportedTags();
-    const size_t length = tagSet.size();
+    const size_t length = face->fFontCollection->getSupportedAxesCount();
     if (length == 0) {
         return nullptr;
     }
     std::vector<jint> tagVec(length);
-    int index = 0;
-    for (const auto& tag : tagSet) {
-        tagVec[index++] = tag;
+    for (size_t i = 0; i < length; i++) {
+        tagVec[i] = face->fFontCollection->getSupportedAxisAt(i);
     }
     std::sort(tagVec.begin(), tagVec.end());
     const jintArray result = env->NewIntArray(length);
@@ -382,18 +380,6 @@
     env->SetStaticObjectField(cls, fid, typeface);
 }
 
-// Critical Native
-static jint Typeface_getFamilySize(CRITICAL_JNI_PARAMS_COMMA jlong faceHandle) {
-    return toTypeface(faceHandle)->fFontCollection->getFamilyCount();
-}
-
-// Critical Native
-static jlong Typeface_getFamily(CRITICAL_JNI_PARAMS_COMMA jlong faceHandle, jint index) {
-    std::shared_ptr<minikin::FontFamily> family =
-            toTypeface(faceHandle)->fFontCollection->getFamilyAt(index);
-    return reinterpret_cast<jlong>(new FontFamilyWrapper(std::move(family)));
-}
-
 // Regular JNI
 static void Typeface_warmUpCache(JNIEnv* env, jobject, jstring jFilePath) {
     ScopedUtfChars filePath(env, jFilePath);
@@ -433,8 +419,6 @@
         {"nativeReadTypefaces", "(Ljava/nio/ByteBuffer;I)[J", (void*)Typeface_readTypefaces},
         {"nativeForceSetStaticFinalField", "(Ljava/lang/String;Landroid/graphics/Typeface;)V",
          (void*)Typeface_forceSetStaticFinalField},
-        {"nativeGetFamilySize", "(J)I", (void*)Typeface_getFamilySize},
-        {"nativeGetFamily", "(JI)J", (void*)Typeface_getFamily},
         {"nativeWarmUpCache", "(Ljava/lang/String;)V", (void*)Typeface_warmUpCache},
         {"nativeAddFontCollections", "(J)V", (void*)Typeface_addFontCollection},
         {"nativeRegisterLocaleList", "(Ljava/lang/String;)V", (void*)Typeface_registerLocaleList},
diff --git a/libs/hwui/jni/YuvToJpegEncoder.cpp b/libs/hwui/jni/YuvToJpegEncoder.cpp
index 87eda7e..1c5f126 100644
--- a/libs/hwui/jni/YuvToJpegEncoder.cpp
+++ b/libs/hwui/jni/YuvToJpegEncoder.cpp
@@ -8,6 +8,8 @@
 
 #include "graphics_jni_helpers.h"
 
+#include <csetjmp>
+
 YuvToJpegEncoder* YuvToJpegEncoder::create(int format, int* strides) {
     // Only ImageFormat.NV21 and ImageFormat.YUY2 are supported
     // for now.
diff --git a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
index 55b1f23..4f281fc 100644
--- a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
+++ b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
@@ -88,6 +88,11 @@
     jmethodID onFrameComplete;
 } gFrameCompleteCallback;
 
+struct {
+    jmethodID onCopyFinished;
+    jmethodID getDestinationBitmap;
+} gCopyRequest;
+
 static JNIEnv* getenv(JavaVM* vm) {
     JNIEnv* env;
     if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
@@ -672,15 +677,41 @@
     }
 }
 
-static jint android_view_ThreadedRenderer_copySurfaceInto(JNIEnv* env,
-        jobject clazz, jobject jsurface, jint left, jint top,
-        jint right, jint bottom, jlong bitmapPtr) {
-    SkBitmap bitmap;
-    bitmap::toBitmap(bitmapPtr).getSkBitmap(&bitmap);
+class CopyRequestAdapter : public CopyRequest {
+public:
+    CopyRequestAdapter(JavaVM* vm, jobject jCopyRequest, Rect srcRect)
+            : CopyRequest(srcRect), mRefHolder(vm, jCopyRequest) {}
+
+    virtual SkBitmap getDestinationBitmap(int srcWidth, int srcHeight) override {
+        JNIEnv* env = getenv(mRefHolder.vm());
+        jlong bitmapPtr = env->CallLongMethod(
+                mRefHolder.object(), gCopyRequest.getDestinationBitmap, srcWidth, srcHeight);
+        SkBitmap bitmap;
+        bitmap::toBitmap(bitmapPtr).getSkBitmap(&bitmap);
+        return bitmap;
+    }
+
+    virtual void onCopyFinished(CopyResult result) override {
+        JNIEnv* env = getenv(mRefHolder.vm());
+        env->CallVoidMethod(mRefHolder.object(), gCopyRequest.onCopyFinished,
+                            static_cast<jint>(result));
+    }
+
+private:
+    JGlobalRefHolder mRefHolder;
+};
+
+static void android_view_ThreadedRenderer_copySurfaceInto(JNIEnv* env, jobject clazz,
+                                                          jobject jsurface, jint left, jint top,
+                                                          jint right, jint bottom,
+                                                          jobject jCopyRequest) {
+    JavaVM* vm = nullptr;
+    LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&vm) != JNI_OK, "Unable to get Java VM");
+    auto copyRequest = std::make_shared<CopyRequestAdapter>(vm, env->NewGlobalRef(jCopyRequest),
+                                                            Rect(left, top, right, bottom));
     ANativeWindow* window = fromSurface(env, jsurface);
-    jint result = RenderProxy::copySurfaceInto(window, left, top, right, bottom, &bitmap);
+    RenderProxy::copySurfaceInto(window, std::move(copyRequest));
     ANativeWindow_release(window);
-    return result;
 }
 
 class ContextFactory : public IContextFactory {
@@ -969,7 +1000,8 @@
          (void*)android_view_ThreadedRenderer_setFrameCompleteCallback},
         {"nAddObserver", "(JJ)V", (void*)android_view_ThreadedRenderer_addObserver},
         {"nRemoveObserver", "(JJ)V", (void*)android_view_ThreadedRenderer_removeObserver},
-        {"nCopySurfaceInto", "(Landroid/view/Surface;IIIIJ)I",
+        {"nCopySurfaceInto",
+         "(Landroid/view/Surface;IIIILandroid/graphics/HardwareRenderer$CopyRequest;)V",
          (void*)android_view_ThreadedRenderer_copySurfaceInto},
         {"nCreateHardwareBitmap", "(JII)Landroid/graphics/Bitmap;",
          (void*)android_view_ThreadedRenderer_createHardwareBitmapFromRenderNode},
@@ -1042,6 +1074,11 @@
     gFrameCompleteCallback.onFrameComplete =
             GetMethodIDOrDie(env, frameCompleteClass, "onFrameComplete", "()V");
 
+    jclass copyRequest = FindClassOrDie(env, "android/graphics/HardwareRenderer$CopyRequest");
+    gCopyRequest.onCopyFinished = GetMethodIDOrDie(env, copyRequest, "onCopyFinished", "(I)V");
+    gCopyRequest.getDestinationBitmap =
+            GetMethodIDOrDie(env, copyRequest, "getDestinationBitmap", "(II)J");
+
     void* handle_ = dlopen("libandroid.so", RTLD_NOW | RTLD_NODELETE);
     fromSurface = (ANW_fromSurface)dlsym(handle_, "ANativeWindow_fromSurface");
     LOG_ALWAYS_FATAL_IF(fromSurface == nullptr,
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index b2ba15c..40a0bac 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -364,12 +364,13 @@
     mRenderThread.queue().post([this, enable]() { mContext->setForceDark(enable); });
 }
 
-int RenderProxy::copySurfaceInto(ANativeWindow* window, int left, int top, int right, int bottom,
-                                 SkBitmap* bitmap) {
+void RenderProxy::copySurfaceInto(ANativeWindow* window, std::shared_ptr<CopyRequest>&& request) {
     auto& thread = RenderThread::getInstance();
-    return static_cast<int>(thread.queue().runSync([&]() -> auto {
-        return thread.readback().copySurfaceInto(window, Rect(left, top, right, bottom), bitmap);
-    }));
+    ANativeWindow_acquire(window);
+    thread.queue().post([&thread, window, request = std::move(request)] {
+        thread.readback().copySurfaceInto(window, request);
+        ANativeWindow_release(window);
+    });
 }
 
 void RenderProxy::prepareToDraw(Bitmap& bitmap) {
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index bbfeeac..2a99a73 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -19,13 +19,14 @@
 
 #include <SkRefCnt.h>
 #include <android/native_window.h>
-#include <cutils/compiler.h>
 #include <android/surface_control.h>
+#include <cutils/compiler.h>
 #include <utils/Functor.h>
 
 #include "../FrameMetricsObserver.h"
 #include "../IContextFactory.h"
 #include "ColorMode.h"
+#include "CopyRequest.h"
 #include "DrawFrameTask.h"
 #include "SwapBehavior.h"
 #include "hwui/Bitmap.h"
@@ -137,8 +138,7 @@
     void removeFrameMetricsObserver(FrameMetricsObserver* observer);
     void setForceDark(bool enable);
 
-    static int copySurfaceInto(ANativeWindow* window, int left, int top, int right,
-                                           int bottom, SkBitmap* bitmap);
+    static void copySurfaceInto(ANativeWindow* window, std::shared_ptr<CopyRequest>&& request);
     static void prepareToDraw(Bitmap& bitmap);
 
     static int copyHWBitmapInto(Bitmap* hwBitmap, SkBitmap* bitmap);
diff --git a/libs/hwui/tests/common/scenes/MagnifierAnimation.cpp b/libs/hwui/tests/common/scenes/MagnifierAnimation.cpp
index 070a339..13a4381 100644
--- a/libs/hwui/tests/common/scenes/MagnifierAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/MagnifierAnimation.cpp
@@ -25,17 +25,51 @@
 
 class MagnifierAnimation;
 
+using Rect = android::uirenderer::Rect;
+
 static TestScene::Registrar _Magnifier(TestScene::Info{
         "magnifier", "A sample magnifier using Readback",
         TestScene::simpleCreateScene<MagnifierAnimation>});
 
+class BlockingCopyRequest : public CopyRequest {
+    sk_sp<Bitmap> mDestination;
+    std::mutex mLock;
+    std::condition_variable mCondVar;
+    CopyResult mResult;
+
+public:
+    BlockingCopyRequest(::Rect rect, sk_sp<Bitmap> bitmap)
+            : CopyRequest(rect), mDestination(bitmap) {}
+
+    virtual SkBitmap getDestinationBitmap(int srcWidth, int srcHeight) override {
+        SkBitmap bitmap;
+        mDestination->getSkBitmap(&bitmap);
+        return bitmap;
+    }
+
+    virtual void onCopyFinished(CopyResult result) override {
+        std::unique_lock _lock{mLock};
+        mResult = result;
+        mCondVar.notify_all();
+    }
+
+    CopyResult waitForResult() {
+        std::unique_lock _lock{mLock};
+        mCondVar.wait(_lock);
+        return mResult;
+    }
+};
+
 class MagnifierAnimation : public TestScene {
 public:
     sp<RenderNode> card;
     sp<RenderNode> zoomImageView;
+    sk_sp<Bitmap> magnifier;
+    std::shared_ptr<BlockingCopyRequest> copyRequest;
 
     void createContent(int width, int height, Canvas& canvas) override {
         magnifier = TestUtils::createBitmap(200, 100);
+        setupCopyRequest();
         SkBitmap temp;
         magnifier->getSkBitmap(&temp);
         temp.eraseColor(Color::White);
@@ -65,19 +99,20 @@
         canvas.enableZ(false);
     }
 
+    void setupCopyRequest() {
+        constexpr int x = 90;
+        constexpr int y = 325;
+        copyRequest = std::make_shared<BlockingCopyRequest>(
+                ::Rect(x, y, x + magnifier->width(), y + magnifier->height()), magnifier);
+    }
+
     void doFrame(int frameNr) override {
         int curFrame = frameNr % 150;
         card->mutateStagingProperties().setTranslationX(curFrame);
         card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
         if (renderTarget) {
-            SkBitmap temp;
-            magnifier->getSkBitmap(&temp);
-            constexpr int x = 90;
-            constexpr int y = 325;
-            RenderProxy::copySurfaceInto(renderTarget.get(), x, y, x + magnifier->width(),
-                                         y + magnifier->height(), &temp);
+            RenderProxy::copySurfaceInto(renderTarget.get(), copyRequest);
+            copyRequest->waitForResult();
         }
     }
-
-    sk_sp<Bitmap> magnifier;
 };
diff --git a/media/java/android/media/AudioPlaybackConfiguration.java b/media/java/android/media/AudioPlaybackConfiguration.java
index c213fcc..60c812a 100644
--- a/media/java/android/media/AudioPlaybackConfiguration.java
+++ b/media/java/android/media/AudioPlaybackConfiguration.java
@@ -243,12 +243,23 @@
      * Flag used when playback is restricted by AppOps manager with OP_PLAY_AUDIO.
      */
     public static final int PLAYER_MUTE_PLAYBACK_RESTRICTED = (1 << 3);
+    /**
+     * @hide
+     * Flag used when muted by client volume.
+     */
+    public static final int PLAYER_MUTE_CLIENT_VOLUME = (1 << 4);
+    /**
+     * @hide
+     * Flag used when muted by volume shaper.
+     */
+    public static final int PLAYER_MUTE_VOLUME_SHAPER = (1 << 5);
 
     /** @hide */
     @IntDef(
             flag = true,
             value = {PLAYER_MUTE_MASTER, PLAYER_MUTE_STREAM_VOLUME, PLAYER_MUTE_STREAM_MUTED,
-                    PLAYER_MUTE_PLAYBACK_RESTRICTED})
+                    PLAYER_MUTE_PLAYBACK_RESTRICTED, PLAYER_MUTE_CLIENT_VOLUME,
+                    PLAYER_MUTE_VOLUME_SHAPER})
     @Retention(RetentionPolicy.SOURCE)
     public @interface PlayerMuteEvent {
     }
@@ -678,7 +689,9 @@
                 + " muteFromStreamVolume=" + ((mMutedState & PLAYER_MUTE_STREAM_VOLUME) != 0)
                 + " muteFromStreamMuted=" + ((mMutedState & PLAYER_MUTE_STREAM_MUTED) != 0)
                 + " muteFromPlaybackRestricted=" + ((mMutedState & PLAYER_MUTE_PLAYBACK_RESTRICTED)
-                != 0);
+                != 0)
+                + " muteFromClientVolume=" + ((mMutedState & PLAYER_MUTE_CLIENT_VOLUME) != 0)
+                + " muteFromVolumeShaper=" + ((mMutedState & PLAYER_MUTE_VOLUME_SHAPER) != 0);
     }
 
     //=====================================================================
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 955bfcc..444366a 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -65,6 +65,8 @@
 
     private static final String TAG = "AudioSystem";
 
+    private static final int SOURCE_CODEC_TYPE_OPUS = 6; // TODO remove in U
+
     // private constructor to prevent instantiating AudioSystem
     private AudioSystem() {
         throw new UnsupportedOperationException("Trying to instantiate AudioSystem");
@@ -243,6 +245,8 @@
     public static final int AUDIO_FORMAT_LDAC           = 0x23000000;
     /** @hide */
     public static final int AUDIO_FORMAT_LC3            = 0x2B000000;
+    /** @hide */
+    public static final int AUDIO_FORMAT_OPUS           = 0x08000000;
 
 
     /** @hide */
@@ -254,7 +258,9 @@
             AUDIO_FORMAT_APTX,
             AUDIO_FORMAT_APTX_HD,
             AUDIO_FORMAT_LDAC,
-            AUDIO_FORMAT_LC3}
+            AUDIO_FORMAT_LC3,
+            AUDIO_FORMAT_OPUS
+           }
     )
     @Retention(RetentionPolicy.SOURCE)
     public @interface AudioFormatNativeEnumForBtCodec {}
@@ -287,6 +293,7 @@
             case AUDIO_FORMAT_APTX_HD: return BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD;
             case AUDIO_FORMAT_LDAC: return BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC;
             case AUDIO_FORMAT_LC3: return BluetoothCodecConfig.SOURCE_CODEC_TYPE_LC3;
+            case AUDIO_FORMAT_OPUS: return SOURCE_CODEC_TYPE_OPUS; // TODO update in U
             default:
                 Log.e(TAG, "Unknown audio format 0x" + Integer.toHexString(audioFormat)
                         + " for conversion to BT codec");
@@ -329,6 +336,8 @@
                 return AudioSystem.AUDIO_FORMAT_LDAC;
             case BluetoothCodecConfig.SOURCE_CODEC_TYPE_LC3:
                 return AudioSystem.AUDIO_FORMAT_LC3;
+            case SOURCE_CODEC_TYPE_OPUS: // TODO update in U
+                return AudioSystem.AUDIO_FORMAT_OPUS;
             default:
                 Log.e(TAG, "Unknown BT codec 0x" + Integer.toHexString(btCodec)
                         + " for conversion to audio format");
diff --git a/media/java/android/media/ImageReader.java b/media/java/android/media/ImageReader.java
index 472586b..fde7afd 100644
--- a/media/java/android/media/ImageReader.java
+++ b/media/java/android/media/ImageReader.java
@@ -18,7 +18,11 @@
 
 import android.annotation.IntRange;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.SuppressLint;
+import android.app.compat.CompatChanges;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledAfter;
 import android.graphics.GraphicBuffer;
 import android.graphics.ImageFormat;
 import android.graphics.ImageFormat.Format;
@@ -29,6 +33,7 @@
 import android.hardware.HardwareBuffer.Usage;
 import android.hardware.SyncFence;
 import android.hardware.camera2.MultiResolutionImageReader;
+import android.os.Build;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.ParcelFileDescriptor;
@@ -87,6 +92,38 @@
 
     /**
      * <p>
+     * Flag to gate correct exception thrown by {@code #detachImage}.
+     * </p>
+     * <p>
+     * {@code #detachImage} is documented as throwing {@link java.lang.IllegalStateException} in
+     * the event of an error; a native helper method to this threw
+     * {@link java.lang.RuntimeException} if the surface was abandoned while detaching the
+     * {@code Image}.
+     * <p>
+     * This previously undocumented exception behavior continues through Android T.
+     * </p>
+     * <p>
+     * After Android T, the native helper method only throws {@code IllegalStateExceptions} in
+     * accordance with the documentation.
+     * </p>
+     * <p>
+     * {@code #detachImage} will now throw only ISEs if it runs into errors while detaching
+     * the image. Behavior on apps targeting API levels <= T remains unchanged.
+     * </p>
+     */
+    @ChangeId
+    @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.TIRAMISU)
+    private static final long DETACH_THROWS_ISE_ONLY = 236825255L;
+
+    /**
+     * Cached value of {@link #DETACH_THROWS_ISE_ONLY} flag to prevent repeated calls when
+     * detaching image.
+     */
+    private final boolean mDetachThrowsIseOnly =
+            CompatChanges.isChangeEnabled(DETACH_THROWS_ISE_ONLY);
+
+    /**
+     * <p>
      * Create a new reader for images of the desired size and format.
      * </p>
      * <p>
@@ -825,10 +862,10 @@
      * </p>
      * <p>
      * After this call, the ImageReader no longer owns this image, and the image
-     * ownership can be transfered to another entity like {@link ImageWriter}
+     * ownership can be transferred to another entity like {@link ImageWriter}
      * via {@link ImageWriter#queueInputImage}. It's up to the new owner to
      * release the resources held by this image. For example, if the ownership
-     * of this image is transfered to an {@link ImageWriter}, the image will be
+     * of this image is transferred to an {@link ImageWriter}, the image will be
      * freed by the ImageWriter after the image data consumption is done.
      * </p>
      * <p>
@@ -849,16 +886,22 @@
      * @throws IllegalStateException If the ImageReader or image have been
      *             closed, or the has been detached, or has not yet been
      *             acquired.
+     * @throws RuntimeException If there is an error detaching {@code Image} from {@code Surface}.
+     *              {@code RuntimeException} is only thrown for applications targeting SDK <=
+     *              {@link android.os.Build.VERSION_CODES#TIRAMISU}.
+     *              For applications targeting SDK >
+     *              {@link android.os.Build.VERSION_CODES#TIRAMISU},
+     *              this method only throws {@code IllegalStateException}.
      * @hide
      */
-     public void detachImage(Image image) {
-       if (image == null) {
+    public void detachImage(@Nullable Image image) {
+        if (image == null) {
            throw new IllegalArgumentException("input image must not be null");
-       }
-       if (!isImageOwnedbyMe(image)) {
+        }
+        if (!isImageOwnedbyMe(image)) {
            throw new IllegalArgumentException("Trying to detach an image that is not owned by"
                    + " this ImageReader");
-       }
+        }
 
         SurfaceImage si = (SurfaceImage) image;
         si.throwISEIfImageIsInvalid();
@@ -867,7 +910,7 @@
             throw new IllegalStateException("Image was already detached from this ImageReader");
         }
 
-        nativeDetachImage(image);
+        nativeDetachImage(image, mDetachThrowsIseOnly);
         si.clearSurfacePlanes();
         si.mPlanes = null;
         si.setDetached(true);
@@ -1408,7 +1451,7 @@
     private synchronized native void nativeClose();
     private synchronized native void nativeReleaseImage(Image i);
     private synchronized native Surface nativeGetSurface();
-    private synchronized native int nativeDetachImage(Image i);
+    private synchronized native int nativeDetachImage(Image i, boolean throwISEOnly);
     private synchronized native void nativeDiscardFreeBuffers();
 
     /**
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index 7b27358..53d4efa 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -45,6 +45,7 @@
 import java.nio.ReadOnlyBufferException;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.BitSet;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -1811,7 +1812,7 @@
                     synchronized(mBufferLock) {
                         switch (mBufferMode) {
                             case BUFFER_MODE_LEGACY:
-                                validateInputByteBuffer(mCachedInputBuffers, index);
+                                validateInputByteBufferLocked(mCachedInputBuffers, index);
                                 break;
                             case BUFFER_MODE_BLOCK:
                                 while (mQueueRequests.size() <= index) {
@@ -1840,7 +1841,7 @@
                     synchronized(mBufferLock) {
                         switch (mBufferMode) {
                             case BUFFER_MODE_LEGACY:
-                                validateOutputByteBuffer(mCachedOutputBuffers, index, info);
+                                validateOutputByteBufferLocked(mCachedOutputBuffers, index, info);
                                 break;
                             case BUFFER_MODE_BLOCK:
                                 while (mOutputFrames.size() <= index) {
@@ -2328,10 +2329,6 @@
      */
     public final void start() {
         native_start();
-        synchronized(mBufferLock) {
-            cacheBuffers(true /* input */);
-            cacheBuffers(false /* input */);
-        }
     }
     private native final void native_start();
 
@@ -2388,8 +2385,10 @@
      */
     public final void flush() {
         synchronized(mBufferLock) {
-            invalidateByteBuffers(mCachedInputBuffers);
-            invalidateByteBuffers(mCachedOutputBuffers);
+            invalidateByteBuffersLocked(mCachedInputBuffers);
+            invalidateByteBuffersLocked(mCachedOutputBuffers);
+            mValidInputIndices.clear();
+            mValidOutputIndices.clear();
             mDequeuedInputBuffers.clear();
             mDequeuedOutputBuffers.clear();
         }
@@ -2673,14 +2672,14 @@
                         + "is not compatible with CONFIGURE_FLAG_USE_BLOCK_MODEL. "
                         + "Please use getQueueRequest() to queue buffers");
             }
-            invalidateByteBuffer(mCachedInputBuffers, index);
+            invalidateByteBufferLocked(mCachedInputBuffers, index, true /* input */);
             mDequeuedInputBuffers.remove(index);
         }
         try {
             native_queueInputBuffer(
                     index, offset, size, presentationTimeUs, flags);
         } catch (CryptoException | IllegalStateException e) {
-            revalidateByteBuffer(mCachedInputBuffers, index);
+            revalidateByteBuffer(mCachedInputBuffers, index, true /* input */);
             throw e;
         }
     }
@@ -2943,14 +2942,14 @@
                         + "is not compatible with CONFIGURE_FLAG_USE_BLOCK_MODEL. "
                         + "Please use getQueueRequest() to queue buffers");
             }
-            invalidateByteBuffer(mCachedInputBuffers, index);
+            invalidateByteBufferLocked(mCachedInputBuffers, index, true /* input */);
             mDequeuedInputBuffers.remove(index);
         }
         try {
             native_queueSecureInputBuffer(
                     index, offset, info, presentationTimeUs, flags);
         } catch (CryptoException | IllegalStateException e) {
-            revalidateByteBuffer(mCachedInputBuffers, index);
+            revalidateByteBuffer(mCachedInputBuffers, index, true /* input */);
             throw e;
         }
     }
@@ -2984,7 +2983,7 @@
         int res = native_dequeueInputBuffer(timeoutUs);
         if (res >= 0) {
             synchronized(mBufferLock) {
-                validateInputByteBuffer(mCachedInputBuffers, res);
+                validateInputByteBufferLocked(mCachedInputBuffers, res);
             }
         }
         return res;
@@ -3581,10 +3580,10 @@
         int res = native_dequeueOutputBuffer(info, timeoutUs);
         synchronized (mBufferLock) {
             if (res == INFO_OUTPUT_BUFFERS_CHANGED) {
-                cacheBuffers(false /* input */);
+                cacheBuffersLocked(false /* input */);
             } else if (res >= 0) {
-                validateOutputByteBuffer(mCachedOutputBuffers, res, info);
-                if (mHasSurface) {
+                validateOutputByteBufferLocked(mCachedOutputBuffers, res, info);
+                if (mHasSurface || mCachedOutputBuffers == null) {
                     mDequeuedOutputInfos.put(res, info.dup());
                 }
             }
@@ -3678,9 +3677,9 @@
         synchronized(mBufferLock) {
             switch (mBufferMode) {
                 case BUFFER_MODE_LEGACY:
-                    invalidateByteBuffer(mCachedOutputBuffers, index);
+                    invalidateByteBufferLocked(mCachedOutputBuffers, index, false /* input */);
                     mDequeuedOutputBuffers.remove(index);
-                    if (mHasSurface) {
+                    if (mHasSurface || mCachedOutputBuffers == null) {
                         info = mDequeuedOutputInfos.remove(index);
                     }
                     break;
@@ -3832,15 +3831,24 @@
 
     private ByteBuffer[] mCachedInputBuffers;
     private ByteBuffer[] mCachedOutputBuffers;
+    private BitSet mValidInputIndices = new BitSet();
+    private BitSet mValidOutputIndices = new BitSet();
+
     private final BufferMap mDequeuedInputBuffers = new BufferMap();
     private final BufferMap mDequeuedOutputBuffers = new BufferMap();
     private final Map<Integer, BufferInfo> mDequeuedOutputInfos =
         new HashMap<Integer, BufferInfo>();
     final private Object mBufferLock;
 
-    private final void invalidateByteBuffer(
-            @Nullable ByteBuffer[] buffers, int index) {
-        if (buffers != null && index >= 0 && index < buffers.length) {
+    private void invalidateByteBufferLocked(
+            @Nullable ByteBuffer[] buffers, int index, boolean input) {
+        if (buffers == null) {
+            if (index < 0) {
+                throw new IllegalStateException("index is negative (" + index + ")");
+            }
+            BitSet indices = input ? mValidInputIndices : mValidOutputIndices;
+            indices.clear(index);
+        } else if (index >= 0 && index < buffers.length) {
             ByteBuffer buffer = buffers[index];
             if (buffer != null) {
                 buffer.setAccessible(false);
@@ -3848,9 +3856,14 @@
         }
     }
 
-    private final void validateInputByteBuffer(
+    private void validateInputByteBufferLocked(
             @Nullable ByteBuffer[] buffers, int index) {
-        if (buffers != null && index >= 0 && index < buffers.length) {
+        if (buffers == null) {
+            if (index < 0) {
+                throw new IllegalStateException("index is negative (" + index + ")");
+            }
+            mValidInputIndices.set(index);
+        } else if (index >= 0 && index < buffers.length) {
             ByteBuffer buffer = buffers[index];
             if (buffer != null) {
                 buffer.setAccessible(true);
@@ -3859,10 +3872,16 @@
         }
     }
 
-    private final void revalidateByteBuffer(
-            @Nullable ByteBuffer[] buffers, int index) {
+    private void revalidateByteBuffer(
+            @Nullable ByteBuffer[] buffers, int index, boolean input) {
         synchronized(mBufferLock) {
-            if (buffers != null && index >= 0 && index < buffers.length) {
+            if (buffers == null) {
+                if (index < 0) {
+                    throw new IllegalStateException("index is negative (" + index + ")");
+                }
+                BitSet indices = input ? mValidInputIndices : mValidOutputIndices;
+                indices.set(index);
+            } else if (index >= 0 && index < buffers.length) {
                 ByteBuffer buffer = buffers[index];
                 if (buffer != null) {
                     buffer.setAccessible(true);
@@ -3871,9 +3890,14 @@
         }
     }
 
-    private final void validateOutputByteBuffer(
+    private void validateOutputByteBufferLocked(
             @Nullable ByteBuffer[] buffers, int index, @NonNull BufferInfo info) {
-        if (buffers != null && index >= 0 && index < buffers.length) {
+        if (buffers == null) {
+            if (index < 0) {
+                throw new IllegalStateException("index is negative (" + index + ")");
+            }
+            mValidOutputIndices.set(index);
+        } else if (index >= 0 && index < buffers.length) {
             ByteBuffer buffer = buffers[index];
             if (buffer != null) {
                 buffer.setAccessible(true);
@@ -3882,7 +3906,7 @@
         }
     }
 
-    private final void invalidateByteBuffers(@Nullable ByteBuffer[] buffers) {
+    private void invalidateByteBuffersLocked(@Nullable ByteBuffer[] buffers) {
         if (buffers != null) {
             for (ByteBuffer buffer: buffers) {
                 if (buffer != null) {
@@ -3892,27 +3916,29 @@
         }
     }
 
-    private final void freeByteBuffer(@Nullable ByteBuffer buffer) {
+    private void freeByteBufferLocked(@Nullable ByteBuffer buffer) {
         if (buffer != null /* && buffer.isDirect() */) {
             // all of our ByteBuffers are direct
             java.nio.NioUtils.freeDirectBuffer(buffer);
         }
     }
 
-    private final void freeByteBuffers(@Nullable ByteBuffer[] buffers) {
+    private void freeByteBuffersLocked(@Nullable ByteBuffer[] buffers) {
         if (buffers != null) {
             for (ByteBuffer buffer: buffers) {
-                freeByteBuffer(buffer);
+                freeByteBufferLocked(buffer);
             }
         }
     }
 
-    private final void freeAllTrackedBuffers() {
+    private void freeAllTrackedBuffers() {
         synchronized(mBufferLock) {
-            freeByteBuffers(mCachedInputBuffers);
-            freeByteBuffers(mCachedOutputBuffers);
+            freeByteBuffersLocked(mCachedInputBuffers);
+            freeByteBuffersLocked(mCachedOutputBuffers);
             mCachedInputBuffers = null;
             mCachedOutputBuffers = null;
+            mValidInputIndices.clear();
+            mValidOutputIndices.clear();
             mDequeuedInputBuffers.clear();
             mDequeuedOutputBuffers.clear();
             mQueueRequests.clear();
@@ -3920,14 +3946,31 @@
         }
     }
 
-    private final void cacheBuffers(boolean input) {
+    private void cacheBuffersLocked(boolean input) {
         ByteBuffer[] buffers = null;
         try {
             buffers = getBuffers(input);
-            invalidateByteBuffers(buffers);
+            invalidateByteBuffersLocked(buffers);
         } catch (IllegalStateException e) {
             // we don't get buffers in async mode
         }
+        if (buffers != null) {
+            BitSet indices = input ? mValidInputIndices : mValidOutputIndices;
+            for (int i = 0; i < buffers.length; ++i) {
+                ByteBuffer buffer = buffers[i];
+                if (buffer == null || !indices.get(i)) {
+                    continue;
+                }
+                buffer.setAccessible(true);
+                if (!input) {
+                    BufferInfo info = mDequeuedOutputInfos.get(i);
+                    if (info != null) {
+                        buffer.limit(info.offset + info.size).position(info.offset);
+                    }
+                }
+            }
+            indices.clear();
+        }
         if (input) {
             mCachedInputBuffers = buffers;
         } else {
@@ -3963,6 +4006,9 @@
                         + "objects and attach to QueueRequest objects.");
             }
             if (mCachedInputBuffers == null) {
+                cacheBuffersLocked(true /* input */);
+            }
+            if (mCachedInputBuffers == null) {
                 throw new IllegalStateException();
             }
             // FIXME: check codec status
@@ -4001,6 +4047,9 @@
                         + "Please use getOutputFrame to get output frames.");
             }
             if (mCachedOutputBuffers == null) {
+                cacheBuffersLocked(false /* input */);
+            }
+            if (mCachedOutputBuffers == null) {
                 throw new IllegalStateException();
             }
             // FIXME: check codec status
@@ -4038,7 +4087,7 @@
         }
         ByteBuffer newBuffer = getBuffer(true /* input */, index);
         synchronized (mBufferLock) {
-            invalidateByteBuffer(mCachedInputBuffers, index);
+            invalidateByteBufferLocked(mCachedInputBuffers, index, true /* input */);
             mDequeuedInputBuffers.put(index, newBuffer);
         }
         return newBuffer;
@@ -4075,7 +4124,7 @@
         }
         Image newImage = getImage(true /* input */, index);
         synchronized (mBufferLock) {
-            invalidateByteBuffer(mCachedInputBuffers, index);
+            invalidateByteBufferLocked(mCachedInputBuffers, index, true /* input */);
             mDequeuedInputBuffers.put(index, newImage);
         }
         return newImage;
@@ -4111,7 +4160,7 @@
         }
         ByteBuffer newBuffer = getBuffer(false /* input */, index);
         synchronized (mBufferLock) {
-            invalidateByteBuffer(mCachedOutputBuffers, index);
+            invalidateByteBufferLocked(mCachedOutputBuffers, index, false /* input */);
             mDequeuedOutputBuffers.put(index, newBuffer);
         }
         return newBuffer;
@@ -4146,7 +4195,7 @@
         }
         Image newImage = getImage(false /* input */, index);
         synchronized (mBufferLock) {
-            invalidateByteBuffer(mCachedOutputBuffers, index);
+            invalidateByteBufferLocked(mCachedOutputBuffers, index, false /* input */);
             mDequeuedOutputBuffers.put(index, newImage);
         }
         return newImage;
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index 75fd64a..bf30c50 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -1445,7 +1445,7 @@
                 sampleRates = new int[] { 8000, 12000, 16000, 24000, 48000 };
                 maxChannels = 255;
             } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_RAW)) {
-                sampleRateRange = Range.create(1, 96000);
+                sampleRateRange = Range.create(1, 192000);
                 bitRates = Range.create(1, 10000000);
                 maxChannels = AudioSystem.OUT_CHANNEL_COUNT_MAX;
             } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_FLAC)) {
diff --git a/media/java/android/media/tv/interactive/ITvInteractiveAppSessionWrapper.java b/media/java/android/media/tv/interactive/ITvInteractiveAppSessionWrapper.java
new file mode 100644
index 0000000..9f92887
--- /dev/null
+++ b/media/java/android/media/tv/interactive/ITvInteractiveAppSessionWrapper.java
@@ -0,0 +1,441 @@
+/*
+ * 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.tv.interactive;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.graphics.Rect;
+import android.media.tv.AdResponse;
+import android.media.tv.BroadcastInfoResponse;
+import android.media.tv.TvContentRating;
+import android.media.tv.TvTrackInfo;
+import android.media.tv.interactive.TvInteractiveAppService.Session;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.util.Log;
+import android.view.InputChannel;
+import android.view.InputEvent;
+import android.view.InputEventReceiver;
+import android.view.Surface;
+
+import com.android.internal.os.HandlerCaller;
+import com.android.internal.os.SomeArgs;
+
+import java.util.List;
+
+/**
+ * Implements the internal ITvInteractiveAppSession interface.
+ * @hide
+ */
+public class ITvInteractiveAppSessionWrapper
+        extends ITvInteractiveAppSession.Stub implements HandlerCaller.Callback {
+    private static final String TAG = "ITvInteractiveAppSessionWrapper";
+
+    private static final int EXECUTE_MESSAGE_TIMEOUT_SHORT_MILLIS = 1000;
+    private static final int EXECUTE_MESSAGE_TIMEOUT_LONG_MILLIS = 5 * 1000;
+
+    private static final int DO_RELEASE = 1;
+    private static final int DO_START_INTERACTIVE_APP = 2;
+    private static final int DO_STOP_INTERACTIVE_APP = 3;
+    private static final int DO_RESET_INTERACTIVE_APP = 4;
+    private static final int DO_CREATE_BI_INTERACTIVE_APP = 5;
+    private static final int DO_DESTROY_BI_INTERACTIVE_APP = 6;
+    private static final int DO_SET_TELETEXT_APP_ENABLED = 7;
+    private static final int DO_SEND_CURRENT_CHANNEL_URI = 8;
+    private static final int DO_SEND_CURRENT_CHANNEL_LCN = 9;
+    private static final int DO_SEND_STREAM_VOLUME = 10;
+    private static final int DO_SEND_TRACK_INFO_LIST = 11;
+    private static final int DO_SEND_CURRENT_TV_INPUT_ID = 12;
+    private static final int DO_SEND_SIGNING_RESULT = 13;
+    private static final int DO_NOTIFY_ERROR = 14;
+    private static final int DO_NOTIFY_TUNED = 15;
+    private static final int DO_NOTIFY_TRACK_SELECTED = 16;
+    private static final int DO_NOTIFY_TRACKS_CHANGED = 17;
+    private static final int DO_NOTIFY_VIDEO_AVAILABLE = 18;
+    private static final int DO_NOTIFY_VIDEO_UNAVAILABLE = 19;
+    private static final int DO_NOTIFY_CONTENT_ALLOWED = 20;
+    private static final int DO_NOTIFY_CONTENT_BLOCKED = 21;
+    private static final int DO_NOTIFY_SIGNAL_STRENGTH = 22;
+    private static final int DO_SET_SURFACE = 23;
+    private static final int DO_DISPATCH_SURFACE_CHANGED = 24;
+    private static final int DO_NOTIFY_BROADCAST_INFO_RESPONSE = 25;
+    private static final int DO_NOTIFY_AD_RESPONSE = 26;
+    private static final int DO_CREATE_MEDIA_VIEW = 27;
+    private static final int DO_RELAYOUT_MEDIA_VIEW = 28;
+    private static final int DO_REMOVE_MEDIA_VIEW = 29;
+
+    private final HandlerCaller mCaller;
+    private Session mSessionImpl;
+    private InputChannel mChannel;
+    private TvInteractiveAppEventReceiver mReceiver;
+
+    public ITvInteractiveAppSessionWrapper(
+            Context context, Session mSessionImpl, InputChannel channel) {
+        this.mSessionImpl = mSessionImpl;
+        mCaller = new HandlerCaller(context, null, this, true /* asyncHandler */);
+        mChannel = channel;
+        if (channel != null) {
+            mReceiver = new TvInteractiveAppEventReceiver(channel, context.getMainLooper());
+        }
+    }
+
+    @Override
+    public void executeMessage(Message msg) {
+        if (mSessionImpl == null) {
+            return;
+        }
+
+        long startTime = System.nanoTime();
+        switch (msg.what) {
+            case DO_RELEASE: {
+                mSessionImpl.release();
+                mSessionImpl = null;
+                if (mReceiver != null) {
+                    mReceiver.dispose();
+                    mReceiver = null;
+                }
+                if (mChannel != null) {
+                    mChannel.dispose();
+                    mChannel = null;
+                }
+                break;
+            }
+            case DO_START_INTERACTIVE_APP: {
+                mSessionImpl.startInteractiveApp();
+                break;
+            }
+            case DO_STOP_INTERACTIVE_APP: {
+                mSessionImpl.stopInteractiveApp();
+                break;
+            }
+            case DO_RESET_INTERACTIVE_APP: {
+                mSessionImpl.resetInteractiveApp();
+                break;
+            }
+            case DO_CREATE_BI_INTERACTIVE_APP: {
+                SomeArgs args = (SomeArgs) msg.obj;
+                mSessionImpl.createBiInteractiveApp((Uri) args.arg1, (Bundle) args.arg2);
+                args.recycle();
+                break;
+            }
+            case DO_DESTROY_BI_INTERACTIVE_APP: {
+                mSessionImpl.destroyBiInteractiveApp((String) msg.obj);
+                break;
+            }
+            case DO_SET_TELETEXT_APP_ENABLED: {
+                mSessionImpl.setTeletextAppEnabled((Boolean) msg.obj);
+                break;
+            }
+            case DO_SEND_CURRENT_CHANNEL_URI: {
+                mSessionImpl.sendCurrentChannelUri((Uri) msg.obj);
+                break;
+            }
+            case DO_SEND_CURRENT_CHANNEL_LCN: {
+                mSessionImpl.sendCurrentChannelLcn((Integer) msg.obj);
+                break;
+            }
+            case DO_SEND_STREAM_VOLUME: {
+                mSessionImpl.sendStreamVolume((Float) msg.obj);
+                break;
+            }
+            case DO_SEND_TRACK_INFO_LIST: {
+                mSessionImpl.sendTrackInfoList((List<TvTrackInfo>) msg.obj);
+                break;
+            }
+            case DO_SEND_CURRENT_TV_INPUT_ID: {
+                mSessionImpl.sendCurrentTvInputId((String) msg.obj);
+                break;
+            }
+            case DO_SEND_SIGNING_RESULT: {
+                SomeArgs args = (SomeArgs) msg.obj;
+                mSessionImpl.sendSigningResult((String) args.arg1, (byte[]) args.arg2);
+                args.recycle();
+                break;
+            }
+            case DO_NOTIFY_ERROR: {
+                SomeArgs args = (SomeArgs) msg.obj;
+                mSessionImpl.notifyError((String) args.arg1, (Bundle) args.arg2);
+                args.recycle();
+                break;
+            }
+            case DO_NOTIFY_TUNED: {
+                mSessionImpl.notifyTuned((Uri) msg.obj);
+                break;
+            }
+            case DO_NOTIFY_TRACK_SELECTED: {
+                SomeArgs args = (SomeArgs) msg.obj;
+                mSessionImpl.notifyTrackSelected((Integer) args.arg1, (String) args.arg2);
+                args.recycle();
+                break;
+            }
+            case DO_NOTIFY_TRACKS_CHANGED: {
+                mSessionImpl.notifyTracksChanged((List<TvTrackInfo>) msg.obj);
+                break;
+            }
+            case DO_NOTIFY_VIDEO_AVAILABLE: {
+                mSessionImpl.notifyVideoAvailable();
+                break;
+            }
+            case DO_NOTIFY_VIDEO_UNAVAILABLE: {
+                mSessionImpl.notifyVideoUnavailable((Integer) msg.obj);
+                break;
+            }
+            case DO_NOTIFY_CONTENT_ALLOWED: {
+                mSessionImpl.notifyContentAllowed();
+                break;
+            }
+            case DO_NOTIFY_CONTENT_BLOCKED: {
+                mSessionImpl.notifyContentBlocked((TvContentRating) msg.obj);
+                break;
+            }
+            case DO_NOTIFY_SIGNAL_STRENGTH: {
+                mSessionImpl.notifySignalStrength((Integer) msg.obj);
+                break;
+            }
+            case DO_SET_SURFACE: {
+                mSessionImpl.setSurface((Surface) msg.obj);
+                break;
+            }
+            case DO_DISPATCH_SURFACE_CHANGED: {
+                SomeArgs args = (SomeArgs) msg.obj;
+                mSessionImpl.dispatchSurfaceChanged(
+                        (Integer) args.arg1, (Integer) args.arg2, (Integer) args.arg3);
+                args.recycle();
+                break;
+            }
+            case DO_NOTIFY_BROADCAST_INFO_RESPONSE: {
+                mSessionImpl.notifyBroadcastInfoResponse((BroadcastInfoResponse) msg.obj);
+                break;
+            }
+            case DO_NOTIFY_AD_RESPONSE: {
+                mSessionImpl.notifyAdResponse((AdResponse) msg.obj);
+                break;
+            }
+            case DO_CREATE_MEDIA_VIEW: {
+                SomeArgs args = (SomeArgs) msg.obj;
+                mSessionImpl.createMediaView((IBinder) args.arg1, (Rect) args.arg2);
+                args.recycle();
+                break;
+            }
+            case DO_RELAYOUT_MEDIA_VIEW: {
+                mSessionImpl.relayoutMediaView((Rect) msg.obj);
+                break;
+            }
+            case DO_REMOVE_MEDIA_VIEW: {
+                mSessionImpl.removeMediaView(true);
+                break;
+            }
+            default: {
+                Log.w(TAG, "Unhandled message code: " + msg.what);
+                break;
+            }
+        }
+        long durationMs = (System.nanoTime() - startTime) / (1000 * 1000);
+        if (durationMs > EXECUTE_MESSAGE_TIMEOUT_SHORT_MILLIS) {
+            Log.w(TAG, "Handling message (" + msg.what + ") took too long time (duration="
+                    + durationMs + "ms)");
+            if (durationMs > EXECUTE_MESSAGE_TIMEOUT_LONG_MILLIS) {
+                // TODO: handle timeout
+            }
+        }
+    }
+
+    @Override
+    public void startInteractiveApp() {
+        mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_START_INTERACTIVE_APP));
+    }
+
+    @Override
+    public void stopInteractiveApp() {
+        mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_STOP_INTERACTIVE_APP));
+    }
+
+    @Override
+    public void resetInteractiveApp() {
+        mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_RESET_INTERACTIVE_APP));
+    }
+
+    @Override
+    public void createBiInteractiveApp(@NonNull Uri biIAppUri, @Nullable Bundle params) {
+        mCaller.executeOrSendMessage(
+                mCaller.obtainMessageOO(DO_CREATE_BI_INTERACTIVE_APP, biIAppUri, params));
+    }
+
+    @Override
+    public void destroyBiInteractiveApp(@NonNull String biIAppId) {
+        mCaller.executeOrSendMessage(
+                mCaller.obtainMessageO(DO_DESTROY_BI_INTERACTIVE_APP, biIAppId));
+    }
+
+    @Override
+    public void setTeletextAppEnabled(boolean enable) {
+        mCaller.executeOrSendMessage(
+                mCaller.obtainMessageO(DO_SET_TELETEXT_APP_ENABLED, enable));
+    }
+
+    @Override
+    public void sendCurrentChannelUri(@Nullable Uri channelUri) {
+        mCaller.executeOrSendMessage(
+                mCaller.obtainMessageO(DO_SEND_CURRENT_CHANNEL_URI, channelUri));
+    }
+
+    @Override
+    public void sendCurrentChannelLcn(int lcn) {
+        mCaller.executeOrSendMessage(
+                mCaller.obtainMessageO(DO_SEND_CURRENT_CHANNEL_LCN, lcn));
+    }
+
+    @Override
+    public void sendStreamVolume(float volume) {
+        mCaller.executeOrSendMessage(
+                mCaller.obtainMessageO(DO_SEND_STREAM_VOLUME, volume));
+    }
+
+    @Override
+    public void sendTrackInfoList(@NonNull List<TvTrackInfo> tracks) {
+        mCaller.executeOrSendMessage(
+                mCaller.obtainMessageO(DO_SEND_TRACK_INFO_LIST, tracks));
+    }
+
+    @Override
+    public void sendCurrentTvInputId(@Nullable String inputId) {
+        mCaller.executeOrSendMessage(
+                mCaller.obtainMessageO(DO_SEND_CURRENT_TV_INPUT_ID, inputId));
+    }
+
+    @Override
+    public void sendSigningResult(@NonNull String signingId, @NonNull byte[] result) {
+        mCaller.executeOrSendMessage(
+                mCaller.obtainMessageOO(DO_SEND_SIGNING_RESULT, signingId, result));
+    }
+
+    @Override
+    public void notifyError(@NonNull String errMsg, @NonNull Bundle params) {
+        mCaller.executeOrSendMessage(
+                mCaller.obtainMessageOO(DO_NOTIFY_ERROR, errMsg, params));
+    }
+
+    @Override
+    public void release() {
+        mSessionImpl.scheduleMediaViewCleanup();
+        mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_RELEASE));
+    }
+
+    @Override
+    public void notifyTuned(Uri channelUri) {
+        mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_NOTIFY_TUNED, channelUri));
+    }
+
+    @Override
+    public void notifyTrackSelected(int type, final String trackId) {
+        mCaller.executeOrSendMessage(
+                mCaller.obtainMessageOO(DO_NOTIFY_TRACK_SELECTED, type, trackId));
+    }
+
+    @Override
+    public void notifyTracksChanged(List<TvTrackInfo> tracks) {
+        mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_NOTIFY_TRACKS_CHANGED, tracks));
+    }
+
+    @Override
+    public void notifyVideoAvailable() {
+        mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_NOTIFY_VIDEO_AVAILABLE));
+    }
+
+    @Override
+    public void notifyVideoUnavailable(int reason) {
+        mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_NOTIFY_VIDEO_UNAVAILABLE, reason));
+    }
+
+    @Override
+    public void notifyContentAllowed() {
+        mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_NOTIFY_CONTENT_ALLOWED));
+    }
+
+    @Override
+    public void notifyContentBlocked(String rating) {
+        mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_NOTIFY_CONTENT_BLOCKED, rating));
+    }
+
+    @Override
+    public void notifySignalStrength(int strength) {
+        mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_NOTIFY_SIGNAL_STRENGTH, strength));
+    }
+
+    @Override
+    public void setSurface(Surface surface) {
+        mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_SET_SURFACE, surface));
+    }
+
+    @Override
+    public void dispatchSurfaceChanged(int format, int width, int height) {
+        mCaller.executeOrSendMessage(
+                mCaller.obtainMessageIIII(DO_DISPATCH_SURFACE_CHANGED, format, width, height, 0));
+    }
+
+    @Override
+    public void notifyBroadcastInfoResponse(BroadcastInfoResponse response) {
+        mCaller.executeOrSendMessage(
+                mCaller.obtainMessageO(DO_NOTIFY_BROADCAST_INFO_RESPONSE, response));
+    }
+
+    @Override
+    public void notifyAdResponse(AdResponse response) {
+        mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_NOTIFY_AD_RESPONSE, response));
+    }
+
+    @Override
+    public void createMediaView(IBinder windowToken, Rect frame) {
+        mCaller.executeOrSendMessage(
+                mCaller.obtainMessageOO(DO_CREATE_MEDIA_VIEW, windowToken, frame));
+    }
+
+    @Override
+    public void relayoutMediaView(Rect frame) {
+        mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_RELAYOUT_MEDIA_VIEW, frame));
+    }
+
+    @Override
+    public void removeMediaView() {
+        mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_REMOVE_MEDIA_VIEW));
+    }
+
+    private final class TvInteractiveAppEventReceiver extends InputEventReceiver {
+        TvInteractiveAppEventReceiver(InputChannel inputChannel, Looper looper) {
+            super(inputChannel, looper);
+        }
+
+        @Override
+        public void onInputEvent(InputEvent event) {
+            if (mSessionImpl == null) {
+                // The session has been finished.
+                finishInputEvent(event, false);
+                return;
+            }
+
+            int handled = mSessionImpl.dispatchInputEvent(event, this);
+            if (handled != TvInteractiveAppManager.Session.DISPATCH_IN_PROGRESS) {
+                finishInputEvent(
+                        event, handled == TvInteractiveAppManager.Session.DISPATCH_HANDLED);
+            }
+        }
+    }
+}
diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppService.java b/media/java/android/media/tv/interactive/TvInteractiveAppService.java
index 54700b6..f5b4322 100755
--- a/media/java/android/media/tv/interactive/TvInteractiveAppService.java
+++ b/media/java/android/media/tv/interactive/TvInteractiveAppService.java
@@ -45,7 +45,6 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
-import android.os.Looper;
 import android.os.Message;
 import android.os.Process;
 import android.os.RemoteCallbackList;
@@ -1416,193 +1415,6 @@
         }
     }
 
-    /**
-     * Implements the internal ITvInteractiveAppSession interface.
-     * @hide
-     */
-    public static class ITvInteractiveAppSessionWrapper extends ITvInteractiveAppSession.Stub {
-        // TODO: put ITvInteractiveAppSessionWrapper in a separate Java file
-        private final Session mSessionImpl;
-        private InputChannel mChannel;
-        private TvInteractiveAppEventReceiver mReceiver;
-
-        public ITvInteractiveAppSessionWrapper(
-                Context context, Session mSessionImpl, InputChannel channel) {
-            this.mSessionImpl = mSessionImpl;
-            mChannel = channel;
-            if (channel != null) {
-                mReceiver = new TvInteractiveAppEventReceiver(channel, context.getMainLooper());
-            }
-        }
-
-        @Override
-        public void startInteractiveApp() {
-            mSessionImpl.startInteractiveApp();
-        }
-
-        @Override
-        public void stopInteractiveApp() {
-            mSessionImpl.stopInteractiveApp();
-        }
-
-        @Override
-        public void resetInteractiveApp() {
-            mSessionImpl.resetInteractiveApp();
-        }
-
-        @Override
-        public void createBiInteractiveApp(@NonNull Uri biIAppUri, @Nullable Bundle params) {
-            mSessionImpl.createBiInteractiveApp(biIAppUri, params);
-        }
-
-        @Override
-        public void setTeletextAppEnabled(boolean enable) {
-            mSessionImpl.setTeletextAppEnabled(enable);
-        }
-
-        @Override
-        public void destroyBiInteractiveApp(@NonNull String biIAppId) {
-            mSessionImpl.destroyBiInteractiveApp(biIAppId);
-        }
-
-        @Override
-        public void sendCurrentChannelUri(@Nullable Uri channelUri) {
-            mSessionImpl.sendCurrentChannelUri(channelUri);
-        }
-
-        @Override
-        public void sendCurrentChannelLcn(int lcn) {
-            mSessionImpl.sendCurrentChannelLcn(lcn);
-        }
-
-        @Override
-        public void sendStreamVolume(float volume) {
-            mSessionImpl.sendStreamVolume(volume);
-        }
-
-        @Override
-        public void sendTrackInfoList(@NonNull List<TvTrackInfo> tracks) {
-            mSessionImpl.sendTrackInfoList(tracks);
-        }
-
-        @Override
-        public void sendCurrentTvInputId(@Nullable String inputId) {
-            mSessionImpl.sendCurrentTvInputId(inputId);
-        }
-
-        @Override
-        public void sendSigningResult(@NonNull String signingId, @NonNull byte[] result) {
-            mSessionImpl.sendSigningResult(signingId, result);
-        }
-
-        @Override
-        public void notifyError(@NonNull String errMsg, @NonNull Bundle params) {
-            mSessionImpl.notifyError(errMsg, params);
-        }
-
-        @Override
-        public void release() {
-            mSessionImpl.scheduleMediaViewCleanup();
-            mSessionImpl.release();
-        }
-
-        @Override
-        public void notifyTuned(Uri channelUri) {
-            mSessionImpl.notifyTuned(channelUri);
-        }
-
-        @Override
-        public void notifyTrackSelected(int type, final String trackId) {
-            mSessionImpl.notifyTrackSelected(type, trackId);
-        }
-
-        @Override
-        public void notifyTracksChanged(List<TvTrackInfo> tracks) {
-            mSessionImpl.notifyTracksChanged(tracks);
-        }
-
-        @Override
-        public void notifyVideoAvailable() {
-            mSessionImpl.notifyVideoAvailable();
-        }
-
-        @Override
-        public void notifyVideoUnavailable(int reason) {
-            mSessionImpl.notifyVideoUnavailable(reason);
-        }
-
-        @Override
-        public void notifyContentAllowed() {
-            mSessionImpl.notifyContentAllowed();
-        }
-
-        @Override
-        public void notifyContentBlocked(String rating) {
-            mSessionImpl.notifyContentBlocked(TvContentRating.unflattenFromString(rating));
-        }
-
-        @Override
-        public void notifySignalStrength(int strength) {
-            mSessionImpl.notifySignalStrength(strength);
-        }
-
-        @Override
-        public void setSurface(Surface surface) {
-            mSessionImpl.setSurface(surface);
-        }
-
-        @Override
-        public void dispatchSurfaceChanged(int format, int width, int height) {
-            mSessionImpl.dispatchSurfaceChanged(format, width, height);
-        }
-
-        @Override
-        public void notifyBroadcastInfoResponse(BroadcastInfoResponse response) {
-            mSessionImpl.notifyBroadcastInfoResponse(response);
-        }
-
-        @Override
-        public void notifyAdResponse(AdResponse response) {
-            mSessionImpl.notifyAdResponse(response);
-        }
-
-        @Override
-        public void createMediaView(IBinder windowToken, Rect frame) {
-            mSessionImpl.createMediaView(windowToken, frame);
-        }
-
-        @Override
-        public void relayoutMediaView(Rect frame) {
-            mSessionImpl.relayoutMediaView(frame);
-        }
-
-        @Override
-        public void removeMediaView() {
-            mSessionImpl.removeMediaView(true);
-        }
-
-        private final class TvInteractiveAppEventReceiver extends InputEventReceiver {
-            TvInteractiveAppEventReceiver(InputChannel inputChannel, Looper looper) {
-                super(inputChannel, looper);
-            }
-
-            @Override
-            public void onInputEvent(InputEvent event) {
-                if (mSessionImpl == null) {
-                    // The session has been finished.
-                    finishInputEvent(event, false);
-                    return;
-                }
-
-                int handled = mSessionImpl.dispatchInputEvent(event, this);
-                if (handled != TvInteractiveAppManager.Session.DISPATCH_IN_PROGRESS) {
-                    finishInputEvent(
-                            event, handled == TvInteractiveAppManager.Session.DISPATCH_HANDLED);
-                }
-            }
-        }
-    }
-
     @SuppressLint("HandlerLeak")
     private final class ServiceHandler extends Handler {
         private static final int DO_CREATE_SESSION = 1;
diff --git a/media/jni/android_media_ImageReader.cpp b/media/jni/android_media_ImageReader.cpp
index 62c0d55..556f98c 100644
--- a/media/jni/android_media_ImageReader.cpp
+++ b/media/jni/android_media_ImageReader.cpp
@@ -643,7 +643,8 @@
     return ACQUIRE_SUCCESS;
 }
 
-static jint ImageReader_detachImage(JNIEnv* env, jobject thiz, jobject image) {
+static jint ImageReader_detachImage(JNIEnv* env, jobject thiz, jobject image,
+                                    jboolean throwISEOnly) {
     ALOGV("%s:", __FUNCTION__);
     JNIImageReaderContext* ctx = ImageReader_getContext(env, thiz);
     if (ctx == NULL) {
@@ -666,8 +667,12 @@
     res = bufferConsumer->detachBuffer(buffer->mSlot);
     if (res != OK) {
         ALOGE("Image detach failed: %s (%d)!!!", strerror(-res), res);
-        jniThrowRuntimeException(env,
-                "nativeDetachImage failed for image!!!");
+        if ((bool) throwISEOnly) {
+            jniThrowException(env, "java/lang/IllegalStateException",
+                    "nativeDetachImage failed for image!!!");
+        } else {
+             jniThrowRuntimeException(env, "nativeDetachImage failed for image!!!");
+        }
         return res;
     }
     return OK;
@@ -965,7 +970,7 @@
     {"nativeReleaseImage",     "(Landroid/media/Image;)V",   (void*)ImageReader_imageRelease },
     {"nativeImageSetup",       "(Landroid/media/Image;Z)I",   (void*)ImageReader_imageSetup },
     {"nativeGetSurface",       "()Landroid/view/Surface;",   (void*)ImageReader_getSurface },
-    {"nativeDetachImage",      "(Landroid/media/Image;)I",   (void*)ImageReader_detachImage },
+    {"nativeDetachImage",      "(Landroid/media/Image;Z)I",   (void*)ImageReader_detachImage },
     {"nativeCreateImagePlanes",
         "(ILandroid/graphics/GraphicBuffer;IIIIII)[Landroid/media/ImageReader$ImagePlane;",
                                                              (void*)ImageReader_createImagePlanes },
diff --git a/mime/java-res/android.mime.types b/mime/java-res/android.mime.types
index 60f0e9e..cb74cfc 100644
--- a/mime/java-res/android.mime.types
+++ b/mime/java-res/android.mime.types
@@ -86,6 +86,7 @@
 ?audio/x-matroska mka
 ?audio/x-pn-realaudio ra
 ?audio/x-mpeg mp3
+?audio/mp3 mp3
 
 ?image/bmp bmp
 ?image/gif gif
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/layout-v31/settingslib_action_buttons.xml b/packages/SettingsLib/ActionButtonsPreference/res/layout-v31/settingslib_action_buttons.xml
index a596a9a..7c3f5a5 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/layout-v31/settingslib_action_buttons.xml
+++ b/packages/SettingsLib/ActionButtonsPreference/res/layout-v31/settingslib_action_buttons.xml
@@ -28,8 +28,7 @@
         style="@style/SettingsLibActionButton"
         android:layout_width="0dp"
         android:layout_height="match_parent"
-        android:layout_weight="1"
-        android:hyphenationFrequency="normalFast"/>
+        android:layout_weight="1"/>
 
     <View
         android:id="@+id/divider1"
@@ -44,8 +43,7 @@
         style="@style/SettingsLibActionButton"
         android:layout_width="0dp"
         android:layout_height="match_parent"
-        android:layout_weight="1"
-        android:hyphenationFrequency="normalFast"/>
+        android:layout_weight="1"/>
 
     <View
         android:id="@+id/divider2"
@@ -60,8 +58,7 @@
         style="@style/SettingsLibActionButton"
         android:layout_width="0dp"
         android:layout_height="match_parent"
-        android:layout_weight="1"
-        android:hyphenationFrequency="normalFast"/>
+        android:layout_weight="1"/>
 
     <View
         android:id="@+id/divider3"
@@ -76,6 +73,5 @@
         style="@style/SettingsLibActionButton"
         android:layout_width="0dp"
         android:layout_height="match_parent"
-        android:layout_weight="1"
-        android:hyphenationFrequency="normalFast"/>
+        android:layout_weight="1"/>
 </LinearLayout>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/layout-v33/settingslib_action_buttons.xml b/packages/SettingsLib/ActionButtonsPreference/res/layout-v33/settingslib_action_buttons.xml
new file mode 100644
index 0000000..b4640ee
--- /dev/null
+++ b/packages/SettingsLib/ActionButtonsPreference/res/layout-v33/settingslib_action_buttons.xml
@@ -0,0 +1,85 @@
+<?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.
+  -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:layout_margin="8dp"
+    android:paddingHorizontal="8dp"
+    android:orientation="horizontal">
+
+    <Button
+        android:id="@+id/button1"
+        style="@style/SettingsLibActionButton"
+        android:layout_width="0dp"
+        android:layout_height="match_parent"
+        android:layout_weight="1"
+        android:lineBreakWordStyle="phrase"
+        android:hyphenationFrequency="normalFast"/>
+
+    <View
+        android:id="@+id/divider1"
+        android:layout_width="1dp"
+        android:layout_height="match_parent"
+        android:paddingHorizontal="4dp"
+        android:visibility="gone"
+        android:background="?android:colorBackground" />
+
+    <Button
+        android:id="@+id/button2"
+        style="@style/SettingsLibActionButton"
+        android:layout_width="0dp"
+        android:layout_height="match_parent"
+        android:layout_weight="1"
+        android:lineBreakWordStyle="phrase"
+        android:hyphenationFrequency="normalFast"/>
+
+    <View
+        android:id="@+id/divider2"
+        android:layout_width="1dp"
+        android:layout_height="match_parent"
+        android:paddingHorizontal="4dp"
+        android:visibility="gone"
+        android:background="?android:colorBackground" />
+
+    <Button
+        android:id="@+id/button3"
+        style="@style/SettingsLibActionButton"
+        android:layout_width="0dp"
+        android:layout_height="match_parent"
+        android:layout_weight="1"
+        android:lineBreakWordStyle="phrase"
+        android:hyphenationFrequency="normalFast"/>
+
+    <View
+        android:id="@+id/divider3"
+        android:layout_width="1dp"
+        android:layout_height="match_parent"
+        android:paddingHorizontal="4dp"
+        android:visibility="gone"
+        android:background="?android:colorBackground" />
+
+    <Button
+        android:id="@+id/button4"
+        style="@style/SettingsLibActionButton"
+        android:layout_width="0dp"
+        android:layout_height="match_parent"
+        android:layout_weight="1"
+        android:lineBreakWordStyle="phrase"
+        android:hyphenationFrequency="normalFast"/>
+</LinearLayout>
diff --git a/packages/SettingsLib/AppPreference/res/layout-v33/preference_app.xml b/packages/SettingsLib/AppPreference/res/layout-v33/preference_app.xml
new file mode 100644
index 0000000..8975857
--- /dev/null
+++ b/packages/SettingsLib/AppPreference/res/layout-v33/preference_app.xml
@@ -0,0 +1,100 @@
+<?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.
+  -->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:background="?android:attr/selectableItemBackground"
+    android:gravity="center_vertical"
+    android:minHeight="?android:attr/listPreferredItemHeightSmall"
+    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
+
+    <LinearLayout
+        android:id="@+id/icon_frame"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:gravity="start|center_vertical"
+        android:minWidth="@dimen/secondary_app_icon_size"
+        android:orientation="horizontal"
+        android:paddingEnd="16dp"
+        android:paddingTop="4dp"
+        android:paddingBottom="4dp">
+        <ImageView
+            android:id="@android:id/icon"
+            android:layout_width="@dimen/secondary_app_icon_size"
+            android:layout_height="@dimen/secondary_app_icon_size"/>
+    </LinearLayout>
+
+    <LinearLayout
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:orientation="vertical"
+        android:paddingTop="16dp"
+        android:paddingBottom="16dp">
+
+        <TextView
+            android:id="@android:id/title"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:ellipsize="marquee"
+            android:fadingEdge="horizontal"
+            android:maxLines="2"
+            android:hyphenationFrequency="normalFast"
+            android:lineBreakWordStyle="phrase"
+            android:textAppearance="?android:attr/textAppearanceListItem"/>
+
+        <TextView
+            android:id="@android:id/summary"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:textDirection="locale"
+            android:hyphenationFrequency="normalFast"
+            android:lineBreakWordStyle="phrase"
+            android:textAppearance="?android:attr/textAppearanceSmall"
+            android:textColor="?android:attr/textColorSecondary"/>
+
+        <TextView
+            android:id="@+id/appendix"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:ellipsize="end"
+            android:maxLines="1"
+            android:textAppearance="?android:attr/textAppearanceSmall"
+            android:textColor="?android:attr/textColorSecondary"
+            android:visibility="gone"/>
+
+        <ProgressBar
+            android:id="@android:id/progress"
+            style="?android:attr/progressBarStyleHorizontal"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="4dp"
+            android:max="100"
+            android:visibility="gone"/>
+    </LinearLayout>
+
+    <LinearLayout
+        android:id="@android:id/widget_frame"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:gravity="center_vertical|end"
+        android:minWidth="@dimen/two_target_min_width"
+        android:orientation="vertical"/>
+
+</LinearLayout>
diff --git a/packages/SettingsLib/AppPreference/res/layout-v33/preference_app_header.xml b/packages/SettingsLib/AppPreference/res/layout-v33/preference_app_header.xml
new file mode 100644
index 0000000..3a219b9
--- /dev/null
+++ b/packages/SettingsLib/AppPreference/res/layout-v33/preference_app_header.xml
@@ -0,0 +1,38 @@
+<?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.
+  -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+    android:paddingBottom="16dp"
+    android:paddingTop="8dp"
+    android:clickable="false">
+
+    <TextView
+        android:id="@+id/apps_top_intro_text"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:textDirection="locale"
+        android:clickable="false"
+        android:longClickable="false"
+        android:hyphenationFrequency="normalFast"
+        android:lineBreakWordStyle="phrase"
+        android:textAppearance="@style/TextAppearance.TopIntroText" />
+
+</LinearLayout>
diff --git a/packages/SettingsLib/AppPreference/res/layout/preference_app.xml b/packages/SettingsLib/AppPreference/res/layout/preference_app.xml
index 12db901..e65f7de 100644
--- a/packages/SettingsLib/AppPreference/res/layout/preference_app.xml
+++ b/packages/SettingsLib/AppPreference/res/layout/preference_app.xml
@@ -55,7 +55,6 @@
             android:ellipsize="marquee"
             android:fadingEdge="horizontal"
             android:maxLines="2"
-            android:hyphenationFrequency="normalFast"
             android:textAppearance="?android:attr/textAppearanceListItem"/>
 
         <TextView
@@ -63,7 +62,6 @@
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:textDirection="locale"
-            android:hyphenationFrequency="normalFast"
             android:textAppearance="?android:attr/textAppearanceSmall"
             android:textColor="?android:attr/textColorSecondary"/>
 
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarDelegate.java b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarDelegate.java
index ec091bf..f4af9c7 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarDelegate.java
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarDelegate.java
@@ -19,6 +19,7 @@
 import static android.text.Layout.HYPHENATION_FREQUENCY_NORMAL_FAST;
 
 import android.app.ActionBar;
+import android.graphics.text.LineBreakConfig;
 import android.os.Build;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -88,6 +89,12 @@
             mCollapsingToolbarLayout.setLineSpacingMultiplier(TOOLBAR_LINE_SPACING_MULTIPLIER);
             if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
                 mCollapsingToolbarLayout.setHyphenationFrequency(HYPHENATION_FREQUENCY_NORMAL_FAST);
+                mCollapsingToolbarLayout.setStaticLayoutBuilderConfigurer(builder ->
+                        builder.setLineBreakConfig(
+                                new LineBreakConfig.Builder()
+                                        .setLineBreakWordStyle(
+                                                LineBreakConfig.LINE_BREAK_WORD_STYLE_PHRASE)
+                                        .build()));
             }
         }
         disableCollapsingToolbarLayoutScrollingBehavior();
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/widget/CollapsingCoordinatorLayout.java b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/widget/CollapsingCoordinatorLayout.java
index a8c7a3f..522de93 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/widget/CollapsingCoordinatorLayout.java
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/widget/CollapsingCoordinatorLayout.java
@@ -22,6 +22,7 @@
 import android.app.Activity;
 import android.content.Context;
 import android.content.res.TypedArray;
+import android.graphics.text.LineBreakConfig;
 import android.os.Build;
 import android.text.TextUtils;
 import android.util.AttributeSet;
@@ -110,6 +111,12 @@
             mCollapsingToolbarLayout.setLineSpacingMultiplier(TOOLBAR_LINE_SPACING_MULTIPLIER);
             if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
                 mCollapsingToolbarLayout.setHyphenationFrequency(HYPHENATION_FREQUENCY_NORMAL_FAST);
+                mCollapsingToolbarLayout.setStaticLayoutBuilderConfigurer(builder ->
+                        builder.setLineBreakConfig(
+                                new LineBreakConfig.Builder()
+                                        .setLineBreakWordStyle(
+                                                LineBreakConfig.LINE_BREAK_WORD_STYLE_PHRASE)
+                                        .build()));
             }
             if (!TextUtils.isEmpty(mToolbarTitle)) {
                 mCollapsingToolbarLayout.setTitle(mToolbarTitle);
diff --git a/packages/SettingsLib/FooterPreference/res/layout-v31/preference_footer.xml b/packages/SettingsLib/FooterPreference/res/layout-v31/preference_footer.xml
index 2c1fdd4..42700b3 100644
--- a/packages/SettingsLib/FooterPreference/res/layout-v31/preference_footer.xml
+++ b/packages/SettingsLib/FooterPreference/res/layout-v31/preference_footer.xml
@@ -53,7 +53,6 @@
             android:paddingTop="16dp"
             android:paddingBottom="8dp"
             android:textColor="?android:attr/textColorSecondary"
-            android:hyphenationFrequency="normalFast"
             android:ellipsize="marquee" />
 
         <com.android.settingslib.widget.LinkTextView
diff --git a/packages/SettingsLib/FooterPreference/res/layout-v33/preference_footer.xml b/packages/SettingsLib/FooterPreference/res/layout-v33/preference_footer.xml
new file mode 100644
index 0000000..a2f2510
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/layout-v33/preference_footer.xml
@@ -0,0 +1,71 @@
+<?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.
+  -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:minHeight="?android:attr/listPreferredItemHeight"
+    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+    android:background="?android:attr/selectableItemBackground"
+    android:orientation="vertical"
+    android:clipToPadding="false">
+
+    <LinearLayout
+        android:id="@+id/icon_frame"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:minWidth="56dp"
+        android:gravity="start|top"
+        android:orientation="horizontal"
+        android:paddingEnd="12dp"
+        android:paddingTop="16dp"
+        android:paddingBottom="4dp">
+        <ImageView
+            android:id="@android:id/icon"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"/>
+    </LinearLayout>
+
+    <LinearLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:orientation="vertical">
+        <TextView
+            android:id="@android:id/title"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:paddingTop="16dp"
+            android:paddingBottom="8dp"
+            android:textColor="?android:attr/textColorSecondary"
+            android:hyphenationFrequency="normalFast"
+            android:lineBreakWordStyle="phrase"
+            android:ellipsize="marquee" />
+
+        <com.android.settingslib.widget.LinkTextView
+            android:id="@+id/settingslib_learn_more"
+            android:text="@string/settingslib_learn_more_text"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:paddingBottom="8dp"
+            android:clickable="true"
+            android:visibility="gone"
+            style="@style/TextAppearance.Footer.Title.SettingsLib"/>
+    </LinearLayout>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SettingsLib/MainSwitchPreference/res/layout-v33/settingslib_main_switch_bar.xml b/packages/SettingsLib/MainSwitchPreference/res/layout-v33/settingslib_main_switch_bar.xml
new file mode 100644
index 0000000..35d1323
--- /dev/null
+++ b/packages/SettingsLib/MainSwitchPreference/res/layout-v33/settingslib_main_switch_bar.xml
@@ -0,0 +1,70 @@
+<?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.
+  -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_height="wrap_content"
+    android:layout_width="match_parent"
+    android:background="?android:attr/colorBackground"
+    android:orientation="vertical">
+
+    <LinearLayout
+        android:id="@+id/frame"
+        android:minHeight="@dimen/settingslib_min_switch_bar_height"
+        android:layout_height="wrap_content"
+        android:layout_width="match_parent"
+        android:layout_margin="@dimen/settingslib_switchbar_margin"
+        android:paddingStart="@dimen/settingslib_switchbar_padding_left"
+        android:paddingEnd="@dimen/settingslib_switchbar_padding_right">
+
+        <TextView
+            android:id="@+id/switch_text"
+            android:layout_height="wrap_content"
+            android:layout_width="0dp"
+            android:layout_weight="1"
+            android:layout_marginEnd="@dimen/settingslib_switch_title_margin"
+            android:layout_marginVertical="@dimen/settingslib_switch_title_margin"
+            android:layout_gravity="center_vertical"
+            android:ellipsize="end"
+            android:textAppearance="?android:attr/textAppearanceListItem"
+            android:hyphenationFrequency="normalFast"
+            android:lineBreakWordStyle="phrase"
+            style="@style/MainSwitchText.Settingslib" />
+
+        <ImageView
+            android:id="@+id/restricted_icon"
+            android:layout_width="@dimen/settingslib_restricted_icon_size"
+            android:layout_height="@dimen/settingslib_restricted_icon_size"
+            android:tint="?android:attr/colorAccent"
+            android:layout_gravity="center_vertical"
+            android:layout_marginEnd="@dimen/settingslib_restricted_icon_margin_end"
+            android:src="@drawable/settingslib_ic_info"
+            android:visibility="gone" />
+
+        <Switch
+            android:id="@android:id/switch_widget"
+            android:layout_width="wrap_content"
+            android:layout_height="48dp"
+            android:layout_gravity="center_vertical"
+            android:focusable="false"
+            android:clickable="false"
+            android:theme="@style/Switch.SettingsLib"/>
+    </LinearLayout>
+
+</LinearLayout>
+
+
diff --git a/packages/SettingsLib/RadioButtonPreference/res/layout-v33/preference_radio.xml b/packages/SettingsLib/RadioButtonPreference/res/layout-v33/preference_radio.xml
new file mode 100644
index 0000000..bb8ac28
--- /dev/null
+++ b/packages/SettingsLib/RadioButtonPreference/res/layout-v33/preference_radio.xml
@@ -0,0 +1,127 @@
+<?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.
+  -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:settings="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:background="?android:attr/selectableItemBackground"
+    android:gravity="center_vertical"
+    android:minHeight="?android:attr/listPreferredItemHeightSmall"
+    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
+
+    <LinearLayout
+        android:id="@android:id/widget_frame"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:paddingHorizontal="20dp"
+        android:gravity="center"
+        android:minWidth="56dp"
+        android:orientation="vertical"/>
+
+    <LinearLayout
+        android:id="@+id/icon_frame"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:gravity="center_vertical"
+        android:minWidth="32dp"
+        android:orientation="horizontal"
+        android:layout_marginEnd="16dp"
+        android:paddingTop="4dp"
+        android:paddingBottom="4dp">
+        <androidx.preference.internal.PreferenceImageView
+            android:id="@android:id/icon"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            settings:maxWidth="@dimen/secondary_app_icon_size"
+            settings:maxHeight="@dimen/secondary_app_icon_size"/>
+    </LinearLayout>
+
+    <LinearLayout
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:orientation="vertical"
+        android:paddingTop="16dp"
+        android:paddingBottom="16dp"
+        android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
+
+        <TextView
+            android:id="@android:id/title"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:maxLines="2"
+            android:hyphenationFrequency="normalFast"
+            android:lineBreakWordStyle="phrase"
+            android:textAppearance="?android:attr/textAppearanceListItem"/>
+
+        <LinearLayout
+            android:id="@+id/summary_container"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:visibility="gone">
+            <TextView
+                android:id="@android:id/summary"
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:layout_weight="1"
+                android:textAppearance="?android:attr/textAppearanceSmall"
+                android:textAlignment="viewStart"
+                android:hyphenationFrequency="normalFast"
+                android:lineBreakWordStyle="phrase"
+                android:textColor="?android:attr/textColorSecondary"/>
+
+            <TextView
+                android:id="@+id/appendix"
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:layout_weight="1"
+                android:textAppearance="?android:attr/textAppearanceSmall"
+                android:textAlignment="viewEnd"
+                android:textColor="?android:attr/textColorSecondary"
+                android:maxLines="1"
+                android:visibility="gone"
+                android:ellipsize="end"/>
+        </LinearLayout>
+    </LinearLayout>
+
+    <LinearLayout
+        android:id="@+id/radio_extra_widget_container"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:orientation="horizontal"
+        android:gravity="center_vertical">
+        <View
+            android:layout_width=".75dp"
+            android:layout_height="32dp"
+            android:layout_marginTop="16dp"
+            android:layout_marginBottom="16dp"
+            android:background="?android:attr/dividerVertical" />
+        <ImageView
+            android:id="@+id/radio_extra_widget"
+            android:layout_width="match_parent"
+            android:minWidth="@dimen/two_target_min_width"
+            android:layout_height="fill_parent"
+            android:src="@drawable/ic_settings_accent"
+            android:contentDescription="@string/settings_label"
+            android:paddingStart="24dp"
+            android:paddingEnd="24dp"
+            android:layout_gravity="center"
+            android:background="?android:attr/selectableItemBackground" />
+    </LinearLayout>
+</LinearLayout>
diff --git a/packages/SettingsLib/RadioButtonPreference/res/layout/preference_radio.xml b/packages/SettingsLib/RadioButtonPreference/res/layout/preference_radio.xml
index 64d100a..906ff2c 100644
--- a/packages/SettingsLib/RadioButtonPreference/res/layout/preference_radio.xml
+++ b/packages/SettingsLib/RadioButtonPreference/res/layout/preference_radio.xml
@@ -66,7 +66,6 @@
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:maxLines="2"
-            android:hyphenationFrequency="normalFast"
             android:textAppearance="?android:attr/textAppearanceListItem"/>
 
         <LinearLayout
@@ -81,7 +80,6 @@
                 android:layout_weight="1"
                 android:textAppearance="?android:attr/textAppearanceSmall"
                 android:textAlignment="viewStart"
-                android:hyphenationFrequency="normalFast"
                 android:textColor="?android:attr/textColorSecondary"/>
 
             <TextView
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/layout-v33/preference_selector_with_widget.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/layout-v33/preference_selector_with_widget.xml
new file mode 100644
index 0000000..0b27464
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/layout-v33/preference_selector_with_widget.xml
@@ -0,0 +1,127 @@
+<?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.
+  -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:settings="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:background="?android:attr/selectableItemBackground"
+    android:gravity="center_vertical"
+    android:minHeight="?android:attr/listPreferredItemHeightSmall"
+    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
+
+    <LinearLayout
+        android:id="@android:id/widget_frame"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:paddingHorizontal="20dp"
+        android:gravity="center"
+        android:minWidth="56dp"
+        android:orientation="vertical"/>
+
+    <LinearLayout
+        android:id="@+id/icon_frame"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:gravity="center_vertical"
+        android:minWidth="32dp"
+        android:orientation="horizontal"
+        android:layout_marginEnd="16dp"
+        android:paddingTop="4dp"
+        android:paddingBottom="4dp">
+        <androidx.preference.internal.PreferenceImageView
+            android:id="@android:id/icon"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            settings:maxWidth="@dimen/secondary_app_icon_size"
+            settings:maxHeight="@dimen/secondary_app_icon_size"/>
+    </LinearLayout>
+
+    <LinearLayout
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:orientation="vertical"
+        android:paddingTop="16dp"
+        android:paddingBottom="16dp"
+        android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
+
+        <TextView
+            android:id="@android:id/title"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:maxLines="2"
+            android:hyphenationFrequency="normalFast"
+            android:lineBreakWordStyle="phrase"
+            android:textAppearance="?android:attr/textAppearanceListItem"/>
+
+        <LinearLayout
+            android:id="@+id/summary_container"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:visibility="gone">
+            <TextView
+                android:id="@android:id/summary"
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:layout_weight="1"
+                android:textAppearance="?android:attr/textAppearanceSmall"
+                android:textAlignment="viewStart"
+                android:hyphenationFrequency="normalFast"
+                android:lineBreakWordStyle="phrase"
+                android:textColor="?android:attr/textColorSecondary"/>
+
+            <TextView
+                android:id="@+id/appendix"
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:layout_weight="1"
+                android:textAppearance="?android:attr/textAppearanceSmall"
+                android:textAlignment="viewEnd"
+                android:textColor="?android:attr/textColorSecondary"
+                android:maxLines="1"
+                android:visibility="gone"
+                android:ellipsize="end"/>
+        </LinearLayout>
+    </LinearLayout>
+
+    <LinearLayout
+        android:id="@+id/selector_extra_widget_container"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:orientation="horizontal"
+        android:gravity="center_vertical">
+        <View
+            android:layout_width=".75dp"
+            android:layout_height="32dp"
+            android:layout_marginTop="16dp"
+            android:layout_marginBottom="16dp"
+            android:background="?android:attr/dividerVertical" />
+        <ImageView
+            android:id="@+id/selector_extra_widget"
+            android:layout_width="match_parent"
+            android:minWidth="@dimen/two_target_min_width"
+            android:layout_height="fill_parent"
+            android:src="@drawable/ic_settings_accent"
+            android:contentDescription="@string/settings_label"
+            android:paddingStart="24dp"
+            android:paddingEnd="24dp"
+            android:layout_gravity="center"
+            android:background="?android:attr/selectableItemBackground" />
+    </LinearLayout>
+</LinearLayout>
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/layout/preference_selector_with_widget.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/layout/preference_selector_with_widget.xml
index 2a550ef..8bb56ff 100644
--- a/packages/SettingsLib/SelectorWithWidgetPreference/res/layout/preference_selector_with_widget.xml
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/layout/preference_selector_with_widget.xml
@@ -66,7 +66,6 @@
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:maxLines="2"
-            android:hyphenationFrequency="normalFast"
             android:textAppearance="?android:attr/textAppearanceListItem"/>
 
         <LinearLayout
@@ -81,7 +80,6 @@
                 android:layout_weight="1"
                 android:textAppearance="?android:attr/textAppearanceSmall"
                 android:textAlignment="viewStart"
-                android:hyphenationFrequency="normalFast"
                 android:textColor="?android:attr/textColorSecondary"/>
 
             <TextView
diff --git a/packages/SettingsLib/SettingsTheme/res/layout-v31/settingslib_preference.xml b/packages/SettingsLib/SettingsTheme/res/layout-v31/settingslib_preference.xml
index d4d466a..23aa993 100644
--- a/packages/SettingsLib/SettingsTheme/res/layout-v31/settingslib_preference.xml
+++ b/packages/SettingsLib/SettingsTheme/res/layout-v31/settingslib_preference.xml
@@ -43,7 +43,6 @@
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:maxLines="2"
-            android:hyphenationFrequency="normalFast"
             android:textAppearance="?android:attr/textAppearanceListItem"
             android:ellipsize="marquee"/>
 
@@ -58,7 +57,6 @@
             android:textAlignment="viewStart"
             android:textColor="?android:attr/textColorSecondary"
             android:maxLines="10"
-            android:hyphenationFrequency="normalFast"
             style="@style/PreferenceSummaryTextStyle"/>
 
     </RelativeLayout>
diff --git a/packages/SettingsLib/SettingsTheme/res/layout-v33/settingslib_preference.xml b/packages/SettingsLib/SettingsTheme/res/layout-v33/settingslib_preference.xml
new file mode 100644
index 0000000..70ce374
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/res/layout-v33/settingslib_preference.xml
@@ -0,0 +1,80 @@
+<?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.
+  -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:minHeight="?android:attr/listPreferredItemHeightSmall"
+    android:gravity="center_vertical"
+    android:paddingLeft="?android:attr/listPreferredItemPaddingLeft"
+    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+    android:paddingRight="?android:attr/listPreferredItemPaddingRight"
+    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+    android:background="?android:attr/selectableItemBackground"
+    android:clipToPadding="false"
+    android:baselineAligned="false">
+
+    <include layout="@layout/settingslib_icon_frame"/>
+
+    <RelativeLayout
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:paddingTop="16dp"
+        android:paddingBottom="16dp">
+
+        <TextView
+            android:id="@android:id/title"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:maxLines="2"
+            android:hyphenationFrequency="normalFast"
+            android:lineBreakWordStyle="phrase"
+            android:textAppearance="?android:attr/textAppearanceListItem"
+            android:ellipsize="marquee"/>
+
+        <TextView
+            android:id="@android:id/summary"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_below="@android:id/title"
+            android:layout_alignLeft="@android:id/title"
+            android:layout_alignStart="@android:id/title"
+            android:layout_gravity="start"
+            android:textAlignment="viewStart"
+            android:textColor="?android:attr/textColorSecondary"
+            android:maxLines="10"
+            android:hyphenationFrequency="normalFast"
+            android:lineBreakWordStyle="phrase"
+            style="@style/PreferenceSummaryTextStyle"/>
+
+    </RelativeLayout>
+
+    <!-- Preference should place its actual preference widget here. -->
+    <LinearLayout
+        android:id="@android:id/widget_frame"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:gravity="end|center_vertical"
+        android:paddingLeft="16dp"
+        android:paddingStart="16dp"
+        android:paddingRight="0dp"
+        android:paddingEnd="0dp"
+        android:orientation="vertical"/>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SettingsLib/Spa/.gitignore b/packages/SettingsLib/Spa/.gitignore
new file mode 100644
index 0000000..b2ed268
--- /dev/null
+++ b/packages/SettingsLib/Spa/.gitignore
@@ -0,0 +1,15 @@
+*.iml
+.gradle
+/local.properties
+/.idea/caches
+/.idea/libraries
+/.idea/modules.xml
+/.idea/workspace.xml
+/.idea/navEditor.xml
+/.idea/assetWizardSettings.xml
+.DS_Store
+build
+/captures
+.externalNativeBuild
+.cxx
+local.properties
diff --git a/packages/SettingsLib/Spa/OWNERS b/packages/SettingsLib/Spa/OWNERS
new file mode 100644
index 0000000..b352b04
--- /dev/null
+++ b/packages/SettingsLib/Spa/OWNERS
@@ -0,0 +1,6 @@
+chaohuiw@google.com
+hanxu@google.com
+kellyz@google.com
+pierreqian@google.com
+
+per-file *.xml = set noparent
diff --git a/packages/SettingsLib/Spa/TEST_MAPPING b/packages/SettingsLib/Spa/TEST_MAPPING
new file mode 100644
index 0000000..ef3db4a
--- /dev/null
+++ b/packages/SettingsLib/Spa/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit": [
+    {
+      "name": "SpaLibTests"
+    }
+  ]
+}
diff --git a/packages/SettingsLib/Spa/build.gradle b/packages/SettingsLib/Spa/build.gradle
new file mode 100644
index 0000000..8c97eca
--- /dev/null
+++ b/packages/SettingsLib/Spa/build.gradle
@@ -0,0 +1,28 @@
+/*
+ * 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.
+ */
+
+buildscript {
+    ext {
+        minSdk_version = 31
+        compose_version = '1.2.0-alpha04'
+        compose_material3_version = '1.0.0-alpha06'
+    }
+}
+plugins {
+    id 'com.android.application' version '7.3.0-beta04' apply false
+    id 'com.android.library' version '7.3.0-beta04' apply false
+    id 'org.jetbrains.kotlin.android' version '1.6.10' apply false
+}
diff --git a/packages/SettingsLib/Spa/codelab/Android.bp b/packages/SettingsLib/Spa/codelab/Android.bp
new file mode 100644
index 0000000..8fbbf9a
--- /dev/null
+++ b/packages/SettingsLib/Spa/codelab/Android.bp
@@ -0,0 +1,33 @@
+//
+// Copyright (C) 2022 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+package {
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_app {
+    name: "SpaLibCodelab",
+
+    srcs: ["src/**/*.kt"],
+
+    static_libs: [
+        "SpaLib",
+        "androidx.compose.runtime_runtime",
+    ],
+    kotlincflags: ["-Xjvm-default=all"],
+    platform_apis: true,
+    min_sdk_version: "31",
+}
diff --git a/packages/SettingsLib/Spa/codelab/AndroidManifest.xml b/packages/SettingsLib/Spa/codelab/AndroidManifest.xml
new file mode 100644
index 0000000..9a89e5e
--- /dev/null
+++ b/packages/SettingsLib/Spa/codelab/AndroidManifest.xml
@@ -0,0 +1,34 @@
+<?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.
+  -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.settingslib.spa.codelab">
+
+    <application
+        android:label="@string/app_name"
+        android:supportsRtl="true"
+        android:theme="@style/Theme.SettingsLib.Compose.DayNight">
+        <activity
+            android:name="com.android.settingslib.spa.codelab.MainActivity"
+            android:exported="true">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+
+</manifest>
diff --git a/packages/SettingsLib/Spa/codelab/build.gradle b/packages/SettingsLib/Spa/codelab/build.gradle
new file mode 100644
index 0000000..5251ddd
--- /dev/null
+++ b/packages/SettingsLib/Spa/codelab/build.gradle
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ */
+
+plugins {
+    id 'com.android.application'
+    id 'kotlin-android'
+}
+
+android {
+    namespace 'com.android.settingslib.spa.codelab'
+    compileSdk 33
+
+    defaultConfig {
+        applicationId "com.android.settingslib.spa.codelab"
+        minSdk minSdk_version
+        targetSdk 33
+        versionCode 1
+        versionName "1.0"
+    }
+
+    sourceSets {
+        main {
+            kotlin {
+                srcDir "src"
+            }
+            res.srcDirs = ["res"]
+            manifest.srcFile "AndroidManifest.xml"
+        }
+    }
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_1_8
+        targetCompatibility JavaVersion.VERSION_1_8
+    }
+    kotlinOptions {
+        jvmTarget = '1.8'
+        freeCompilerArgs = ["-Xjvm-default=all"]
+    }
+    buildFeatures {
+        compose true
+    }
+    composeOptions {
+        kotlinCompilerExtensionVersion compose_version
+    }
+    packagingOptions {
+        resources {
+            excludes += '/META-INF/{AL2.0,LGPL2.1}'
+        }
+    }
+}
+
+dependencies {
+    implementation(project(":spa"))
+}
diff --git a/packages/SettingsLib/Spa/codelab/res/values/strings.xml b/packages/SettingsLib/Spa/codelab/res/values/strings.xml
new file mode 100644
index 0000000..6fdbba0
--- /dev/null
+++ b/packages/SettingsLib/Spa/codelab/res/values/strings.xml
@@ -0,0 +1,19 @@
+<!--
+  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>
+    <!-- Codelab App name. [DO NOT TRANSLATE] -->
+    <string name="app_name" translatable="false">SpaLib Codelab</string>
+</resources>
diff --git a/packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/MainActivity.kt b/packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/MainActivity.kt
new file mode 100644
index 0000000..eef81e0
--- /dev/null
+++ b/packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/MainActivity.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spa.codelab
+
+import com.android.settingslib.spa.codelab.page.codelabPageRepository
+import com.android.settingslib.spa.framework.SpaActivity
+
+class MainActivity : SpaActivity(codelabPageRepository)
diff --git a/packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/page/ArgumentPage.kt b/packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/page/ArgumentPage.kt
new file mode 100644
index 0000000..484b604
--- /dev/null
+++ b/packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/page/ArgumentPage.kt
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spa.codelab.page
+
+import android.os.Bundle
+import androidx.compose.foundation.layout.Column
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.navigation.NavType
+import androidx.navigation.navArgument
+import com.android.settingslib.spa.api.SettingsPageProvider
+import com.android.settingslib.spa.framework.navigator
+import com.android.settingslib.spa.framework.toState
+import com.android.settingslib.spa.theme.SettingsTheme
+import com.android.settingslib.spa.widget.preference.Preference
+import com.android.settingslib.spa.widget.preference.PreferenceModel
+
+private const val STRING_PARAM_NAME = "stringParam"
+private const val INT_PARAM_NAME = "intParam"
+
+object ArgumentPageProvider : SettingsPageProvider {
+    override val name = Destinations.Argument
+
+    override val arguments = listOf(
+        navArgument(STRING_PARAM_NAME) { type = NavType.StringType },
+        navArgument(INT_PARAM_NAME) { type = NavType.IntType },
+    )
+
+    @Composable
+    override fun Page(arguments: Bundle?) {
+        ArgumentPage(
+            stringParam = arguments!!.getString(STRING_PARAM_NAME, "default"),
+            intParam = arguments.getInt(INT_PARAM_NAME),
+        )
+    }
+
+    @Composable
+    fun EntryItem(stringParam: String, intParam: Int) {
+        Preference(object : PreferenceModel {
+            override val title = "Sample page with arguments"
+            override val summary =
+                "$STRING_PARAM_NAME=$stringParam, $INT_PARAM_NAME=$intParam".toState()
+            override val onClick = navigator("${Destinations.Argument}/$stringParam/$intParam")
+        })
+    }
+}
+
+@Composable
+fun ArgumentPage(stringParam: String, intParam: Int) {
+    Column {
+        Preference(object : PreferenceModel {
+            override val title = "String param value"
+            override val summary = stringParam.toState()
+        })
+
+        Preference(object : PreferenceModel {
+            override val title = "Int param value"
+            override val summary = intParam.toString().toState()
+        })
+
+        ArgumentPageProvider.EntryItem(stringParam = "foo", intParam = intParam + 1)
+
+        ArgumentPageProvider.EntryItem(stringParam = "bar", intParam = intParam + 1)
+    }
+}
+
+@Preview(showBackground = true)
+@Composable
+private fun ArgumentPagePreview() {
+    SettingsTheme {
+        ArgumentPage(stringParam = "foo", intParam = 0)
+    }
+}
diff --git a/packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/page/HomePage.kt b/packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/page/HomePage.kt
new file mode 100644
index 0000000..27c70e4
--- /dev/null
+++ b/packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/page/HomePage.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.settingslib.spa.codelab.page
+
+import android.os.Bundle
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.tooling.preview.Preview
+import com.android.settingslib.spa.api.SettingsPageProvider
+import com.android.settingslib.spa.codelab.R
+import com.android.settingslib.spa.theme.SettingsDimension
+import com.android.settingslib.spa.theme.SettingsTheme
+
+object HomePageProvider : SettingsPageProvider {
+    override val name = Destinations.Home
+
+    @Composable
+    override fun Page(arguments: Bundle?) {
+        HomePage()
+    }
+}
+
+@Composable
+private fun HomePage() {
+    Column {
+        Text(
+            text = stringResource(R.string.app_name),
+            modifier = Modifier.padding(SettingsDimension.itemPadding),
+            color = MaterialTheme.colorScheme.onSurface,
+            style = MaterialTheme.typography.headlineMedium,
+        )
+
+        PreferencePageProvider.EntryItem()
+
+        ArgumentPageProvider.EntryItem(stringParam = "foo", intParam = 0)
+    }
+}
+
+@Preview(showBackground = true)
+@Composable
+private fun HomeScreenPreview() {
+    SettingsTheme {
+        HomePage()
+    }
+}
diff --git a/packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/page/PageRepository.kt b/packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/page/PageRepository.kt
new file mode 100644
index 0000000..862fc1e
--- /dev/null
+++ b/packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/page/PageRepository.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spa.codelab.page
+
+import com.android.settingslib.spa.api.SettingsPageRepository
+
+object Destinations {
+    const val Home = "Home"
+    const val Preference = "Preference"
+    const val Argument = "Argument"
+}
+
+val codelabPageRepository = SettingsPageRepository(
+    allPages = listOf(HomePageProvider, PreferencePageProvider, ArgumentPageProvider),
+    startDestination = Destinations.Home,
+)
diff --git a/packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/page/PreferencePage.kt b/packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/page/PreferencePage.kt
new file mode 100644
index 0000000..81627f6
--- /dev/null
+++ b/packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/page/PreferencePage.kt
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spa.codelab.page
+
+import android.os.Bundle
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.verticalScroll
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.derivedStateOf
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.produceState
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.tooling.preview.Preview
+import com.android.settingslib.spa.api.SettingsPageProvider
+import com.android.settingslib.spa.framework.navigator
+import com.android.settingslib.spa.framework.toState
+import com.android.settingslib.spa.theme.SettingsTheme
+import com.android.settingslib.spa.widget.preference.Preference
+import com.android.settingslib.spa.widget.preference.PreferenceModel
+import kotlinx.coroutines.delay
+
+object PreferencePageProvider : SettingsPageProvider {
+    override val name = Destinations.Preference
+
+    @Composable
+    override fun Page(arguments: Bundle?) {
+        PreferencePage()
+    }
+
+    @Composable
+    fun EntryItem() {
+        Preference(object : PreferenceModel {
+            override val title = "Sample Preference"
+            override val onClick = navigator(Destinations.Preference)
+        })
+    }
+}
+
+@Composable
+private fun PreferencePage() {
+    Column(Modifier.verticalScroll(rememberScrollState())) {
+        Preference(object : PreferenceModel {
+            override val title = "Preference"
+        })
+
+        Preference(object : PreferenceModel {
+            override val title = "Preference"
+            override val summary = "With summary".toState()
+        })
+
+        Preference(object : PreferenceModel {
+            override val title = "Preference"
+            override val summary = produceState(initialValue = " ") {
+                delay(1000L)
+                value = "Async summary"
+            }
+        })
+
+        var count by remember { mutableStateOf(0) }
+        Preference(object : PreferenceModel {
+            override val title = "Click me"
+            override val summary = derivedStateOf { count.toString() }
+            override val onClick: (() -> Unit) = { count++ }
+        })
+
+        var ticks by remember { mutableStateOf(0) }
+        LaunchedEffect(ticks) {
+            delay(1000L)
+            ticks++
+        }
+        Preference(object : PreferenceModel {
+            override val title = "Ticker"
+            override val summary = derivedStateOf { ticks.toString() }
+        })
+
+        Preference(object : PreferenceModel {
+            override val title = "Disabled"
+            override val summary = "Disabled".toState()
+            override val enabled = false.toState()
+        })
+    }
+}
+
+@Preview(showBackground = true)
+@Composable
+private fun PreferencePagePreview() {
+    SettingsTheme {
+        PreferencePage()
+    }
+}
diff --git a/packages/SettingsLib/Spa/gradle.properties b/packages/SettingsLib/Spa/gradle.properties
new file mode 100644
index 0000000..82bd512
--- /dev/null
+++ b/packages/SettingsLib/Spa/gradle.properties
@@ -0,0 +1,39 @@
+#
+# 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.
+#
+
+# Project-wide Gradle settings.
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
+# AndroidX package structure to make it clearer which packages are bundled with the
+# Android operating system, and which are packaged with your app's APK
+# https://developer.android.com/topic/libraries/support-library/androidx-rn
+android.useAndroidX=true
+# Kotlin code style for this project: "official" or "obsolete":
+kotlin.code.style=official
+# Enables namespacing of each library's R class so that its R class includes only the
+# resources declared in the library itself and none from the library's dependencies,
+# thereby reducing the size of the R class for that library
+android.nonTransitiveRClass=true
\ No newline at end of file
diff --git a/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.jar b/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..e708b1c
--- /dev/null
+++ b/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties b/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..52095e1
--- /dev/null
+++ b/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,22 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+#Thu Jul 14 10:36:06 CST 2022
+distributionBase=GRADLE_USER_HOME
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip
+distributionPath=wrapper/dists
+zipStorePath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
diff --git a/packages/SettingsLib/Spa/gradlew b/packages/SettingsLib/Spa/gradlew
new file mode 100755
index 0000000..4f906e0
--- /dev/null
+++ b/packages/SettingsLib/Spa/gradlew
@@ -0,0 +1,185 @@
+#!/usr/bin/env sh
+
+#
+# Copyright 2015 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##############################################################################
+##
+##  Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+    ls=`ls -ld "$PRG"`
+    link=`expr "$ls" : '.*-> \(.*\)$'`
+    if expr "$link" : '/.*' > /dev/null; then
+        PRG="$link"
+    else
+        PRG=`dirname "$PRG"`"/$link"
+    fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+    echo "$*"
+}
+
+die () {
+    echo
+    echo "$*"
+    echo
+    exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+  CYGWIN* )
+    cygwin=true
+    ;;
+  Darwin* )
+    darwin=true
+    ;;
+  MINGW* )
+    msys=true
+    ;;
+  NONSTOP* )
+    nonstop=true
+    ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+        # IBM's JDK on AIX uses strange locations for the executables
+        JAVACMD="$JAVA_HOME/jre/sh/java"
+    else
+        JAVACMD="$JAVA_HOME/bin/java"
+    fi
+    if [ ! -x "$JAVACMD" ] ; then
+        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+    fi
+else
+    JAVACMD="java"
+    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+    MAX_FD_LIMIT=`ulimit -H -n`
+    if [ $? -eq 0 ] ; then
+        if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+            MAX_FD="$MAX_FD_LIMIT"
+        fi
+        ulimit -n $MAX_FD
+        if [ $? -ne 0 ] ; then
+            warn "Could not set maximum file descriptor limit: $MAX_FD"
+        fi
+    else
+        warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+    fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+    GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
+    APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+    CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+    JAVACMD=`cygpath --unix "$JAVACMD"`
+
+    # We build the pattern for arguments to be converted via cygpath
+    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+    SEP=""
+    for dir in $ROOTDIRSRAW ; do
+        ROOTDIRS="$ROOTDIRS$SEP$dir"
+        SEP="|"
+    done
+    OURCYGPATTERN="(^($ROOTDIRS))"
+    # Add a user-defined pattern to the cygpath arguments
+    if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+        OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+    fi
+    # Now convert the arguments - kludge to limit ourselves to /bin/sh
+    i=0
+    for arg in "$@" ; do
+        CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+        CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### Determine if an option
+
+        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition
+            eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+        else
+            eval `echo args$i`="\"$arg\""
+        fi
+        i=`expr $i + 1`
+    done
+    case $i in
+        0) set -- ;;
+        1) set -- "$args0" ;;
+        2) set -- "$args0" "$args1" ;;
+        3) set -- "$args0" "$args1" "$args2" ;;
+        4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+        5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+        6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+        7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+        8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+        9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+    esac
+fi
+
+# Escape application args
+save () {
+    for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+    echo " "
+}
+APP_ARGS=`save "$@"`
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+exec "$JAVACMD" "$@"
diff --git a/packages/SettingsLib/Spa/gradlew.bat b/packages/SettingsLib/Spa/gradlew.bat
new file mode 100644
index 0000000..107acd32
--- /dev/null
+++ b/packages/SettingsLib/Spa/gradlew.bat
@@ -0,0 +1,89 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem      https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem  Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if  not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/packages/SettingsLib/Spa/settings.gradle b/packages/SettingsLib/Spa/settings.gradle
new file mode 100644
index 0000000..1b69c1e
--- /dev/null
+++ b/packages/SettingsLib/Spa/settings.gradle
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+
+pluginManagement {
+    repositories {
+        gradlePluginPortal()
+        google()
+        mavenCentral()
+    }
+}
+dependencyResolutionManagement {
+    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
+    repositories {
+        google()
+        mavenCentral()
+    }
+}
+rootProject.name = "SpaLib"
+include ':spa'
+include ':codelab'
+include ':tests'
diff --git a/packages/SettingsLib/Spa/spa/Android.bp b/packages/SettingsLib/Spa/spa/Android.bp
new file mode 100644
index 0000000..463c076
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/Android.bp
@@ -0,0 +1,36 @@
+//
+// 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 {
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_library {
+    name: "SpaLib",
+
+    srcs: ["src/**/*.kt"],
+
+    static_libs: [
+        "androidx.compose.material3_material3",
+        "androidx.compose.material_material-icons-extended",
+        "androidx.compose.runtime_runtime",
+        "androidx.compose.ui_ui-tooling-preview",
+        "androidx.navigation_navigation-compose",
+        "com.google.android.material_material",
+    ],
+    kotlincflags: ["-Xjvm-default=all"],
+    min_sdk_version: "31",
+}
diff --git a/packages/SettingsLib/Spa/spa/AndroidManifest.xml b/packages/SettingsLib/Spa/spa/AndroidManifest.xml
new file mode 100644
index 0000000..410bcdb
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/AndroidManifest.xml
@@ -0,0 +1,17 @@
+<?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.
+  -->
+
+<manifest package="com.android.settingslib.spa" />
diff --git a/packages/SettingsLib/Spa/spa/build.gradle b/packages/SettingsLib/Spa/spa/build.gradle
new file mode 100644
index 0000000..60794c8
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/build.gradle
@@ -0,0 +1,69 @@
+/*
+ * 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.
+ */
+
+plugins {
+    id 'com.android.library'
+    id 'kotlin-android'
+}
+
+android {
+    namespace 'com.android.settingslib.spa'
+    compileSdk 33
+
+    defaultConfig {
+        minSdk minSdk_version
+        targetSdk 33
+    }
+
+    sourceSets {
+        main {
+            kotlin {
+                srcDir "src"
+            }
+            res.srcDirs = ["res"]
+            manifest.srcFile "AndroidManifest.xml"
+        }
+    }
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_1_8
+        targetCompatibility JavaVersion.VERSION_1_8
+    }
+    kotlinOptions {
+        jvmTarget = '1.8'
+        freeCompilerArgs = ["-Xjvm-default=all"]
+    }
+    buildFeatures {
+        compose true
+    }
+    composeOptions {
+        kotlinCompilerExtensionVersion compose_version
+    }
+    packagingOptions {
+        resources {
+            excludes += '/META-INF/{AL2.0,LGPL2.1}'
+        }
+    }
+}
+
+dependencies {
+    api "androidx.compose.material3:material3:$compose_material3_version"
+    api "androidx.compose.material:material-icons-extended:$compose_version"
+    api "androidx.compose.runtime:runtime-livedata:$compose_version"
+    api "androidx.compose.ui:ui-tooling-preview:$compose_version"
+    api 'androidx.navigation:navigation-compose:2.5.0'
+    api 'com.google.android.material:material:1.6.1'
+    debugApi "androidx.compose.ui:ui-tooling:$compose_version"
+}
diff --git a/packages/SettingsLib/Spa/spa/res/values-night/themes.xml b/packages/SettingsLib/Spa/spa/res/values-night/themes.xml
new file mode 100644
index 0000000..8b52b50
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/res/values-night/themes.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2022 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+<resources>
+
+    <style name="Theme.SettingsLib.Compose.DayNight" />
+</resources>
diff --git a/packages/SettingsLib/Spa/spa/res/values/themes.xml b/packages/SettingsLib/Spa/spa/res/values/themes.xml
new file mode 100644
index 0000000..01f9ea5
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/res/values/themes.xml
@@ -0,0 +1,27 @@
+<?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="Theme.SettingsLib.Compose" parent="Theme.Material3.DayNight.NoActionBar">
+        <item name="android:statusBarColor">@android:color/transparent</item>
+        <item name="android:navigationBarColor">@android:color/transparent</item>
+    </style>
+
+    <style name="Theme.SettingsLib.Compose.DayNight">
+        <item name="android:windowLightStatusBar">true</item>
+    </style>
+</resources>
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/api/SettingsPageProvider.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/api/SettingsPageProvider.kt
new file mode 100644
index 0000000..0ad0003
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/api/SettingsPageProvider.kt
@@ -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.settingslib.spa.api
+
+import android.os.Bundle
+import androidx.compose.runtime.Composable
+import androidx.navigation.NamedNavArgument
+
+/**
+ * An SettingsPageProvider represent a Settings page.
+ */
+interface SettingsPageProvider {
+
+    /** The page name without arguments. */
+    val name: String
+
+    /** The page arguments, default is no arguments. */
+    val arguments: List<NamedNavArgument>
+        get() = emptyList()
+
+    /** The [Composable] used to render this page. */
+    @Composable
+    fun Page(arguments: Bundle?)
+}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/api/SettingsPageRepository.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/api/SettingsPageRepository.kt
new file mode 100644
index 0000000..ce39f4f
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/api/SettingsPageRepository.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spa.api
+
+data class SettingsPageRepository(
+    val allPages: List<SettingsPageProvider>,
+    val startDestination: String,
+)
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/NavControllerWrapper.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/NavControllerWrapper.kt
new file mode 100644
index 0000000..35112ad
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/NavControllerWrapper.kt
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spa.framework
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.compositionLocalOf
+import androidx.compose.runtime.remember
+import androidx.navigation.NavHostController
+
+interface NavControllerWrapper {
+    fun navigate(route: String)
+    fun navigateUp()
+}
+
+@Composable
+fun NavHostController.localNavController() =
+    LocalNavController provides remember { NavControllerWrapperImpl(this) }
+
+val LocalNavController = compositionLocalOf<NavControllerWrapper> {
+    object : NavControllerWrapper {
+        override fun navigate(route: String) {}
+
+        override fun navigateUp() {}
+    }
+}
+
+@Composable
+fun navigator(route: String): () -> Unit {
+    val navController = LocalNavController.current
+    return { navController.navigate(route) }
+}
+
+internal class NavControllerWrapperImpl(
+    private val navController: NavHostController,
+) : NavControllerWrapper {
+    override fun navigate(route: String) {
+        navController.navigate(route)
+    }
+
+    override fun navigateUp() {
+        navController.navigateUp()
+    }
+}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/RuntimeUtils.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/RuntimeUtils.kt
new file mode 100644
index 0000000..22d85e0
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/RuntimeUtils.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spa.framework
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.State
+import androidx.compose.runtime.remember
+
+/**
+ * Remember the [State] initialized with the [this].
+ */
+@Composable
+fun <T> T.toState(): State<T> = remember { stateOf(this) }
+
+/**
+ * Return a new [State] initialized with the passed in [value].
+ */
+fun <T> stateOf(value: T) = object : State<T> {
+    override val value = value
+}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/SpaActivity.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/SpaActivity.kt
new file mode 100644
index 0000000..28a5899e
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/SpaActivity.kt
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spa.framework
+
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.navigation.compose.NavHost
+import androidx.navigation.compose.composable
+import androidx.navigation.compose.rememberNavController
+import com.android.settingslib.spa.api.SettingsPageProvider
+import com.android.settingslib.spa.api.SettingsPageRepository
+import com.android.settingslib.spa.theme.SettingsTheme
+
+open class SpaActivity(
+    private val settingsPageRepository: SettingsPageRepository,
+) : ComponentActivity() {
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        setContent {
+            MainContent()
+        }
+    }
+
+    @Composable
+    private fun MainContent() {
+        SettingsTheme {
+            val navController = rememberNavController()
+            CompositionLocalProvider(navController.localNavController()) {
+                NavHost(navController, settingsPageRepository.startDestination) {
+                    for (page in settingsPageRepository.allPages) {
+                        composable(
+                            route = page.route,
+                            arguments = page.arguments,
+                        ) { navBackStackEntry ->
+                            page.Page(navBackStackEntry.arguments)
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    private val SettingsPageProvider.route: String
+        get() = name + arguments.joinToString("") { argument -> "/{${argument.name}}" }
+}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/theme/MaterialColors.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/theme/MaterialColors.kt
new file mode 100644
index 0000000..8cd9184
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/theme/MaterialColors.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spa.theme
+
+import androidx.compose.material3.ColorScheme
+import androidx.compose.material3.dynamicDarkColorScheme
+import androidx.compose.material3.dynamicLightColorScheme
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.ui.platform.LocalContext
+
+@Composable
+internal fun materialColorScheme(isDarkTheme: Boolean): ColorScheme {
+    val context = LocalContext.current
+    return remember(isDarkTheme) {
+        when {
+            isDarkTheme -> dynamicDarkColorScheme(context)
+            else -> dynamicLightColorScheme(context)
+        }
+    }
+}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/theme/SettingsDimension.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/theme/SettingsDimension.kt
new file mode 100644
index 0000000..51e75c5
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/theme/SettingsDimension.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spa.theme
+
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.ui.unit.dp
+
+object SettingsDimension {
+    val itemIconContainerSize = 72.dp
+    val itemPaddingStart = 24.dp
+    val itemPaddingEnd = 16.dp
+    val itemPaddingVertical = 16.dp
+    val itemPadding = PaddingValues(
+        start = itemPaddingStart,
+        top = itemPaddingVertical,
+        end = itemPaddingEnd,
+        bottom = itemPaddingVertical,
+    )
+}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/theme/SettingsOpacity.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/theme/SettingsOpacity.kt
new file mode 100644
index 0000000..5f4da8a
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/theme/SettingsOpacity.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spa.theme
+
+object SettingsOpacity {
+    const val Full = 1f
+    const val Disabled = 0.38f
+}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/theme/SettingsTheme.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/theme/SettingsTheme.kt
new file mode 100644
index 0000000..fce9f2b
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/theme/SettingsTheme.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spa.theme
+
+import androidx.compose.foundation.isSystemInDarkTheme
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.runtime.Composable
+
+/**
+ * The Material 3 Theme for Settings.
+ */
+@Composable
+fun SettingsTheme(content: @Composable () -> Unit) {
+    val isDarkTheme = isSystemInDarkTheme()
+    val colorScheme = materialColorScheme(isDarkTheme)
+
+    MaterialTheme(colorScheme = colorScheme) {
+        content()
+    }
+}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/BaseLayout.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/BaseLayout.kt
new file mode 100644
index 0000000..d415e9b
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/BaseLayout.kt
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spa.widget.preference
+
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.width
+import androidx.compose.material3.Divider
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.State
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.alpha
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
+import com.android.settingslib.spa.framework.toState
+import com.android.settingslib.spa.theme.SettingsDimension
+import com.android.settingslib.spa.theme.SettingsOpacity
+import com.android.settingslib.spa.theme.SettingsTheme
+
+@Composable
+internal fun BaseLayout(
+    title: String,
+    subTitle: @Composable () -> Unit,
+    modifier: Modifier = Modifier,
+    icon: (@Composable () -> Unit)? = null,
+    enabled: State<Boolean> = true.toState(),
+    paddingStart: Dp = SettingsDimension.itemPaddingStart,
+    paddingEnd: Dp = SettingsDimension.itemPaddingEnd,
+    paddingVertical: Dp = SettingsDimension.itemPaddingVertical,
+    widget: @Composable () -> Unit = {},
+) {
+    Row(
+        modifier = modifier
+            .fillMaxWidth()
+            .padding(end = paddingEnd),
+        verticalAlignment = Alignment.CenterVertically,
+    ) {
+        val alphaModifier =
+            Modifier.alpha(if (enabled.value) SettingsOpacity.Full else SettingsOpacity.Disabled)
+        BaseIcon(icon, alphaModifier, paddingStart)
+        Titles(
+            title = title,
+            subTitle = subTitle,
+            modifier = alphaModifier
+                .weight(1f)
+                .padding(vertical = paddingVertical),
+        )
+        widget()
+    }
+}
+
+@Composable
+private fun BaseIcon(
+    icon: @Composable (() -> Unit)?,
+    modifier: Modifier,
+    paddingStart: Dp,
+) {
+    if (icon != null) {
+        Box(
+            modifier = modifier.size(SettingsDimension.itemIconContainerSize),
+            contentAlignment = Alignment.Center,
+        ) {
+            icon()
+        }
+    } else {
+        Spacer(modifier = Modifier.width(width = paddingStart))
+    }
+}
+
+// Extracts a scope to avoid frequent recompose outside scope.
+@Composable
+private fun Titles(title: String, subTitle: @Composable () -> Unit, modifier: Modifier) {
+    Column(modifier) {
+        Text(
+            text = title,
+            color = MaterialTheme.colorScheme.onSurface,
+            style = MaterialTheme.typography.titleMedium,
+        )
+        subTitle()
+    }
+}
+
+@Preview
+@Composable
+private fun BaseLayoutPreview() {
+    SettingsTheme {
+        BaseLayout(
+            title = "Title",
+            subTitle = {
+                Divider(thickness = 10.dp)
+            }
+        )
+    }
+}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/BasePreference.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/BasePreference.kt
new file mode 100644
index 0000000..563a47a
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/BasePreference.kt
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spa.widget.preference
+
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.outlined.BatteryChargingFull
+import androidx.compose.material3.Icon
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.State
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.Dp
+import com.android.settingslib.spa.framework.toState
+import com.android.settingslib.spa.theme.SettingsDimension
+import com.android.settingslib.spa.theme.SettingsTheme
+
+@Composable
+internal fun BasePreference(
+    title: String,
+    summary: State<String>,
+    modifier: Modifier = Modifier,
+    icon: (@Composable () -> Unit)? = null,
+    enabled: State<Boolean> = true.toState(),
+    paddingStart: Dp = SettingsDimension.itemPaddingStart,
+    paddingEnd: Dp = SettingsDimension.itemPaddingEnd,
+    paddingVertical: Dp = SettingsDimension.itemPaddingVertical,
+    widget: @Composable () -> Unit = {},
+) {
+    BaseLayout(
+        title = title,
+        subTitle = {
+            if (summary.value.isNotEmpty()) {
+                Text(
+                    text = summary.value,
+                    color = MaterialTheme.colorScheme.onSurfaceVariant,
+                    style = MaterialTheme.typography.bodyMedium,
+                )
+            }
+        },
+        modifier = modifier,
+        icon = icon,
+        enabled = enabled,
+        paddingStart = paddingStart,
+        paddingEnd = paddingEnd,
+        paddingVertical = paddingVertical,
+        widget = widget,
+    )
+}
+
+@Preview
+@Composable
+private fun BasePreferencePreview() {
+    SettingsTheme {
+        BasePreference(
+            title = "Screen Saver",
+            summary = "Clock".toState(),
+        )
+    }
+}
+
+@Preview
+@Composable
+private fun BasePreferenceIconPreview() {
+    SettingsTheme {
+        BasePreference(
+            title = "Screen Saver",
+            summary = "Clock".toState(),
+            icon = {
+                Icon(imageVector = Icons.Outlined.BatteryChargingFull, contentDescription = null)
+            },
+        )
+    }
+}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/Preference.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/Preference.kt
new file mode 100644
index 0000000..ee20280
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/Preference.kt
@@ -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.settingslib.spa.widget.preference
+
+import androidx.compose.foundation.clickable
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.State
+import androidx.compose.ui.Modifier
+import com.android.settingslib.spa.framework.stateOf
+
+/**
+ * The widget model for [Preference] widget.
+ */
+interface PreferenceModel {
+    /**
+     * The title of this [Preference].
+     */
+    val title: String
+
+    /**
+     * The summary of this [Preference].
+     */
+    val summary: State<String>
+        get() = stateOf("")
+
+    /**
+     * Indicates whether this [Preference] is enabled.
+     *
+     * Disabled [Preference] will be displayed in disabled style.
+     */
+    val enabled: State<Boolean>
+        get() = stateOf(true)
+
+    /**
+     * The on click handler of this [Preference].
+     *
+     * This also indicates whether this [Preference] is clickable.
+     */
+    val onClick: (() -> Unit)?
+        get() = null
+}
+
+/**
+ * Preference widget.
+ *
+ * Data is provided through [PreferenceModel].
+ */
+@Composable
+fun Preference(model: PreferenceModel) {
+    val modifier = model.onClick?.let { onClick ->
+        Modifier.clickable(enabled = model.enabled.value) { onClick() }
+    } ?: Modifier
+    BasePreference(
+        title = model.title,
+        summary = model.summary,
+        modifier = modifier,
+        enabled = model.enabled,
+    )
+}
diff --git a/packages/SettingsLib/Spa/tests/Android.bp b/packages/SettingsLib/Spa/tests/Android.bp
new file mode 100644
index 0000000..037d8c4
--- /dev/null
+++ b/packages/SettingsLib/Spa/tests/Android.bp
@@ -0,0 +1,36 @@
+//
+// 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 {
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test {
+    name: "SpaLibTests",
+    test_suites: ["device-tests"],
+
+    srcs: ["src/**/*.kt"],
+
+    static_libs: [
+        "SpaLib",
+        "androidx.test.runner",
+        "androidx.test.ext.junit",
+        "androidx.compose.runtime_runtime",
+        "androidx.compose.ui_ui-test-junit4",
+        "androidx.compose.ui_ui-test-manifest",
+    ],
+    kotlincflags: ["-Xjvm-default=all"],
+}
diff --git a/packages/SettingsLib/Spa/tests/AndroidManifest.xml b/packages/SettingsLib/Spa/tests/AndroidManifest.xml
new file mode 100644
index 0000000..c224caf
--- /dev/null
+++ b/packages/SettingsLib/Spa/tests/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?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.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.settingslib.spa.tests">
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation
+        android:name="androidx.test.runner.AndroidJUnitRunner"
+        android:label="Tests for SpaLib"
+        android:targetPackage="com.android.settingslib.spa.tests">
+    </instrumentation>
+</manifest>
diff --git a/packages/SettingsLib/Spa/tests/build.gradle b/packages/SettingsLib/Spa/tests/build.gradle
new file mode 100644
index 0000000..707017e
--- /dev/null
+++ b/packages/SettingsLib/Spa/tests/build.gradle
@@ -0,0 +1,67 @@
+/*
+ * 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.
+ */
+
+plugins {
+    id 'com.android.library'
+    id 'kotlin-android'
+}
+
+android {
+    namespace 'com.android.settingslib.spa.tests'
+    compileSdk 33
+
+    defaultConfig {
+        minSdk minSdk_version
+        targetSdk 33
+
+        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+    }
+
+    sourceSets {
+        androidTest {
+            kotlin {
+                srcDir "src"
+            }
+            manifest.srcFile "AndroidManifest.xml"
+        }
+    }
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_1_8
+        targetCompatibility JavaVersion.VERSION_1_8
+    }
+    kotlinOptions {
+        jvmTarget = '1.8'
+        freeCompilerArgs = ["-Xjvm-default=all"]
+    }
+    buildFeatures {
+        compose true
+    }
+    composeOptions {
+        kotlinCompilerExtensionVersion compose_version
+    }
+    packagingOptions {
+        resources {
+            excludes += '/META-INF/{AL2.0,LGPL2.1}'
+        }
+    }
+}
+
+dependencies {
+    androidTestImplementation(project(":spa"))
+    androidTestImplementation 'androidx.test.ext:junit-ktx:1.1.3'
+    androidTestImplementation("androidx.compose.ui:ui-test-junit4:$compose_version")
+    androidTestDebugImplementation "androidx.compose.ui:ui-test-manifest:$compose_version"
+}
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/PreferenceTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/PreferenceTest.kt
new file mode 100644
index 0000000..4097946
--- /dev/null
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/PreferenceTest.kt
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spa.widget.preference
+
+import androidx.compose.runtime.derivedStateOf
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithText
+import androidx.compose.ui.test.performClick
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settingslib.spa.framework.toState
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class PreferenceTest {
+    @get:Rule
+    val composeTestRule = createComposeRule()
+
+    @Test
+    fun title_displayed() {
+        composeTestRule.setContent {
+            Preference(object : PreferenceModel {
+                override val title = "Preference"
+            })
+        }
+
+        composeTestRule.onNodeWithText("Preference").assertIsDisplayed()
+    }
+
+    @Test
+    fun click_enabled_withEffect() {
+        composeTestRule.setContent {
+            var count by remember { mutableStateOf(0) }
+            Preference(object : PreferenceModel {
+                override val title = "Preference"
+                override val summary = derivedStateOf { count.toString() }
+                override val onClick: (() -> Unit) = { count++ }
+            })
+        }
+
+        composeTestRule.onNodeWithText("Preference").performClick()
+        composeTestRule.onNodeWithText("1").assertIsDisplayed()
+    }
+
+    @Test
+    fun click_disabled_noEffect() {
+        composeTestRule.setContent {
+            var count by remember { mutableStateOf(0) }
+            Preference(object : PreferenceModel {
+                override val title = "Preference"
+                override val summary = derivedStateOf { count.toString() }
+                override val enabled = false.toState()
+                override val onClick: (() -> Unit) = { count++ }
+            })
+        }
+
+        composeTestRule.onNodeWithText("Preference").performClick()
+        composeTestRule.onNodeWithText("0").assertIsDisplayed()
+    }
+}
diff --git a/packages/SettingsLib/TopIntroPreference/res/layout-v33/top_intro_preference.xml b/packages/SettingsLib/TopIntroPreference/res/layout-v33/top_intro_preference.xml
new file mode 100644
index 0000000..6046d91
--- /dev/null
+++ b/packages/SettingsLib/TopIntroPreference/res/layout-v33/top_intro_preference.xml
@@ -0,0 +1,39 @@
+<?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.
+  -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+    android:paddingBottom="16dp"
+    android:paddingTop="8dp"
+    android:background="?android:attr/selectableItemBackground"
+    android:clipToPadding="false">
+
+    <TextView
+        android:id="@android:id/title"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:clickable="false"
+        android:longClickable="false"
+        android:maxLines="10"
+        android:hyphenationFrequency="normalFast"
+        android:lineBreakWordStyle="phrase"
+        android:textAppearance="@style/TextAppearance.TopIntroText"/>
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SettingsLib/TopIntroPreference/res/layout/top_intro_preference.xml b/packages/SettingsLib/TopIntroPreference/res/layout/top_intro_preference.xml
index b2a9037..4d6e1b7 100644
--- a/packages/SettingsLib/TopIntroPreference/res/layout/top_intro_preference.xml
+++ b/packages/SettingsLib/TopIntroPreference/res/layout/top_intro_preference.xml
@@ -33,6 +33,5 @@
         android:clickable="false"
         android:longClickable="false"
         android:maxLines="10"
-        android:hyphenationFrequency="normalFast"
         android:textAppearance="@style/TextAppearance.TopIntroText"/>
 </LinearLayout>
\ No newline at end of file
diff --git a/packages/SettingsLib/TwoTargetPreference/res/layout-v31/preference_two_target.xml b/packages/SettingsLib/TwoTargetPreference/res/layout-v31/preference_two_target.xml
index ac5807d..2c35772 100644
--- a/packages/SettingsLib/TwoTargetPreference/res/layout-v31/preference_two_target.xml
+++ b/packages/SettingsLib/TwoTargetPreference/res/layout-v31/preference_two_target.xml
@@ -41,7 +41,6 @@
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:maxLines="2"
-            android:hyphenationFrequency="normalFast"
             android:textAppearance="?android:attr/textAppearanceListItem"
             android:ellipsize="marquee"/>
 
@@ -53,7 +52,6 @@
             android:layout_alignStart="@android:id/title"
             android:textAppearance="?android:attr/textAppearanceListItemSecondary"
             android:textColor="?android:attr/textColorSecondary"
-            android:hyphenationFrequency="normalFast"
             android:maxLines="10"/>
 
     </RelativeLayout>
diff --git a/packages/SettingsLib/TwoTargetPreference/res/layout-v33/preference_two_target.xml b/packages/SettingsLib/TwoTargetPreference/res/layout-v33/preference_two_target.xml
new file mode 100644
index 0000000..0bca9ab
--- /dev/null
+++ b/packages/SettingsLib/TwoTargetPreference/res/layout-v33/preference_two_target.xml
@@ -0,0 +1,74 @@
+<?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.
+  -->
+
+<!-- Based off preference_material_settings.xml except that ripple on only on the left side. -->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:minHeight="?android:attr/listPreferredItemHeightSmall"
+    android:gravity="center_vertical"
+    android:background="?android:attr/selectableItemBackground"
+    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+    android:clipToPadding="false">
+
+    <include layout="@layout/settingslib_icon_frame"/>
+
+    <RelativeLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:paddingTop="16dp"
+        android:paddingBottom="16dp">
+
+        <TextView
+            android:id="@android:id/title"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:maxLines="2"
+            android:hyphenationFrequency="normalFast"
+            android:lineBreakWordStyle="phrase"
+            android:textAppearance="?android:attr/textAppearanceListItem"
+            android:ellipsize="marquee"/>
+
+        <TextView
+            android:id="@android:id/summary"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_below="@android:id/title"
+            android:layout_alignStart="@android:id/title"
+            android:textAppearance="?android:attr/textAppearanceListItemSecondary"
+            android:textColor="?android:attr/textColorSecondary"
+            android:hyphenationFrequency="normalFast"
+            android:lineBreakWordStyle="phrase"
+            android:maxLines="10"/>
+
+    </RelativeLayout>
+
+    <include layout="@layout/preference_two_target_divider" />
+
+    <!-- Preference should place its actual preference widget here. -->
+    <LinearLayout
+        android:id="@android:id/widget_frame"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:minWidth="@dimen/two_target_min_width"
+        android:gravity="center"
+        android:orientation="vertical" />
+
+</LinearLayout>
diff --git a/packages/SettingsLib/res/layout-v33/preference_access_point.xml b/packages/SettingsLib/res/layout-v33/preference_access_point.xml
new file mode 100644
index 0000000..81bfeffd
--- /dev/null
+++ b/packages/SettingsLib/res/layout-v33/preference_access_point.xml
@@ -0,0 +1,110 @@
+<?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.
+  -->
+
+<!-- Based off preference_two_target.xml with Material ripple moved to parent for full ripple. -->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:settings="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:minHeight="?android:attr/listPreferredItemHeightSmall"
+    android:gravity="center_vertical"
+    android:background="?android:attr/selectableItemBackground"
+    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+    android:clipToPadding="false">
+
+    <LinearLayout
+        android:layout_width="0dp"
+        android:layout_height="match_parent"
+        android:layout_weight="1"
+        android:gravity="start|center_vertical"
+        android:clipToPadding="false">
+
+        <LinearLayout
+            android:id="@+id/icon_frame"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:gravity="start|center_vertical"
+            android:minWidth="48dp"
+            android:orientation="horizontal"
+            android:clipToPadding="false"
+            android:paddingTop="4dp"
+            android:paddingBottom="4dp">
+            <androidx.preference.internal.PreferenceImageView
+                android:id="@android:id/icon"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                settings:maxWidth="48dp"
+                settings:maxHeight="48dp" />
+        </LinearLayout>
+
+        <RelativeLayout
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:paddingTop="16dp"
+            android:paddingBottom="16dp">
+
+            <TextView
+                android:id="@android:id/title"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:maxLines="2"
+                android:hyphenationFrequency="normalFast"
+                android:lineBreakWordStyle="phrase"
+                android:textAppearance="?android:attr/textAppearanceListItem"
+                android:ellipsize="marquee" />
+
+            <TextView
+                android:id="@android:id/summary"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_below="@android:id/title"
+                android:layout_alignStart="@android:id/title"
+                android:textAppearance="?android:attr/textAppearanceListItemSecondary"
+                android:textColor="?android:attr/textColorSecondary"
+                android:hyphenationFrequency="normalFast"
+                android:lineBreakWordStyle="phrase"
+                android:maxLines="10" />
+
+        </RelativeLayout>
+
+    </LinearLayout>
+
+    <include layout="@layout/preference_two_target_divider" />
+
+    <!-- Preference should place its actual preference widget here. -->
+    <LinearLayout
+        android:id="@android:id/widget_frame"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:minWidth="@dimen/two_target_min_width"
+        android:gravity="center"
+        android:orientation="vertical" />
+
+    <ImageButton
+        android:id="@+id/icon_button"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:minWidth="@dimen/two_target_min_width"
+        android:minHeight="@dimen/min_tap_target_size"
+        android:layout_gravity="center"
+        android:background="?android:attr/selectableItemBackground"
+        android:visibility="gone">
+    </ImageButton>
+</LinearLayout>
diff --git a/packages/SettingsLib/res/layout-v33/preference_checkable_two_target.xml b/packages/SettingsLib/res/layout-v33/preference_checkable_two_target.xml
new file mode 100644
index 0000000..7ad018c
--- /dev/null
+++ b/packages/SettingsLib/res/layout-v33/preference_checkable_two_target.xml
@@ -0,0 +1,97 @@
+<?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.
+  -->
+
+<!-- Based off preference_material_settings.xml except that ripple on only on the left side. -->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:settings="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:minHeight="?android:attr/listPreferredItemHeightSmall"
+    android:gravity="center_vertical"
+    android:background="@android:color/transparent"
+    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+    android:clipToPadding="false">
+
+    <LinearLayout
+        android:layout_width="0dp"
+        android:layout_height="match_parent"
+        android:layout_weight="1"
+        android:background="?android:attr/selectableItemBackground"
+        android:gravity="start|center_vertical"
+        android:clipToPadding="false">
+
+        <LinearLayout
+            android:id="@+id/checkbox_container"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:gravity="start|center_vertical"
+            android:minWidth="48dp"
+            android:minHeight="48dp"
+            android:orientation="horizontal"
+            android:clipToPadding="false"
+            android:paddingTop="4dp"
+            android:paddingBottom="4dp">
+            <include layout="@layout/preference_widget_checkbox" />
+        </LinearLayout>
+
+        <RelativeLayout
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:paddingTop="16dp"
+            android:paddingBottom="16dp">
+
+            <TextView
+                android:id="@android:id/title"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:maxLines="2"
+                android:hyphenationFrequency="normalFast"
+                android:lineBreakWordStyle="phrase"
+                android:textAppearance="?android:attr/textAppearanceListItem"
+                android:ellipsize="marquee" />
+
+            <TextView
+                android:id="@android:id/summary"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_below="@android:id/title"
+                android:layout_alignStart="@android:id/title"
+                android:textAppearance="?android:attr/textAppearanceListItemSecondary"
+                android:textColor="?android:attr/textColorSecondary"
+                android:hyphenationFrequency="normalFast"
+                android:lineBreakWordStyle="phrase"
+                android:maxLines="10" />
+
+        </RelativeLayout>
+
+    </LinearLayout>
+
+    <include layout="@layout/preference_two_target_divider" />
+
+    <!-- Preference should place its actual preference widget here. -->
+    <LinearLayout
+        android:id="@android:id/widget_frame"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:minWidth="@dimen/two_target_min_width"
+        android:gravity="center"
+        android:orientation="vertical" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SettingsLib/res/layout-v33/restricted_switch_preference.xml b/packages/SettingsLib/res/layout-v33/restricted_switch_preference.xml
new file mode 100644
index 0000000..31e9696
--- /dev/null
+++ b/packages/SettingsLib/res/layout-v33/restricted_switch_preference.xml
@@ -0,0 +1,97 @@
+<?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.
+-->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:minHeight="?android:attr/listPreferredItemHeightSmall"
+    android:gravity="center_vertical"
+    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+    android:background="?android:attr/selectableItemBackground"
+    android:clipToPadding="false">
+
+    <LinearLayout
+        android:id="@+id/icon_frame"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:minWidth="56dp"
+        android:gravity="start|center_vertical"
+        android:orientation="horizontal"
+        android:paddingEnd="12dp"
+        android:paddingTop="4dp"
+        android:paddingBottom="4dp">
+        <com.android.internal.widget.PreferenceImageView
+            android:id="@android:id/icon"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:maxWidth="48dp"
+            android:maxHeight="48dp" />
+    </LinearLayout>
+
+    <RelativeLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:paddingTop="16dp"
+        android:paddingBottom="16dp">
+
+        <TextView
+            android:id="@android:id/title"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:maxLines="2"
+            android:hyphenationFrequency="normalFast"
+            android:lineBreakWordStyle="phrase"
+            android:textAppearance="?android:attr/textAppearanceListItem"
+            android:ellipsize="marquee" />
+
+        <TextView
+            android:id="@android:id/summary"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_below="@android:id/title"
+            android:layout_alignStart="@android:id/title"
+            android:textAppearance="?android:attr/textAppearanceListItemSecondary"
+            android:textColor="?android:attr/textColorSecondary"
+            android:hyphenationFrequency="normalFast"
+            android:lineBreakWordStyle="phrase"
+            android:maxLines="10" />
+
+        <TextView
+            android:id="@+id/additional_summary"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_below="@android:id/summary"
+            android:layout_alignStart="@android:id/summary"
+            android:textAppearance="?android:attr/textAppearanceListItemSecondary"
+            android:textColor="?android:attr/textColorSecondary"
+            android:maxLines="10"
+            android:hyphenationFrequency="normalFast"
+            android:lineBreakWordStyle="phrase"
+            android:visibility="gone" />
+    </RelativeLayout>
+
+    <!-- Preference should place its actual preference widget here. -->
+    <LinearLayout
+        android:id="@android:id/widget_frame"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:gravity="end|center_vertical"
+        android:paddingStart="16dp"
+        android:orientation="vertical" />
+
+</LinearLayout>
diff --git a/packages/SettingsLib/res/layout/preference_access_point.xml b/packages/SettingsLib/res/layout/preference_access_point.xml
index 4ad9d80..802d604 100644
--- a/packages/SettingsLib/res/layout/preference_access_point.xml
+++ b/packages/SettingsLib/res/layout/preference_access_point.xml
@@ -65,7 +65,6 @@
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
                 android:maxLines="2"
-                android:hyphenationFrequency="normalFast"
                 android:textAppearance="?android:attr/textAppearanceListItem"
                 android:ellipsize="marquee" />
 
@@ -77,7 +76,6 @@
                 android:layout_alignStart="@android:id/title"
                 android:textAppearance="?android:attr/textAppearanceListItemSecondary"
                 android:textColor="?android:attr/textColorSecondary"
-                android:hyphenationFrequency="normalFast"
                 android:maxLines="10" />
 
         </RelativeLayout>
diff --git a/packages/SettingsLib/res/layout/preference_checkable_two_target.xml b/packages/SettingsLib/res/layout/preference_checkable_two_target.xml
index cbe49cd..f512f9b 100644
--- a/packages/SettingsLib/res/layout/preference_checkable_two_target.xml
+++ b/packages/SettingsLib/res/layout/preference_checkable_two_target.xml
@@ -62,7 +62,6 @@
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
                 android:maxLines="2"
-                android:hyphenationFrequency="normalFast"
                 android:textAppearance="?android:attr/textAppearanceListItem"
                 android:ellipsize="marquee" />
 
@@ -74,7 +73,6 @@
                 android:layout_alignStart="@android:id/title"
                 android:textAppearance="?android:attr/textAppearanceListItemSecondary"
                 android:textColor="?android:attr/textColorSecondary"
-                android:hyphenationFrequency="normalFast"
                 android:maxLines="10" />
 
         </RelativeLayout>
diff --git a/packages/SettingsLib/res/layout/restricted_switch_preference.xml b/packages/SettingsLib/res/layout/restricted_switch_preference.xml
index edea144..169ae97 100644
--- a/packages/SettingsLib/res/layout/restricted_switch_preference.xml
+++ b/packages/SettingsLib/res/layout/restricted_switch_preference.xml
@@ -52,7 +52,6 @@
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:maxLines="2"
-            android:hyphenationFrequency="normalFast"
             android:textAppearance="?android:attr/textAppearanceListItem"
             android:ellipsize="marquee" />
 
@@ -63,7 +62,6 @@
             android:layout_alignStart="@android:id/title"
             android:textAppearance="?android:attr/textAppearanceListItemSecondary"
             android:textColor="?android:attr/textColorSecondary"
-                  android:hyphenationFrequency="normalFast"
             android:maxLines="10" />
 
         <TextView android:id="@+id/additional_summary"
@@ -74,7 +72,6 @@
             android:textAppearance="?android:attr/textAppearanceListItemSecondary"
             android:textColor="?android:attr/textColorSecondary"
             android:maxLines="10"
-                  android:hyphenationFrequency="normalFast"
             android:visibility="gone" />
     </RelativeLayout>
 
diff --git a/packages/SettingsLib/res/values-af/arrays.xml b/packages/SettingsLib/res/values-af/arrays.xml
index 7165c14..1de7668 100644
--- a/packages/SettingsLib/res/values-af/arrays.xml
+++ b/packages/SettingsLib/res/values-af/arrays.xml
@@ -85,22 +85,10 @@
     <item msgid="7073042887003102964">"map13"</item>
     <item msgid="8147982633566548515">"map14"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="2494959071796102843">"Gebruik stelselkeuse (verstek)"</item>
-    <item msgid="4055460186095649420">"SBC"</item>
-    <item msgid="720249083677397051">"AAC"</item>
-    <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>-oudio"</item>
-    <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>-oudio"</item>
-    <item msgid="3825367753087348007">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="8868109554557331312">"Gebruik stelselkeuse (verstek)"</item>
-    <item msgid="9024885861221697796">"SBC"</item>
-    <item msgid="4688890470703790013">"AAC"</item>
-    <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>-oudio"</item>
-    <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>-oudio"</item>
-    <item msgid="2553206901068987657">"LDAC"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
     <item msgid="926809261293414607">"Gebruik stelselkeuse (verstek)"</item>
     <item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml
index 264faad..fe8030f 100644
--- a/packages/SettingsLib/res/values-af/strings.xml
+++ b/packages/SettingsLib/res/values-af/strings.xml
@@ -117,10 +117,8 @@
     <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Lêeroordrag"</string>
     <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Invoertoestel"</string>
     <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Internettoegang"</string>
-    <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
-    <skip />
-    <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
-    <skip />
+    <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Deling van kontakte en oproepgeskiedenis"</string>
+    <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Gebruik vir deling van kontakte en oproepgeskiedenis"</string>
     <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Deling van internetverbinding"</string>
     <string name="bluetooth_profile_map" msgid="8907204701162107271">"Teksboodskappe"</string>
     <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM-toegang"</string>
@@ -662,6 +660,7 @@
     <string name="dream_complication_title_weather" msgid="598609151677172783">"Weer"</string>
     <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Luggehalte"</string>
     <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Uitsaai-inligting"</string>
+    <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Huiskontroles"</string>
     <string name="avatar_picker_title" msgid="8492884172713170652">"Kies \'n profielprent"</string>
     <string name="default_user_icon_description" msgid="6554047177298972638">"Verstekgebruikerikoon"</string>
     <string name="physical_keyboard_title" msgid="4811935435315835220">"Fisieke sleutelbord"</string>
diff --git a/packages/SettingsLib/res/values-am/arrays.xml b/packages/SettingsLib/res/values-am/arrays.xml
index 1108c82..a900d13 100644
--- a/packages/SettingsLib/res/values-am/arrays.xml
+++ b/packages/SettingsLib/res/values-am/arrays.xml
@@ -85,22 +85,10 @@
     <item msgid="7073042887003102964">"map13"</item>
     <item msgid="8147982633566548515">"map14"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="2494959071796102843">"የስርዓቱን ምርጫ (ነባሪ) ተጠቀም"</item>
-    <item msgid="4055460186095649420">"SBC"</item>
-    <item msgid="720249083677397051">"AAC"</item>
-    <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> ኦዲዮ"</item>
-    <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> ኦዲዮ"</item>
-    <item msgid="3825367753087348007">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="8868109554557331312">"የስርዓቱን ምርጫ (ነባሪ) ተጠቀም"</item>
-    <item msgid="9024885861221697796">"SBC"</item>
-    <item msgid="4688890470703790013">"AAC"</item>
-    <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> ኦዲዮ"</item>
-    <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> ኦዲዮ"</item>
-    <item msgid="2553206901068987657">"LDAC"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
     <item msgid="926809261293414607">"የስርዓቱን ምርጫ (ነባሪ) ተጠቀም"</item>
     <item msgid="8003118270854840095">"44.1 ኪኸ"</item>
diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml
index b68cbd7..d3c034f 100644
--- a/packages/SettingsLib/res/values-am/strings.xml
+++ b/packages/SettingsLib/res/values-am/strings.xml
@@ -117,10 +117,8 @@
     <string name="bluetooth_profile_opp" msgid="6692618568149493430">"ፋይል ማስተላለፍ"</string>
     <string name="bluetooth_profile_hid" msgid="2969922922664315866">"ግቤት መሣሪያ"</string>
     <string name="bluetooth_profile_pan" msgid="1006235139308318188">"የበይነመረብ ድረስ"</string>
-    <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
-    <skip />
-    <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
-    <skip />
+    <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"የእውቂያዎች እና የጥሪ ታሪክ ማጋራት"</string>
+    <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"እውቂያዎችን እና የጥሪ ታሪክን ለማጋራት ይጠቀሙበት"</string>
     <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"የበይነ መረብ ተያያዥ ማጋሪያ"</string>
     <string name="bluetooth_profile_map" msgid="8907204701162107271">"የጽሑፍ መልዕክቶች"</string>
     <string name="bluetooth_profile_sap" msgid="8304170950447934386">"የሲም መዳረሻ"</string>
@@ -662,6 +660,8 @@
     <string name="dream_complication_title_weather" msgid="598609151677172783">"የአየር ሁኔታ"</string>
     <string name="dream_complication_title_aqi" msgid="4587552608957834110">"የአየር ጥራት"</string>
     <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"የCast መረጃ"</string>
+    <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+    <skip />
     <string name="avatar_picker_title" msgid="8492884172713170652">"የመገለጫ ሥዕል ይምረጡ"</string>
     <string name="default_user_icon_description" msgid="6554047177298972638">"ነባሪ የተጠቃሚ አዶ"</string>
     <string name="physical_keyboard_title" msgid="4811935435315835220">"አካላዊ ቁልፍ ሰሌዳ"</string>
diff --git a/packages/SettingsLib/res/values-ar/arrays.xml b/packages/SettingsLib/res/values-ar/arrays.xml
index eb4be38..8f7d7d2 100644
--- a/packages/SettingsLib/res/values-ar/arrays.xml
+++ b/packages/SettingsLib/res/values-ar/arrays.xml
@@ -85,22 +85,10 @@
     <item msgid="7073042887003102964">"map13"</item>
     <item msgid="8147982633566548515">"map14"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="2494959071796102843">"استخدام اختيار النظام (تلقائي)"</item>
-    <item msgid="4055460186095649420">"SBC"</item>
-    <item msgid="720249083677397051">"AAC"</item>
-    <item msgid="1049450003868150455">"صوت <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
-    <item msgid="2908219194098827570">"صوت <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
-    <item msgid="3825367753087348007">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="8868109554557331312">"استخدام اختيار النظام (تلقائي)"</item>
-    <item msgid="9024885861221697796">"SBC"</item>
-    <item msgid="4688890470703790013">"AAC"</item>
-    <item msgid="8627333814413492563">"صوت <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
-    <item msgid="3517061573669307965">"صوت <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
-    <item msgid="2553206901068987657">"LDAC"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
     <item msgid="926809261293414607">"استخدام اختيار النظام (تلقائي)"</item>
     <item msgid="8003118270854840095">"44.1 كيلو هرتز"</item>
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index c8b263b..6783654 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -117,10 +117,8 @@
     <string name="bluetooth_profile_opp" msgid="6692618568149493430">"نقل الملف"</string>
     <string name="bluetooth_profile_hid" msgid="2969922922664315866">"جهاز الإرسال"</string>
     <string name="bluetooth_profile_pan" msgid="1006235139308318188">"استخدام الإنترنت"</string>
-    <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
-    <skip />
-    <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
-    <skip />
+    <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"مشاركة جهات الاتصال وسجل المكالمات"</string>
+    <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"استخدام إعدادات بلوتوث لمشاركة جهات الاتصال وسجل المكالمات"</string>
     <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"مشاركة اتصال الإنترنت"</string>
     <string name="bluetooth_profile_map" msgid="8907204701162107271">"الرسائل النصية"</string>
     <string name="bluetooth_profile_sap" msgid="8304170950447934386">"‏الوصول إلى شريحة SIM"</string>
@@ -662,6 +660,8 @@
     <string name="dream_complication_title_weather" msgid="598609151677172783">"الطقس"</string>
     <string name="dream_complication_title_aqi" msgid="4587552608957834110">"جودة الهواء"</string>
     <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"معلومات البث"</string>
+    <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+    <skip />
     <string name="avatar_picker_title" msgid="8492884172713170652">"اختيار صورة الملف الشخصي"</string>
     <string name="default_user_icon_description" msgid="6554047177298972638">"رمز المستخدم التلقائي"</string>
     <string name="physical_keyboard_title" msgid="4811935435315835220">"لوحة مفاتيح خارجية"</string>
diff --git a/packages/SettingsLib/res/values-as/arrays.xml b/packages/SettingsLib/res/values-as/arrays.xml
index df23f67..4c879d0 100644
--- a/packages/SettingsLib/res/values-as/arrays.xml
+++ b/packages/SettingsLib/res/values-as/arrays.xml
@@ -85,22 +85,10 @@
     <item msgid="7073042887003102964">"map13"</item>
     <item msgid="8147982633566548515">"map14"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="2494959071796102843">"ছিষ্টেমৰ বাছনি ব্যৱহাৰ কৰক (ডিফ\'ল্ট)"</item>
-    <item msgid="4055460186095649420">"এছবিচি"</item>
-    <item msgid="720249083677397051">"এএচি"</item>
-    <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> অডিঅ\'"</item>
-    <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> অডিঅ’"</item>
-    <item msgid="3825367753087348007">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="8868109554557331312">"ছিষ্টেমৰ বাছনি ব্যৱহাৰ কৰক (ডিফ\'ল্ট)"</item>
-    <item msgid="9024885861221697796">"এছবিচি"</item>
-    <item msgid="4688890470703790013">"এএচি"</item>
-    <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> অডিঅ’"</item>
-    <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> অডিঅ’"</item>
-    <item msgid="2553206901068987657">"LDAC"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
     <item msgid="926809261293414607">"ছিষ্টেমৰ বাছনি ব্যৱহাৰ কৰক (ডিফ\'ল্ট)"</item>
     <item msgid="8003118270854840095">"৪৪.১ কিল\'হাৰ্টজ"</item>
diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml
index ea1aff0..543c703 100644
--- a/packages/SettingsLib/res/values-as/strings.xml
+++ b/packages/SettingsLib/res/values-as/strings.xml
@@ -117,10 +117,8 @@
     <string name="bluetooth_profile_opp" msgid="6692618568149493430">"ফাইল স্থানান্তৰণ"</string>
     <string name="bluetooth_profile_hid" msgid="2969922922664315866">"ইনপুট ডিভাইচ"</string>
     <string name="bluetooth_profile_pan" msgid="1006235139308318188">"ইণ্টাৰনেট সংযোগ"</string>
-    <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
-    <skip />
-    <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
-    <skip />
+    <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"সম্পৰ্কসূচী আৰু কলৰ ইতিহাস শ্বেয়াৰ কৰা"</string>
+    <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"সম্পৰ্কসূচী আৰু কলৰ ইতিহাস শ্বেয়াৰ কৰাৰ বাবে ব্যৱহাৰ কৰক"</string>
     <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"ইণ্টাৰনেট সংযোগ শ্বেয়াৰ"</string>
     <string name="bluetooth_profile_map" msgid="8907204701162107271">"পাঠ বাৰ্তা"</string>
     <string name="bluetooth_profile_sap" msgid="8304170950447934386">"ছিম প্ৰৱেশ"</string>
@@ -662,6 +660,8 @@
     <string name="dream_complication_title_weather" msgid="598609151677172783">"বতৰ"</string>
     <string name="dream_complication_title_aqi" msgid="4587552608957834110">"বায়ুৰ গুণগত মান"</string>
     <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"কাষ্টৰ তথ্য"</string>
+    <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+    <skip />
     <string name="avatar_picker_title" msgid="8492884172713170652">"এখন প্ৰ’ফাইল চিত্ৰ বাছনি কৰক"</string>
     <string name="default_user_icon_description" msgid="6554047177298972638">"ডিফ’ল্ট ব্যৱহাৰকাৰীৰ চিহ্ন"</string>
     <string name="physical_keyboard_title" msgid="4811935435315835220">"কায়িক কীব’ৰ্ড"</string>
diff --git a/packages/SettingsLib/res/values-az/arrays.xml b/packages/SettingsLib/res/values-az/arrays.xml
index 5163791..48974a7 100644
--- a/packages/SettingsLib/res/values-az/arrays.xml
+++ b/packages/SettingsLib/res/values-az/arrays.xml
@@ -85,22 +85,10 @@
     <item msgid="7073042887003102964">"map13"</item>
     <item msgid="8147982633566548515">"map14"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="2494959071796102843">"Sistem Seçimini istifadə edin (Defolt)"</item>
-    <item msgid="4055460186095649420">"SBC"</item>
-    <item msgid="720249083677397051">"AAC"</item>
-    <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> audio"</item>
-    <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> audio"</item>
-    <item msgid="3825367753087348007">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="8868109554557331312">"Sistem Seçimini istifadə edin (Defolt)"</item>
-    <item msgid="9024885861221697796">"SBC"</item>
-    <item msgid="4688890470703790013">"AAC"</item>
-    <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> audio"</item>
-    <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> audio"</item>
-    <item msgid="2553206901068987657">"LDAC"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
     <item msgid="926809261293414607">"Sistem Seçimini istifadə edin (Defolt)"</item>
     <item msgid="8003118270854840095">"44.1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml
index 88333e82..f3e5d95 100644
--- a/packages/SettingsLib/res/values-az/strings.xml
+++ b/packages/SettingsLib/res/values-az/strings.xml
@@ -117,10 +117,8 @@
     <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Fayl transferi"</string>
     <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Daxiletmə cihazı"</string>
     <string name="bluetooth_profile_pan" msgid="1006235139308318188">"İnternetə giriş"</string>
-    <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
-    <skip />
-    <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
-    <skip />
+    <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Kontaktlar və zəng tarixçəsi paylaşımı"</string>
+    <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Kontaktlar və zəng tarixçəsi paylaşımı üçün istifadə edin"</string>
     <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"internet bağlantı paylaşımı"</string>
     <string name="bluetooth_profile_map" msgid="8907204701162107271">"Mətn Mesajları"</string>
     <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM-karta giriş"</string>
@@ -662,6 +660,8 @@
     <string name="dream_complication_title_weather" msgid="598609151677172783">"Hava"</string>
     <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Havanın keyfiyyəti"</string>
     <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Yayım məlumatı"</string>
+    <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+    <skip />
     <string name="avatar_picker_title" msgid="8492884172713170652">"Profil şəkli seçin"</string>
     <string name="default_user_icon_description" msgid="6554047177298972638">"Defolt istifadəçi ikonası"</string>
     <string name="physical_keyboard_title" msgid="4811935435315835220">"Fiziki klaviatura"</string>
diff --git a/packages/SettingsLib/res/values-b+sr+Latn/arrays.xml b/packages/SettingsLib/res/values-b+sr+Latn/arrays.xml
index 5cc43f6..337da26 100644
--- a/packages/SettingsLib/res/values-b+sr+Latn/arrays.xml
+++ b/packages/SettingsLib/res/values-b+sr+Latn/arrays.xml
@@ -85,22 +85,10 @@
     <item msgid="7073042887003102964">"map13"</item>
     <item msgid="8147982633566548515">"map14"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="2494959071796102843">"Koristi izbor sistema (podrazumevano)"</item>
-    <item msgid="4055460186095649420">"SBC"</item>
-    <item msgid="720249083677397051">"AAC"</item>
-    <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> audio"</item>
-    <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> audio"</item>
-    <item msgid="3825367753087348007">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="8868109554557331312">"Koristi izbor sistema (podrazumevano)"</item>
-    <item msgid="9024885861221697796">"SBC"</item>
-    <item msgid="4688890470703790013">"AAC"</item>
-    <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> audio"</item>
-    <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> audio"</item>
-    <item msgid="2553206901068987657">"LDAC"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
     <item msgid="926809261293414607">"Koristi izbor sistema (podrazumevano)"</item>
     <item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
index a84d019..6a880aa 100644
--- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
+++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
@@ -117,10 +117,8 @@
     <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Prenos datoteke"</string>
     <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Ulazni uređaj"</string>
     <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Pristup Internetu"</string>
-    <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
-    <skip />
-    <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
-    <skip />
+    <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Deljenje kontakata i istorije poziva"</string>
+    <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Koristite za deljenje kontakata i istorije poziva"</string>
     <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Deljenje internet veze"</string>
     <string name="bluetooth_profile_map" msgid="8907204701162107271">"SMS-ovi"</string>
     <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Pristup SIM kartici"</string>
@@ -662,6 +660,8 @@
     <string name="dream_complication_title_weather" msgid="598609151677172783">"Vreme"</string>
     <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Kvalitet vazduha"</string>
     <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Podaci o prebacivanju"</string>
+    <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+    <skip />
     <string name="avatar_picker_title" msgid="8492884172713170652">"Odaberite sliku profila"</string>
     <string name="default_user_icon_description" msgid="6554047177298972638">"Podrazumevana ikona korisnika"</string>
     <string name="physical_keyboard_title" msgid="4811935435315835220">"Fizička tastatura"</string>
diff --git a/packages/SettingsLib/res/values-be/arrays.xml b/packages/SettingsLib/res/values-be/arrays.xml
index 6259c2d..d843629 100644
--- a/packages/SettingsLib/res/values-be/arrays.xml
+++ b/packages/SettingsLib/res/values-be/arrays.xml
@@ -85,22 +85,10 @@
     <item msgid="7073042887003102964">"map13"</item>
     <item msgid="8147982633566548515">"map14"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="2494959071796102843">"Выбар сістэмы (стандартны)"</item>
-    <item msgid="4055460186095649420">"SBC"</item>
-    <item msgid="720249083677397051">"AAC"</item>
-    <item msgid="1049450003868150455">"Аўдыя <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
-    <item msgid="2908219194098827570">"Аўдыя <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
-    <item msgid="3825367753087348007">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="8868109554557331312">"Выбар сістэмы (стандартны)"</item>
-    <item msgid="9024885861221697796">"SBC"</item>
-    <item msgid="4688890470703790013">"AAC"</item>
-    <item msgid="8627333814413492563">"Аўдыя <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
-    <item msgid="3517061573669307965">"Аўдыя <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
-    <item msgid="2553206901068987657">"LDAC"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
     <item msgid="926809261293414607">"Выбар сістэмы (стандартны)"</item>
     <item msgid="8003118270854840095">"44,1 кГц"</item>
diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml
index 37f8410..a902426 100644
--- a/packages/SettingsLib/res/values-be/strings.xml
+++ b/packages/SettingsLib/res/values-be/strings.xml
@@ -117,10 +117,8 @@
     <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Перадача файлаў"</string>
     <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Прылада ўводу"</string>
     <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Доступ у інтэрнэт"</string>
-    <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
-    <skip />
-    <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
-    <skip />
+    <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Абагульванне кантактаў і гісторыі выклікаў"</string>
+    <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Ужываць для абагульвання кантактаў і гісторыі выклікаў"</string>
     <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Прадастаўленне доступу да Інтэрнэту"</string>
     <string name="bluetooth_profile_map" msgid="8907204701162107271">"Тэкставыя паведамленні"</string>
     <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Доступ да SIM-карты"</string>
@@ -662,6 +660,8 @@
     <string name="dream_complication_title_weather" msgid="598609151677172783">"Надвор\'е"</string>
     <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Якасць паветра"</string>
     <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Даныя пра трансляцыю"</string>
+    <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+    <skip />
     <string name="avatar_picker_title" msgid="8492884172713170652">"Выберыце відарыс профілю"</string>
     <string name="default_user_icon_description" msgid="6554047177298972638">"Стандартны карыстальніцкі значок"</string>
     <string name="physical_keyboard_title" msgid="4811935435315835220">"Фізічная клавіятура"</string>
diff --git a/packages/SettingsLib/res/values-bg/arrays.xml b/packages/SettingsLib/res/values-bg/arrays.xml
index 49e66c0..1aad6ca7 100644
--- a/packages/SettingsLib/res/values-bg/arrays.xml
+++ b/packages/SettingsLib/res/values-bg/arrays.xml
@@ -85,22 +85,10 @@
     <item msgid="7073042887003102964">"map13"</item>
     <item msgid="8147982633566548515">"map14"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="2494959071796102843">"Използване на сист. избор (стандартно)"</item>
-    <item msgid="4055460186095649420">"SBC"</item>
-    <item msgid="720249083677397051">"Разширено аудиокодиране (AAC)"</item>
-    <item msgid="1049450003868150455">"Аудио: <xliff:g id="APTX">aptX™</xliff:g> от <xliff:g id="QUALCOMM">Qualcomm®</xliff:g>"</item>
-    <item msgid="2908219194098827570">"Аудио: <xliff:g id="APTX_HD">aptX™ HD</xliff:g> от <xliff:g id="QUALCOMM">Qualcomm®</xliff:g>"</item>
-    <item msgid="3825367753087348007">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="8868109554557331312">"Използване на сист. избор (стандартно)"</item>
-    <item msgid="9024885861221697796">"SBC"</item>
-    <item msgid="4688890470703790013">"Разширено аудиокодиране (AAC)"</item>
-    <item msgid="8627333814413492563">"Аудио: <xliff:g id="APTX">aptX™</xliff:g> от <xliff:g id="QUALCOMM">Qualcomm®</xliff:g>"</item>
-    <item msgid="3517061573669307965">"Аудио: <xliff:g id="APTX_HD">aptX™ HD</xliff:g> от <xliff:g id="QUALCOMM">Qualcomm®</xliff:g>"</item>
-    <item msgid="2553206901068987657">"LDAC"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
     <item msgid="926809261293414607">"Използване на сист. избор (стандартно)"</item>
     <item msgid="8003118270854840095">"44,1 кХц"</item>
diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml
index f2cf69e..c512366 100644
--- a/packages/SettingsLib/res/values-bg/strings.xml
+++ b/packages/SettingsLib/res/values-bg/strings.xml
@@ -117,10 +117,8 @@
     <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Прехвърляне на файл"</string>
     <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Входно устройство"</string>
     <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Достъп до интернет"</string>
-    <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
-    <skip />
-    <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
-    <skip />
+    <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Споделяне на контактите и ист. на обажд."</string>
+    <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Служи за споделяне на контактите и историята на обажданията"</string>
     <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Споделяне на връзката с интернет"</string>
     <string name="bluetooth_profile_map" msgid="8907204701162107271">"Текстови съобщения"</string>
     <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Достъп до SIM картата"</string>
@@ -662,6 +660,8 @@
     <string name="dream_complication_title_weather" msgid="598609151677172783">"Времето"</string>
     <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Качество на въздуха"</string>
     <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Предаване: Инф."</string>
+    <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+    <skip />
     <string name="avatar_picker_title" msgid="8492884172713170652">"Изберете снимка на потребителския профил"</string>
     <string name="default_user_icon_description" msgid="6554047177298972638">"Икона за основния потребител"</string>
     <string name="physical_keyboard_title" msgid="4811935435315835220">"Физическа клавиатура"</string>
diff --git a/packages/SettingsLib/res/values-bn/arrays.xml b/packages/SettingsLib/res/values-bn/arrays.xml
index fdb611b..5e6bb95 100644
--- a/packages/SettingsLib/res/values-bn/arrays.xml
+++ b/packages/SettingsLib/res/values-bn/arrays.xml
@@ -85,22 +85,10 @@
     <item msgid="7073042887003102964">"map13"</item>
     <item msgid="8147982633566548515">"map14"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="2494959071796102843">"সিস্টেমের নির্বাচন ব্যবহার করুন (ডিফল্ট)"</item>
-    <item msgid="4055460186095649420">"SBC"</item>
-    <item msgid="720249083677397051">"AAC"</item>
-    <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> অডিও"</item>
-    <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> অডিও"</item>
-    <item msgid="3825367753087348007">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="8868109554557331312">"সিস্টেমের নির্বাচন ব্যবহার করুন (ডিফল্ট)"</item>
-    <item msgid="9024885861221697796">"SBC"</item>
-    <item msgid="4688890470703790013">"AAC"</item>
-    <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> অডিও"</item>
-    <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> অডিও"</item>
-    <item msgid="2553206901068987657">"LDAC"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
     <item msgid="926809261293414607">"সিস্টেমের নির্বাচন ব্যবহার করুন (ডিফল্ট)"</item>
     <item msgid="8003118270854840095">"৪৪.১ kHz"</item>
diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml
index 801fb34..23eed04 100644
--- a/packages/SettingsLib/res/values-bn/strings.xml
+++ b/packages/SettingsLib/res/values-bn/strings.xml
@@ -117,10 +117,8 @@
     <string name="bluetooth_profile_opp" msgid="6692618568149493430">"ফাইল স্থানান্তর"</string>
     <string name="bluetooth_profile_hid" msgid="2969922922664315866">"ইনপুট ডিভাইস"</string>
     <string name="bluetooth_profile_pan" msgid="1006235139308318188">"ইন্টারনেট অ্যাক্সেস"</string>
-    <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
-    <skip />
-    <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
-    <skip />
+    <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"পরিচিতি এবং কলের ইতিহাস শেয়ার করা"</string>
+    <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"পরিচিতি ও কলের ইতিহাস শেয়ার করার জন্য ব্যবহার করুন"</string>
     <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"ইন্টারনেট কানেকশন শেয়ার করা হচ্ছে"</string>
     <string name="bluetooth_profile_map" msgid="8907204701162107271">"এসএমএস"</string>
     <string name="bluetooth_profile_sap" msgid="8304170950447934386">"সিম অ্যাক্সেস"</string>
@@ -662,6 +660,7 @@
     <string name="dream_complication_title_weather" msgid="598609151677172783">"আবহাওয়া"</string>
     <string name="dream_complication_title_aqi" msgid="4587552608957834110">"এয়ার কোয়ালিটি"</string>
     <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"কাস্ট সম্পর্কিত তথ্য"</string>
+    <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"হোম কন্ট্রোল"</string>
     <string name="avatar_picker_title" msgid="8492884172713170652">"একটি প্রোফাইল ছবি বেছে নিন"</string>
     <string name="default_user_icon_description" msgid="6554047177298972638">"ডিফল্ট ব্যবহারকারীর আইকন"</string>
     <string name="physical_keyboard_title" msgid="4811935435315835220">"ফিজিক্যাল কীবোর্ড"</string>
diff --git a/packages/SettingsLib/res/values-bs/arrays.xml b/packages/SettingsLib/res/values-bs/arrays.xml
index 32edef1..262a35f 100644
--- a/packages/SettingsLib/res/values-bs/arrays.xml
+++ b/packages/SettingsLib/res/values-bs/arrays.xml
@@ -85,22 +85,10 @@
     <item msgid="7073042887003102964">"map13"</item>
     <item msgid="8147982633566548515">"map14"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="2494959071796102843">"Korištenje odabira sistema (zadano)"</item>
-    <item msgid="4055460186095649420">"SBC"</item>
-    <item msgid="720249083677397051">"AAC"</item>
-    <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> audio"</item>
-    <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> audio"</item>
-    <item msgid="3825367753087348007">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="8868109554557331312">"Korištenje odabira sistema (zadano)"</item>
-    <item msgid="9024885861221697796">"SBC"</item>
-    <item msgid="4688890470703790013">"AAC"</item>
-    <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> audio"</item>
-    <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> audio"</item>
-    <item msgid="2553206901068987657">"LDAC"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
     <item msgid="926809261293414607">"Korištenje odabira sistema (zadano)"</item>
     <item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml
index 61082f6..78a484f 100644
--- a/packages/SettingsLib/res/values-bs/strings.xml
+++ b/packages/SettingsLib/res/values-bs/strings.xml
@@ -117,10 +117,8 @@
     <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Prenošenje fajla"</string>
     <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Ulazni uređaj"</string>
     <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Pristup internetu"</string>
-    <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
-    <skip />
-    <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
-    <skip />
+    <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Dijeljenje kontakata i historije poziva"</string>
+    <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Upotrijebite za dijeljenje kontakata i historije poziva"</string>
     <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Dijeljenje internet veze"</string>
     <string name="bluetooth_profile_map" msgid="8907204701162107271">"SMS-ovi"</string>
     <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Pristup SIM-u"</string>
@@ -662,6 +660,8 @@
     <string name="dream_complication_title_weather" msgid="598609151677172783">"Vremenska prognoza"</string>
     <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Kvalitet zraka"</string>
     <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Podaci o emitiranju"</string>
+    <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+    <skip />
     <string name="avatar_picker_title" msgid="8492884172713170652">"Odaberite sliku profila"</string>
     <string name="default_user_icon_description" msgid="6554047177298972638">"Zadana ikona korisnika"</string>
     <string name="physical_keyboard_title" msgid="4811935435315835220">"Fizička tastatura"</string>
diff --git a/packages/SettingsLib/res/values-ca/arrays.xml b/packages/SettingsLib/res/values-ca/arrays.xml
index a267af8..8c34a1f 100644
--- a/packages/SettingsLib/res/values-ca/arrays.xml
+++ b/packages/SettingsLib/res/values-ca/arrays.xml
@@ -85,22 +85,10 @@
     <item msgid="7073042887003102964">"map13"</item>
     <item msgid="8147982633566548515">"map14"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="2494959071796102843">"Utilitza la selecció del sistema (predeterminada)"</item>
-    <item msgid="4055460186095649420">"SBC"</item>
-    <item msgid="720249083677397051">"AAC"</item>
-    <item msgid="1049450003868150455">"Àudio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
-    <item msgid="2908219194098827570">"Àudio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
-    <item msgid="3825367753087348007">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="8868109554557331312">"Utilitza la selecció del sistema (predeterminada)"</item>
-    <item msgid="9024885861221697796">"SBC"</item>
-    <item msgid="4688890470703790013">"AAC"</item>
-    <item msgid="8627333814413492563">"Àudio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
-    <item msgid="3517061573669307965">"Àudio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
-    <item msgid="2553206901068987657">"LDAC"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
     <item msgid="926809261293414607">"Utilitza la selecció del sistema (predeterminada)"</item>
     <item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
index 881f9d4..606a379 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -117,10 +117,8 @@
     <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Transferència de fitxers"</string>
     <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Dispositiu d\'entrada"</string>
     <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Accés a Internet"</string>
-    <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
-    <skip />
-    <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
-    <skip />
+    <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Compartició de contactes i trucades"</string>
+    <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Utilitza per compartir contactes i l\'historial de trucades"</string>
     <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Compartició de connexió d\'Internet"</string>
     <string name="bluetooth_profile_map" msgid="8907204701162107271">"Missatges de text"</string>
     <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Accés a la SIM"</string>
@@ -662,6 +660,8 @@
     <string name="dream_complication_title_weather" msgid="598609151677172783">"Temps"</string>
     <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Qualitat de l\'aire"</string>
     <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Informació d\'emissió"</string>
+    <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+    <skip />
     <string name="avatar_picker_title" msgid="8492884172713170652">"Tria una foto de perfil"</string>
     <string name="default_user_icon_description" msgid="6554047177298972638">"Icona d\'usuari predeterminat"</string>
     <string name="physical_keyboard_title" msgid="4811935435315835220">"Teclat físic"</string>
diff --git a/packages/SettingsLib/res/values-cs/arrays.xml b/packages/SettingsLib/res/values-cs/arrays.xml
index 3eeae64..90bcaa4 100644
--- a/packages/SettingsLib/res/values-cs/arrays.xml
+++ b/packages/SettingsLib/res/values-cs/arrays.xml
@@ -85,22 +85,10 @@
     <item msgid="7073042887003102964">"map13"</item>
     <item msgid="8147982633566548515">"map14"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="2494959071796102843">"Použít systémový výběr (výchozí)"</item>
-    <item msgid="4055460186095649420">"SBC"</item>
-    <item msgid="720249083677397051">"AAC"</item>
-    <item msgid="1049450003868150455">"Zvuk <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
-    <item msgid="2908219194098827570">"Zvuk <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
-    <item msgid="3825367753087348007">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="8868109554557331312">"Použít systémový výběr (výchozí)"</item>
-    <item msgid="9024885861221697796">"SBC"</item>
-    <item msgid="4688890470703790013">"AAC"</item>
-    <item msgid="8627333814413492563">"Zvuk <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
-    <item msgid="3517061573669307965">"Zvuk <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
-    <item msgid="2553206901068987657">"LDAC"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
     <item msgid="926809261293414607">"Použít systémový výběr (výchozí)"</item>
     <item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml
index 7f314bd..37c0bb4 100644
--- a/packages/SettingsLib/res/values-cs/strings.xml
+++ b/packages/SettingsLib/res/values-cs/strings.xml
@@ -117,10 +117,8 @@
     <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Přenos souborů"</string>
     <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Vstupní zařízení"</string>
     <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Přístup k internetu"</string>
-    <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
-    <skip />
-    <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
-    <skip />
+    <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Sdílení kontaktů a historie volání"</string>
+    <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Používat ke sdílení kontaktů a historie hovorů"</string>
     <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Sdílení internetového připojení"</string>
     <string name="bluetooth_profile_map" msgid="8907204701162107271">"Textové zprávy"</string>
     <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Přístup k SIM kartě"</string>
@@ -662,6 +660,8 @@
     <string name="dream_complication_title_weather" msgid="598609151677172783">"Počasí"</string>
     <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Kvalita vzduchu"</string>
     <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Info o odesílání"</string>
+    <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+    <skip />
     <string name="avatar_picker_title" msgid="8492884172713170652">"Vyberte profilový obrázek"</string>
     <string name="default_user_icon_description" msgid="6554047177298972638">"Výchozí uživatelská ikona"</string>
     <string name="physical_keyboard_title" msgid="4811935435315835220">"Fyzická klávesnice"</string>
diff --git a/packages/SettingsLib/res/values-da/arrays.xml b/packages/SettingsLib/res/values-da/arrays.xml
index 58ca722..155104ae 100644
--- a/packages/SettingsLib/res/values-da/arrays.xml
+++ b/packages/SettingsLib/res/values-da/arrays.xml
@@ -85,22 +85,10 @@
     <item msgid="7073042887003102964">"map13"</item>
     <item msgid="8147982633566548515">"map14"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="2494959071796102843">"Brug systemvalg (standard)"</item>
-    <item msgid="4055460186095649420">"SBC"</item>
-    <item msgid="720249083677397051">"AAC"</item>
-    <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>-lyd"</item>
-    <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>-lyd"</item>
-    <item msgid="3825367753087348007">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="8868109554557331312">"Brug systemvalg (standard)"</item>
-    <item msgid="9024885861221697796">"SBC"</item>
-    <item msgid="4688890470703790013">"AAC"</item>
-    <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>-lyd"</item>
-    <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>-lyd"</item>
-    <item msgid="2553206901068987657">"LDAC"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
     <item msgid="926809261293414607">"Brug systemvalg (standard)"</item>
     <item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml
index c470b81..d80e1ec 100644
--- a/packages/SettingsLib/res/values-da/strings.xml
+++ b/packages/SettingsLib/res/values-da/strings.xml
@@ -117,10 +117,8 @@
     <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Filoverførsel"</string>
     <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Inputenhed"</string>
     <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Internetadgang"</string>
-    <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
-    <skip />
-    <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
-    <skip />
+    <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Deling af kontakter og opkaldshistorik"</string>
+    <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Brug til deling af kontakter og opkaldshistorik"</string>
     <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Deling af internetforbindelse"</string>
     <string name="bluetooth_profile_map" msgid="8907204701162107271">"Sms-beskeder"</string>
     <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Adgang til SIM-kort"</string>
@@ -662,6 +660,8 @@
     <string name="dream_complication_title_weather" msgid="598609151677172783">"Vejr"</string>
     <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Luftkvalitet"</string>
     <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Cast-oplysninger"</string>
+    <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+    <skip />
     <string name="avatar_picker_title" msgid="8492884172713170652">"Vælg et profilbillede"</string>
     <string name="default_user_icon_description" msgid="6554047177298972638">"Ikon for standardbruger"</string>
     <string name="physical_keyboard_title" msgid="4811935435315835220">"Fysisk tastatur"</string>
diff --git a/packages/SettingsLib/res/values-de/arrays.xml b/packages/SettingsLib/res/values-de/arrays.xml
index a8eb3f6..31126a8 100644
--- a/packages/SettingsLib/res/values-de/arrays.xml
+++ b/packages/SettingsLib/res/values-de/arrays.xml
@@ -85,22 +85,10 @@
     <item msgid="7073042887003102964">"map13"</item>
     <item msgid="8147982633566548515">"map14"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="2494959071796102843">"Systemauswahl verwenden (Standard)"</item>
-    <item msgid="4055460186095649420">"SBC"</item>
-    <item msgid="720249083677397051">"AAC"</item>
-    <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>-Audio"</item>
-    <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>-Audio"</item>
-    <item msgid="3825367753087348007">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="8868109554557331312">"Systemauswahl verwenden (Standard)"</item>
-    <item msgid="9024885861221697796">"SBC"</item>
-    <item msgid="4688890470703790013">"AAC"</item>
-    <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>-Audio"</item>
-    <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>-Audio"</item>
-    <item msgid="2553206901068987657">"LDAC"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
     <item msgid="926809261293414607">"Systemauswahl verwenden (Standard)"</item>
     <item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml
index 34a622b..d4fafe1 100644
--- a/packages/SettingsLib/res/values-de/strings.xml
+++ b/packages/SettingsLib/res/values-de/strings.xml
@@ -117,10 +117,8 @@
     <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Dateiübertragung"</string>
     <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Eingabegerät"</string>
     <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Internetzugriff"</string>
-    <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
-    <skip />
-    <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
-    <skip />
+    <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Teilen von Kontakten und der Anrufliste"</string>
+    <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Zum Teilen von Kontakten und der Anrufliste verwenden"</string>
     <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Freigabe der Internetverbindung"</string>
     <string name="bluetooth_profile_map" msgid="8907204701162107271">"SMS"</string>
     <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Zugriff auf SIM"</string>
@@ -662,6 +660,8 @@
     <string name="dream_complication_title_weather" msgid="598609151677172783">"Wetter"</string>
     <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Luftqualität"</string>
     <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Streaming-Info"</string>
+    <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+    <skip />
     <string name="avatar_picker_title" msgid="8492884172713170652">"Profilbild auswählen"</string>
     <string name="default_user_icon_description" msgid="6554047177298972638">"Standardmäßiges Nutzersymbol"</string>
     <string name="physical_keyboard_title" msgid="4811935435315835220">"Physische Tastatur"</string>
diff --git a/packages/SettingsLib/res/values-el/arrays.xml b/packages/SettingsLib/res/values-el/arrays.xml
index ccd06fa..70000e1 100644
--- a/packages/SettingsLib/res/values-el/arrays.xml
+++ b/packages/SettingsLib/res/values-el/arrays.xml
@@ -85,22 +85,10 @@
     <item msgid="7073042887003102964">"map13"</item>
     <item msgid="8147982633566548515">"map14"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="2494959071796102843">"Χρήση επιλογής συστήματος (Προεπιλογή)"</item>
-    <item msgid="4055460186095649420">"SBC"</item>
-    <item msgid="720249083677397051">"AAC"</item>
-    <item msgid="1049450003868150455">"Ήχος <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
-    <item msgid="2908219194098827570">"Ήχος <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
-    <item msgid="3825367753087348007">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="8868109554557331312">"Χρήση επιλογής συστήματος (Προεπιλογή)"</item>
-    <item msgid="9024885861221697796">"SBC"</item>
-    <item msgid="4688890470703790013">"AAC"</item>
-    <item msgid="8627333814413492563">"Ήχος <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
-    <item msgid="3517061573669307965">"Ήχος <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
-    <item msgid="2553206901068987657">"LDAC"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
     <item msgid="926809261293414607">"Χρήση επιλογής συστήματος (Προεπιλογή)"</item>
     <item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml
index fdc7419..aa613a5 100644
--- a/packages/SettingsLib/res/values-el/strings.xml
+++ b/packages/SettingsLib/res/values-el/strings.xml
@@ -117,10 +117,8 @@
     <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Μεταφορά αρχείου"</string>
     <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Συσκευή εισόδου"</string>
     <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Πρόσβαση στο Διαδίκτυο"</string>
-    <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
-    <skip />
-    <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
-    <skip />
+    <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Κοινοποίηση επαφών και ιστορικού κλήσεων"</string>
+    <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Χρήση για την κοινοποίηση επαφών και του ιστορικού κλήσεων"</string>
     <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Κοινή χρήση σύνδεσης στο Διαδίκτυο"</string>
     <string name="bluetooth_profile_map" msgid="8907204701162107271">"Μηνύματα κειμένου"</string>
     <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Πρόσβαση SIM"</string>
@@ -662,6 +660,8 @@
     <string name="dream_complication_title_weather" msgid="598609151677172783">"Καιρός"</string>
     <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Ποιότητα αέρα"</string>
     <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Πληροφορίες ηθοποιών"</string>
+    <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+    <skip />
     <string name="avatar_picker_title" msgid="8492884172713170652">"Επιλογή φωτογραφίας προφίλ"</string>
     <string name="default_user_icon_description" msgid="6554047177298972638">"Προεπιλεγμένο εικονίδιο χρήστη"</string>
     <string name="physical_keyboard_title" msgid="4811935435315835220">"Φυσικό πληκτρολόγιο"</string>
diff --git a/packages/SettingsLib/res/values-en-rAU/arrays.xml b/packages/SettingsLib/res/values-en-rAU/arrays.xml
index 697e49a..fc6f791 100644
--- a/packages/SettingsLib/res/values-en-rAU/arrays.xml
+++ b/packages/SettingsLib/res/values-en-rAU/arrays.xml
@@ -85,22 +85,10 @@
     <item msgid="7073042887003102964">"map13"</item>
     <item msgid="8147982633566548515">"map14"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="2494959071796102843">"Use system selection (default)"</item>
-    <item msgid="4055460186095649420">"SBC"</item>
-    <item msgid="720249083677397051">"AAC"</item>
-    <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> audio"</item>
-    <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> audio"</item>
-    <item msgid="3825367753087348007">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="8868109554557331312">"Use system selection (default)"</item>
-    <item msgid="9024885861221697796">"SBC"</item>
-    <item msgid="4688890470703790013">"AAC"</item>
-    <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> audio"</item>
-    <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> audio"</item>
-    <item msgid="2553206901068987657">"LDAC"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
     <item msgid="926809261293414607">"Use system selection (default)"</item>
     <item msgid="8003118270854840095">"44.1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml
index 234109b..62c8e21 100644
--- a/packages/SettingsLib/res/values-en-rAU/strings.xml
+++ b/packages/SettingsLib/res/values-en-rAU/strings.xml
@@ -117,10 +117,8 @@
     <string name="bluetooth_profile_opp" msgid="6692618568149493430">"File transfer"</string>
     <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Input device"</string>
     <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Internet access"</string>
-    <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
-    <skip />
-    <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
-    <skip />
+    <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Contacts and call history sharing"</string>
+    <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Use for contacts and call history sharing"</string>
     <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Internet connection sharing"</string>
     <string name="bluetooth_profile_map" msgid="8907204701162107271">"Text messages"</string>
     <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM access"</string>
@@ -662,6 +660,7 @@
     <string name="dream_complication_title_weather" msgid="598609151677172783">"Weather"</string>
     <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Air quality"</string>
     <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Cast info"</string>
+    <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Home Controls"</string>
     <string name="avatar_picker_title" msgid="8492884172713170652">"Choose a profile picture"</string>
     <string name="default_user_icon_description" msgid="6554047177298972638">"Default user icon"</string>
     <string name="physical_keyboard_title" msgid="4811935435315835220">"Physical keyboard"</string>
diff --git a/packages/SettingsLib/res/values-en-rCA/arrays.xml b/packages/SettingsLib/res/values-en-rCA/arrays.xml
index 697e49a..fc6f791 100644
--- a/packages/SettingsLib/res/values-en-rCA/arrays.xml
+++ b/packages/SettingsLib/res/values-en-rCA/arrays.xml
@@ -85,22 +85,10 @@
     <item msgid="7073042887003102964">"map13"</item>
     <item msgid="8147982633566548515">"map14"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="2494959071796102843">"Use system selection (default)"</item>
-    <item msgid="4055460186095649420">"SBC"</item>
-    <item msgid="720249083677397051">"AAC"</item>
-    <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> audio"</item>
-    <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> audio"</item>
-    <item msgid="3825367753087348007">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="8868109554557331312">"Use system selection (default)"</item>
-    <item msgid="9024885861221697796">"SBC"</item>
-    <item msgid="4688890470703790013">"AAC"</item>
-    <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> audio"</item>
-    <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> audio"</item>
-    <item msgid="2553206901068987657">"LDAC"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
     <item msgid="926809261293414607">"Use system selection (default)"</item>
     <item msgid="8003118270854840095">"44.1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-en-rCA/strings.xml b/packages/SettingsLib/res/values-en-rCA/strings.xml
index d88bb0e..8650b77 100644
--- a/packages/SettingsLib/res/values-en-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-en-rCA/strings.xml
@@ -117,10 +117,8 @@
     <string name="bluetooth_profile_opp" msgid="6692618568149493430">"File transfer"</string>
     <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Input device"</string>
     <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Internet access"</string>
-    <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
-    <skip />
-    <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
-    <skip />
+    <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Contacts and call history sharing"</string>
+    <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Use for contacts and call history sharing"</string>
     <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Internet connection sharing"</string>
     <string name="bluetooth_profile_map" msgid="8907204701162107271">"Text messages"</string>
     <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM access"</string>
@@ -662,6 +660,7 @@
     <string name="dream_complication_title_weather" msgid="598609151677172783">"Weather"</string>
     <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Air quality"</string>
     <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Cast info"</string>
+    <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Home Controls"</string>
     <string name="avatar_picker_title" msgid="8492884172713170652">"Choose a profile picture"</string>
     <string name="default_user_icon_description" msgid="6554047177298972638">"Default user icon"</string>
     <string name="physical_keyboard_title" msgid="4811935435315835220">"Physical keyboard"</string>
diff --git a/packages/SettingsLib/res/values-en-rGB/arrays.xml b/packages/SettingsLib/res/values-en-rGB/arrays.xml
index 697e49a..fc6f791 100644
--- a/packages/SettingsLib/res/values-en-rGB/arrays.xml
+++ b/packages/SettingsLib/res/values-en-rGB/arrays.xml
@@ -85,22 +85,10 @@
     <item msgid="7073042887003102964">"map13"</item>
     <item msgid="8147982633566548515">"map14"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="2494959071796102843">"Use system selection (default)"</item>
-    <item msgid="4055460186095649420">"SBC"</item>
-    <item msgid="720249083677397051">"AAC"</item>
-    <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> audio"</item>
-    <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> audio"</item>
-    <item msgid="3825367753087348007">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="8868109554557331312">"Use system selection (default)"</item>
-    <item msgid="9024885861221697796">"SBC"</item>
-    <item msgid="4688890470703790013">"AAC"</item>
-    <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> audio"</item>
-    <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> audio"</item>
-    <item msgid="2553206901068987657">"LDAC"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
     <item msgid="926809261293414607">"Use system selection (default)"</item>
     <item msgid="8003118270854840095">"44.1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml
index 234109b..62c8e21 100644
--- a/packages/SettingsLib/res/values-en-rGB/strings.xml
+++ b/packages/SettingsLib/res/values-en-rGB/strings.xml
@@ -117,10 +117,8 @@
     <string name="bluetooth_profile_opp" msgid="6692618568149493430">"File transfer"</string>
     <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Input device"</string>
     <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Internet access"</string>
-    <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
-    <skip />
-    <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
-    <skip />
+    <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Contacts and call history sharing"</string>
+    <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Use for contacts and call history sharing"</string>
     <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Internet connection sharing"</string>
     <string name="bluetooth_profile_map" msgid="8907204701162107271">"Text messages"</string>
     <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM access"</string>
@@ -662,6 +660,7 @@
     <string name="dream_complication_title_weather" msgid="598609151677172783">"Weather"</string>
     <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Air quality"</string>
     <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Cast info"</string>
+    <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Home Controls"</string>
     <string name="avatar_picker_title" msgid="8492884172713170652">"Choose a profile picture"</string>
     <string name="default_user_icon_description" msgid="6554047177298972638">"Default user icon"</string>
     <string name="physical_keyboard_title" msgid="4811935435315835220">"Physical keyboard"</string>
diff --git a/packages/SettingsLib/res/values-en-rIN/arrays.xml b/packages/SettingsLib/res/values-en-rIN/arrays.xml
index 697e49a..fc6f791 100644
--- a/packages/SettingsLib/res/values-en-rIN/arrays.xml
+++ b/packages/SettingsLib/res/values-en-rIN/arrays.xml
@@ -85,22 +85,10 @@
     <item msgid="7073042887003102964">"map13"</item>
     <item msgid="8147982633566548515">"map14"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="2494959071796102843">"Use system selection (default)"</item>
-    <item msgid="4055460186095649420">"SBC"</item>
-    <item msgid="720249083677397051">"AAC"</item>
-    <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> audio"</item>
-    <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> audio"</item>
-    <item msgid="3825367753087348007">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="8868109554557331312">"Use system selection (default)"</item>
-    <item msgid="9024885861221697796">"SBC"</item>
-    <item msgid="4688890470703790013">"AAC"</item>
-    <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> audio"</item>
-    <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> audio"</item>
-    <item msgid="2553206901068987657">"LDAC"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
     <item msgid="926809261293414607">"Use system selection (default)"</item>
     <item msgid="8003118270854840095">"44.1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml
index 234109b..62c8e21 100644
--- a/packages/SettingsLib/res/values-en-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-en-rIN/strings.xml
@@ -117,10 +117,8 @@
     <string name="bluetooth_profile_opp" msgid="6692618568149493430">"File transfer"</string>
     <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Input device"</string>
     <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Internet access"</string>
-    <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
-    <skip />
-    <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
-    <skip />
+    <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Contacts and call history sharing"</string>
+    <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Use for contacts and call history sharing"</string>
     <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Internet connection sharing"</string>
     <string name="bluetooth_profile_map" msgid="8907204701162107271">"Text messages"</string>
     <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM access"</string>
@@ -662,6 +660,7 @@
     <string name="dream_complication_title_weather" msgid="598609151677172783">"Weather"</string>
     <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Air quality"</string>
     <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Cast info"</string>
+    <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Home Controls"</string>
     <string name="avatar_picker_title" msgid="8492884172713170652">"Choose a profile picture"</string>
     <string name="default_user_icon_description" msgid="6554047177298972638">"Default user icon"</string>
     <string name="physical_keyboard_title" msgid="4811935435315835220">"Physical keyboard"</string>
diff --git a/packages/SettingsLib/res/values-en-rXC/arrays.xml b/packages/SettingsLib/res/values-en-rXC/arrays.xml
index aca3eb4..34db380 100644
--- a/packages/SettingsLib/res/values-en-rXC/arrays.xml
+++ b/packages/SettingsLib/res/values-en-rXC/arrays.xml
@@ -85,22 +85,10 @@
     <item msgid="7073042887003102964">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‎‎‎‏‎‎‎‏‎‏‎‎‎‎‏‏‏‏‏‏‏‎‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‏‏‎‏‏‎‎‎‏‎‏‏‏‏‎‏‏‏‏‎‏‎‎‎map13‎‏‎‎‏‎"</item>
     <item msgid="8147982633566548515">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‏‎‎‎‏‎‎‎‏‎‎‏‏‎‏‏‏‎‎‏‏‎‎‎‎‏‎‎‏‎‎‎‎‏‏‎‏‏‏‎‎‏‎‎‏‎‎‏‏‎‎‏‎‎‎‏‎‎‎‏‏‎map14‎‏‎‎‏‎"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="2494959071796102843">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‎‎‎‏‎‏‎‎‏‏‏‏‏‏‏‏‎‎‎‎‎‎‎‎‎‏‏‏‎‏‎‎‏‎‎‏‏‏‎‎‎‏‎‏‏‏‎‎‎‎‏‏‎‏‎‏‏‏‎‏‏‎Use System Selection (Default)‎‏‎‎‏‎"</item>
-    <item msgid="4055460186095649420">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‏‏‎‎‎‎‏‎‎‎‏‏‏‏‏‏‎‎‎‏‏‏‎‎‎‏‏‎‏‎‏‏‏‎‎‎‎‏‏‏‏‏‎‎‎‏‎‎‎‎‎‏‎‏‎‎‎‏‏‎‎‎SBC‎‏‎‎‏‎"</item>
-    <item msgid="720249083677397051">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‎‏‎‎‏‏‏‏‏‏‏‏‎‏‏‎‏‎‏‏‎‏‎‏‏‏‎‎‏‏‎‏‏‏‏‏‎‎‏‏‎‎‏‎‎‎‏‎‏‎‎‎‎‎‎‏‏‏‎‏‏‎AAC‎‏‎‎‏‎"</item>
-    <item msgid="1049450003868150455">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‎‏‏‏‎‏‎‎‏‎‎‎‎‎‏‏‎‎‏‎‏‎‎‏‏‎‏‏‏‏‏‏‎‎‎‏‎‎‎‎‏‎‏‏‏‏‏‏‎‏‎‏‎‏‎‏‏‎‏‏‏‎‎‏‎‎‏‏‎<xliff:g id="QUALCOMM">Qualcomm®</xliff:g>‎‏‎‎‏‏‏‎ ‎‏‎‎‏‏‎<xliff:g id="APTX">aptX™</xliff:g>‎‏‎‎‏‏‏‎ audio‎‏‎‎‏‎"</item>
-    <item msgid="2908219194098827570">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‎‏‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‏‏‏‏‏‎‎‏‎‏‏‎‏‏‎‎‏‎‎‏‏‏‎‏‏‏‏‎‎‎‎‎‏‎‎‏‏‎‎‏‎‎‎‏‎‎‏‏‎<xliff:g id="QUALCOMM">Qualcomm®</xliff:g>‎‏‎‎‏‏‏‎ ‎‏‎‎‏‏‎<xliff:g id="APTX_HD">aptX™ HD</xliff:g>‎‏‎‎‏‏‏‎ audio‎‏‎‎‏‎"</item>
-    <item msgid="3825367753087348007">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‏‎‏‎‏‎‎‎‏‎‏‏‎‎‏‏‎‏‏‏‏‏‎‏‏‎‏‎‎‎‎‏‎‏‎‏‎‎‎‎‎‎‎‎‏‎‏‏‎‏‏‎‏‎‎‏‎‎‏‏‏‎LDAC‎‏‎‎‏‎"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="8868109554557331312">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‏‏‎‏‏‎‎‎‏‎‎‎‏‏‏‎‏‏‎‏‎‏‎‏‎‎‏‏‏‏‎‎‏‎‏‎‎‏‎‏‎‎‎‏‏‎‎‏‎‎‏‏‏‎‏‏‏‎‎‎‎‎Use System Selection (Default)‎‏‎‎‏‎"</item>
-    <item msgid="9024885861221697796">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‏‏‏‎‏‎‎‏‏‏‏‏‎‏‏‎‏‎‏‎‏‏‏‎‏‏‏‏‏‏‏‏‏‏‎‎‎‏‎‎‏‎‎‎‎‏‎‎‎‏‎‎‏‎‎‎‎‎‏‎‎‎SBC‎‏‎‎‏‎"</item>
-    <item msgid="4688890470703790013">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‎‎‎‎‏‎‎‎‏‎‎‏‎‎‏‎‎‏‎‎‏‎‎‎‎‏‎‏‎‎‏‎‏‎‎‎‏‎‎‎‎‎‎‏‏‎‏‏‏‎‏‏‏‏‎‏‏‏‏‎‏‎AAC‎‏‎‎‏‎"</item>
-    <item msgid="8627333814413492563">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‏‎‏‏‏‏‎‏‏‏‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎‎‏‎‏‎‎‎‎‎‏‏‎‏‏‎‏‎‏‎‏‎‎‎‎‎‎‎‏‎‏‎‏‎‎‏‏‎‎‏‎‎‏‏‎<xliff:g id="QUALCOMM">Qualcomm®</xliff:g>‎‏‎‎‏‏‏‎ ‎‏‎‎‏‏‎<xliff:g id="APTX">aptX™</xliff:g>‎‏‎‎‏‏‏‎ audio‎‏‎‎‏‎"</item>
-    <item msgid="3517061573669307965">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‏‎‎‎‎‏‏‎‎‏‏‏‏‎‎‎‏‏‏‎‎‏‏‎‏‏‏‎‏‏‏‏‏‎‏‏‏‏‎‏‏‏‏‎‎‎‏‎‎‎‏‏‎‎‎‏‏‏‏‎‏‎‎‏‎‎‏‏‎<xliff:g id="QUALCOMM">Qualcomm®</xliff:g>‎‏‎‎‏‏‏‎ ‎‏‎‎‏‏‎<xliff:g id="APTX_HD">aptX™ HD</xliff:g>‎‏‎‎‏‏‏‎ audio‎‏‎‎‏‎"</item>
-    <item msgid="2553206901068987657">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‎‎‎‏‏‎‏‏‎‏‏‏‎‏‏‎‏‎‎‎‎‎‎‏‎‎‏‏‎‎‎‏‎‏‎‎‎‎‏‎‎‏‎‏‎‏‏‏‏‏‏‎‏‎‎‎‎‏‎‎‏‎LDAC‎‏‎‎‏‎"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
     <item msgid="926809261293414607">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‎‏‏‎‎‏‏‎‏‏‏‎‎‏‎‏‏‎‎‎‎‎‎‎‏‏‏‎‎‏‏‎‏‏‎‎‏‏‏‏‎‏‏‏‏‏‎‏‎‏‎‎‎‏‏‎‎‏‏‏‏‎Use System Selection (Default)‎‏‎‎‏‎"</item>
     <item msgid="8003118270854840095">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‎‏‏‏‏‎‎‎‏‎‎‎‎‏‏‎‎‏‎‎‏‏‎‏‎‏‎‏‎‎‎‏‏‏‏‎‏‏‎‏‏‏‏‎‏‏‎‏‏‏‏‏‏‎‎‎‏‏‏‏‏‎44.1 kHz‎‏‎‎‏‎"</item>
diff --git a/packages/SettingsLib/res/values-en-rXC/strings.xml b/packages/SettingsLib/res/values-en-rXC/strings.xml
index 5d775d3..21697b9 100644
--- a/packages/SettingsLib/res/values-en-rXC/strings.xml
+++ b/packages/SettingsLib/res/values-en-rXC/strings.xml
@@ -660,6 +660,7 @@
     <string name="dream_complication_title_weather" msgid="598609151677172783">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‎‏‎‎‎‎‏‎‎‏‏‏‎‏‎‏‎‏‏‏‏‏‏‎‏‏‎‏‎‎‎‎‏‎‏‎‏‎‎‎‎‎‏‎‎‎‎‎‎‏‏‎‎‎‎‏‎‏‏‏‏‎Weather‎‏‎‎‏‎"</string>
     <string name="dream_complication_title_aqi" msgid="4587552608957834110">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‏‏‏‏‏‏‎‏‎‏‎‏‎‎‏‎‎‎‎‏‎‏‏‎‎‏‎‏‎‏‎‏‏‏‏‎‏‎‏‎‏‏‎‎‏‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‎‎Air Quality‎‏‎‎‏‎"</string>
     <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‏‏‎‎‎‎‎‎‎‏‏‎‎‏‎‎‏‏‏‎‏‏‏‏‏‎‏‏‏‏‎‏‎‎‏‏‏‏‏‎‎‏‎‎‎‎‎‎‏‏‏‎‏‏‎‎‏‏‏‎‎‎Cast Info‎‏‎‎‏‎"</string>
+    <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‏‏‏‏‏‎‎‎‎‎‏‏‏‎‏‎‏‏‎‎‎‎‎‎‏‏‎‎‏‎‎‎‏‏‎‎‏‎‏‎‏‏‏‎‎‎‏‏‏‎‏‎‎‏‏‏‏‏‎‏‏‎Home Controls‎‏‎‎‏‎"</string>
     <string name="avatar_picker_title" msgid="8492884172713170652">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‏‎‏‎‏‏‏‎‏‏‏‎‎‏‏‎‎‏‎‎‏‎‎‏‎‏‎‎‎‎‏‏‎‏‎‎‏‎‎‎‏‏‎‏‏‎‎‎‏‎‏‏‎‏‏‎‏‏‏‎‎‎Choose a profile picture‎‏‎‎‏‎"</string>
     <string name="default_user_icon_description" msgid="6554047177298972638">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‏‏‎‏‎‏‏‏‏‎‏‎‎‏‎‏‎‎‏‏‏‎‎‏‎‏‏‏‎‏‏‎‏‎‏‎‎‎‎‎‎‏‏‏‎‏‎‎‎‏‏‏‏‏‏‎‏‏‏‏‎‎Default user icon‎‏‎‎‏‎"</string>
     <string name="physical_keyboard_title" msgid="4811935435315835220">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‎‎‎‏‎‏‏‎‎‎‏‏‏‎‏‏‎‏‏‎‏‏‏‎‎‏‎‎‎‏‎‏‎‎‎‎‎‎‎‎‏‏‏‎‏‎‏‏‏‎‎‎‏‎‏‎‏‎‏‎‎‎Physical keyboard‎‏‎‎‏‎"</string>
diff --git a/packages/SettingsLib/res/values-es-rUS/arrays.xml b/packages/SettingsLib/res/values-es-rUS/arrays.xml
index 6a926d2..9b1aa3a 100644
--- a/packages/SettingsLib/res/values-es-rUS/arrays.xml
+++ b/packages/SettingsLib/res/values-es-rUS/arrays.xml
@@ -85,22 +85,10 @@
     <item msgid="7073042887003102964">"map13"</item>
     <item msgid="8147982633566548515">"map14"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="2494959071796102843">"Usar selección del sistema (predeterminado)"</item>
-    <item msgid="4055460186095649420">"SBC"</item>
-    <item msgid="720249083677397051">"AAC"</item>
-    <item msgid="1049450003868150455">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
-    <item msgid="2908219194098827570">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
-    <item msgid="3825367753087348007">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="8868109554557331312">"Usar selección del sistema (predeterminado)"</item>
-    <item msgid="9024885861221697796">"SBC"</item>
-    <item msgid="4688890470703790013">"AAC"</item>
-    <item msgid="8627333814413492563">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
-    <item msgid="3517061573669307965">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
-    <item msgid="2553206901068987657">"LDAC"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
     <item msgid="926809261293414607">"Usar selección del sistema (predeterminado)"</item>
     <item msgid="8003118270854840095">"44.1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index 98c1400..27cd0ab 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -117,10 +117,8 @@
     <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Transferencia de archivos"</string>
     <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Dispositivo de entrada"</string>
     <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Acceso a Internet"</string>
-    <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
-    <skip />
-    <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
-    <skip />
+    <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Compartir contactos e historial de llam."</string>
+    <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Uso para compartir contactos e historial de llamadas"</string>
     <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Compartir conexión a Internet"</string>
     <string name="bluetooth_profile_map" msgid="8907204701162107271">"Mensajes de texto"</string>
     <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Acceso a SIM"</string>
@@ -662,6 +660,7 @@
     <string name="dream_complication_title_weather" msgid="598609151677172783">"Clima"</string>
     <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Calidad del aire"</string>
     <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Info de reparto"</string>
+    <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Control de la casa"</string>
     <string name="avatar_picker_title" msgid="8492884172713170652">"Elige una foto de perfil"</string>
     <string name="default_user_icon_description" msgid="6554047177298972638">"Ícono de usuario predeterminado"</string>
     <string name="physical_keyboard_title" msgid="4811935435315835220">"Teclado físico"</string>
diff --git a/packages/SettingsLib/res/values-es/arrays.xml b/packages/SettingsLib/res/values-es/arrays.xml
index 7c37fa5..0677864 100644
--- a/packages/SettingsLib/res/values-es/arrays.xml
+++ b/packages/SettingsLib/res/values-es/arrays.xml
@@ -85,22 +85,10 @@
     <item msgid="7073042887003102964">"map13"</item>
     <item msgid="8147982633566548515">"map14"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="2494959071796102843">"Usar preferencia del sistema (predeterminado)"</item>
-    <item msgid="4055460186095649420">"SBC"</item>
-    <item msgid="720249083677397051">"AAC"</item>
-    <item msgid="1049450003868150455">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
-    <item msgid="2908219194098827570">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
-    <item msgid="3825367753087348007">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="8868109554557331312">"Usar preferencia del sistema (predeterminado)"</item>
-    <item msgid="9024885861221697796">"SBC"</item>
-    <item msgid="4688890470703790013">"AAC"</item>
-    <item msgid="8627333814413492563">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
-    <item msgid="3517061573669307965">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
-    <item msgid="2553206901068987657">"LDAC"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
     <item msgid="926809261293414607">"Usar preferencia del sistema (predeterminado)"</item>
     <item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml
index 90c4fb8..d221c8d 100644
--- a/packages/SettingsLib/res/values-es/strings.xml
+++ b/packages/SettingsLib/res/values-es/strings.xml
@@ -117,10 +117,8 @@
     <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Transferencia de archivos"</string>
     <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Dispositivo de entrada"</string>
     <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Acceso a Internet"</string>
-    <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
-    <skip />
-    <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
-    <skip />
+    <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Compartir contactos e historial de llamadas"</string>
+    <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Usar para compartir los contactos y el historial de llamadas"</string>
     <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Compartir conexión a Internet"</string>
     <string name="bluetooth_profile_map" msgid="8907204701162107271">"Mensajes de texto"</string>
     <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Acceso a tarjeta SIM"</string>
@@ -662,6 +660,8 @@
     <string name="dream_complication_title_weather" msgid="598609151677172783">"Tiempo"</string>
     <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Calidad del aire"</string>
     <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Info. de emisión"</string>
+    <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+    <skip />
     <string name="avatar_picker_title" msgid="8492884172713170652">"Elige una imagen de perfil"</string>
     <string name="default_user_icon_description" msgid="6554047177298972638">"Icono de usuario predeterminado"</string>
     <string name="physical_keyboard_title" msgid="4811935435315835220">"Teclado físico"</string>
diff --git a/packages/SettingsLib/res/values-et/arrays.xml b/packages/SettingsLib/res/values-et/arrays.xml
index 14d0fd1..d986ecf 100644
--- a/packages/SettingsLib/res/values-et/arrays.xml
+++ b/packages/SettingsLib/res/values-et/arrays.xml
@@ -85,22 +85,10 @@
     <item msgid="7073042887003102964">"map13"</item>
     <item msgid="8147982633566548515">"map14"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="2494959071796102843">"Süsteemi valiku kasutamine (vaikeseade)"</item>
-    <item msgid="4055460186095649420">"SBC"</item>
-    <item msgid="720249083677397051">"AAC"</item>
-    <item msgid="1049450003868150455">"Heli: <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
-    <item msgid="2908219194098827570">"Heli: <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
-    <item msgid="3825367753087348007">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="8868109554557331312">"Süsteemi valiku kasutamine (vaikeseade)"</item>
-    <item msgid="9024885861221697796">"SBC"</item>
-    <item msgid="4688890470703790013">"AAC"</item>
-    <item msgid="8627333814413492563">"Heli: <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
-    <item msgid="3517061573669307965">"Heli: <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
-    <item msgid="2553206901068987657">"LDAC"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
     <item msgid="926809261293414607">"Süsteemi valiku kasutamine (vaikeseade)"</item>
     <item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml
index a6c787a..f32ae23 100644
--- a/packages/SettingsLib/res/values-et/strings.xml
+++ b/packages/SettingsLib/res/values-et/strings.xml
@@ -117,10 +117,8 @@
     <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Failiedastus"</string>
     <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Sisendseade"</string>
     <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Juurdepääs internetile"</string>
-    <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
-    <skip />
-    <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
-    <skip />
+    <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Kontaktide ja kõneajaloo jagamine"</string>
+    <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Kasutage kontaktide ja kõneajaloo jagamiseks"</string>
     <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Interneti-ühenduse jagamine"</string>
     <string name="bluetooth_profile_map" msgid="8907204701162107271">"Tekstsõnumid"</string>
     <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Juurdepääs SIM-ile"</string>
@@ -662,6 +660,8 @@
     <string name="dream_complication_title_weather" msgid="598609151677172783">"Ilm"</string>
     <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Õhukvaliteet"</string>
     <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Osatäitjate teave"</string>
+    <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+    <skip />
     <string name="avatar_picker_title" msgid="8492884172713170652">"Valige profiilipilt"</string>
     <string name="default_user_icon_description" msgid="6554047177298972638">"Vaikekasutajaikoon"</string>
     <string name="physical_keyboard_title" msgid="4811935435315835220">"Füüsiline klaviatuur"</string>
diff --git a/packages/SettingsLib/res/values-eu/arrays.xml b/packages/SettingsLib/res/values-eu/arrays.xml
index cc47e27..d166e1b 100644
--- a/packages/SettingsLib/res/values-eu/arrays.xml
+++ b/packages/SettingsLib/res/values-eu/arrays.xml
@@ -85,22 +85,10 @@
     <item msgid="7073042887003102964">"map13"</item>
     <item msgid="8147982633566548515">"map14"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="2494959071796102843">"Erabili sistema-hautapena (lehenetsia)"</item>
-    <item msgid="4055460186095649420">"SBC"</item>
-    <item msgid="720249083677397051">"AAC"</item>
-    <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> audioa"</item>
-    <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> audioa"</item>
-    <item msgid="3825367753087348007">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="8868109554557331312">"Erabili sistema-hautapena (lehenetsia)"</item>
-    <item msgid="9024885861221697796">"SBC"</item>
-    <item msgid="4688890470703790013">"AAC"</item>
-    <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> audioa"</item>
-    <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> audioa"</item>
-    <item msgid="2553206901068987657">"LDAC"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
     <item msgid="926809261293414607">"Erabili sistema-hautapena (lehenetsia)"</item>
     <item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml
index 87cfaa8..bcb57e8 100644
--- a/packages/SettingsLib/res/values-eu/strings.xml
+++ b/packages/SettingsLib/res/values-eu/strings.xml
@@ -117,10 +117,8 @@
     <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Fitxategi-transferentzia"</string>
     <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Sarrerako gailua"</string>
     <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Interneteko konexioa"</string>
-    <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
-    <skip />
-    <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
-    <skip />
+    <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Kontaktuak eta deien historia partekatzeko aukera"</string>
+    <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Erabili kontaktuetarako eta deien historia partekatzeko"</string>
     <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Interneteko konexioa partekatzea"</string>
     <string name="bluetooth_profile_map" msgid="8907204701162107271">"Testu-mezuak"</string>
     <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIMerako sarbidea"</string>
@@ -662,6 +660,8 @@
     <string name="dream_complication_title_weather" msgid="598609151677172783">"Eguraldia"</string>
     <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Airearen kalitatea"</string>
     <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Igorpenari buruzko informazioa"</string>
+    <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+    <skip />
     <string name="avatar_picker_title" msgid="8492884172713170652">"Aukeratu profileko argazki bat"</string>
     <string name="default_user_icon_description" msgid="6554047177298972638">"Erabiltzaile lehenetsiaren ikonoa"</string>
     <string name="physical_keyboard_title" msgid="4811935435315835220">"Teklatu fisikoa"</string>
diff --git a/packages/SettingsLib/res/values-fa/arrays.xml b/packages/SettingsLib/res/values-fa/arrays.xml
index d76389b..b7761dd 100644
--- a/packages/SettingsLib/res/values-fa/arrays.xml
+++ b/packages/SettingsLib/res/values-fa/arrays.xml
@@ -85,22 +85,10 @@
     <item msgid="7073042887003102964">"map13"</item>
     <item msgid="8147982633566548515">"map14"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="2494959071796102843">"استفاده از انتخاب سیستم (پیش‌فرض)"</item>
-    <item msgid="4055460186095649420">"SBC"</item>
-    <item msgid="720249083677397051">"AAC"</item>
-    <item msgid="1049450003868150455">"صوت <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
-    <item msgid="2908219194098827570">"صوت <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
-    <item msgid="3825367753087348007">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="8868109554557331312">"استفاده از انتخاب سیستم (پیش‌فرض)"</item>
-    <item msgid="9024885861221697796">"SBC"</item>
-    <item msgid="4688890470703790013">"AAC"</item>
-    <item msgid="8627333814413492563">"صوت <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
-    <item msgid="3517061573669307965">"صوت <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
-    <item msgid="2553206901068987657">"LDAC"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
     <item msgid="926809261293414607">"استفاده از انتخاب سیستم (پیش‌فرض)"</item>
     <item msgid="8003118270854840095">"۴۴٫۱ کیلوهرتز"</item>
diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml
index 51d74dd..6abe873 100644
--- a/packages/SettingsLib/res/values-fa/strings.xml
+++ b/packages/SettingsLib/res/values-fa/strings.xml
@@ -117,10 +117,8 @@
     <string name="bluetooth_profile_opp" msgid="6692618568149493430">"انتقال فایل"</string>
     <string name="bluetooth_profile_hid" msgid="2969922922664315866">"دستگاه ورودی"</string>
     <string name="bluetooth_profile_pan" msgid="1006235139308318188">"دسترسی به اینترنت"</string>
-    <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
-    <skip />
-    <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
-    <skip />
+    <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"هم‌رسانی مخاطبین و سابقه تماس"</string>
+    <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"استفاده برای هم‌رسانی مخاطبین و سابقه تماس"</string>
     <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"اشتراک‌گذاری اتصال اینترنت"</string>
     <string name="bluetooth_profile_map" msgid="8907204701162107271">"پیام‌های نوشتاری"</string>
     <string name="bluetooth_profile_sap" msgid="8304170950447934386">"دسترسی سیم‌کارت"</string>
@@ -662,6 +660,8 @@
     <string name="dream_complication_title_weather" msgid="598609151677172783">"آب‌وهوا"</string>
     <string name="dream_complication_title_aqi" msgid="4587552608957834110">"کیفیت هوا"</string>
     <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"اطلاعات پخش محتوا"</string>
+    <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+    <skip />
     <string name="avatar_picker_title" msgid="8492884172713170652">"انتخاب عکس نمایه"</string>
     <string name="default_user_icon_description" msgid="6554047177298972638">"نماد کاربر پیش‌فرض"</string>
     <string name="physical_keyboard_title" msgid="4811935435315835220">"صفحه‌کلید فیزیکی"</string>
diff --git a/packages/SettingsLib/res/values-fi/arrays.xml b/packages/SettingsLib/res/values-fi/arrays.xml
index f828186..2969892 100644
--- a/packages/SettingsLib/res/values-fi/arrays.xml
+++ b/packages/SettingsLib/res/values-fi/arrays.xml
@@ -85,22 +85,10 @@
     <item msgid="7073042887003102964">"map13"</item>
     <item msgid="8147982633566548515">"map14"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="2494959071796102843">"Käytä järjestelmän valintaa (oletus)"</item>
-    <item msgid="4055460186095649420">"SBC"</item>
-    <item msgid="720249083677397051">"AAC"</item>
-    <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> ‑ääni"</item>
-    <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> ‑ääni"</item>
-    <item msgid="3825367753087348007">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="8868109554557331312">"Käytä järjestelmän valintaa (oletus)"</item>
-    <item msgid="9024885861221697796">"SBC"</item>
-    <item msgid="4688890470703790013">"AAC"</item>
-    <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> ‑ääni"</item>
-    <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> ‑ääni"</item>
-    <item msgid="2553206901068987657">"LDAC"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
     <item msgid="926809261293414607">"Käytä järjestelmän valintaa (oletus)"</item>
     <item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml
index c0f90b8..2f3436e 100644
--- a/packages/SettingsLib/res/values-fi/strings.xml
+++ b/packages/SettingsLib/res/values-fi/strings.xml
@@ -117,10 +117,8 @@
     <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Tiedostonsiirto"</string>
     <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Syöttölaite"</string>
     <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Internetyhteys"</string>
-    <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
-    <skip />
-    <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
-    <skip />
+    <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Yhteystietojen ja soittohistorian jako"</string>
+    <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Käytä yhteystiedoissa ja soittohistorian jakamiseen"</string>
     <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Internetyhteyden jakaminen"</string>
     <string name="bluetooth_profile_map" msgid="8907204701162107271">"Tekstiviestit"</string>
     <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM-kortin käyttö"</string>
@@ -662,6 +660,8 @@
     <string name="dream_complication_title_weather" msgid="598609151677172783">"Sää"</string>
     <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Ilmanlaatu"</string>
     <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Striimaustiedot"</string>
+    <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+    <skip />
     <string name="avatar_picker_title" msgid="8492884172713170652">"Valitse profiilikuva"</string>
     <string name="default_user_icon_description" msgid="6554047177298972638">"Oletuskäyttäjäkuvake"</string>
     <string name="physical_keyboard_title" msgid="4811935435315835220">"Fyysinen näppäimistö"</string>
diff --git a/packages/SettingsLib/res/values-fr-rCA/arrays.xml b/packages/SettingsLib/res/values-fr-rCA/arrays.xml
index 50c1bcb..12acbb6 100644
--- a/packages/SettingsLib/res/values-fr-rCA/arrays.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/arrays.xml
@@ -85,22 +85,10 @@
     <item msgid="7073042887003102964">"map13"</item>
     <item msgid="8147982633566548515">"map14"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="2494959071796102843">"Utiliser sélect. du système (par défaut)"</item>
-    <item msgid="4055460186095649420">"SBC"</item>
-    <item msgid="720249083677397051">"AAC"</item>
-    <item msgid="1049450003868150455">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
-    <item msgid="2908219194098827570">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
-    <item msgid="3825367753087348007">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="8868109554557331312">"Utiliser sélect. du système (par défaut)"</item>
-    <item msgid="9024885861221697796">"SBC"</item>
-    <item msgid="4688890470703790013">"AAC"</item>
-    <item msgid="8627333814413492563">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
-    <item msgid="3517061573669307965">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
-    <item msgid="2553206901068987657">"LDAC"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
     <item msgid="926809261293414607">"Utiliser sélect. du système (par défaut)"</item>
     <item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml
index 14515cd..28b3cd9 100644
--- a/packages/SettingsLib/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml
@@ -117,10 +117,8 @@
     <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Transfert de fichier"</string>
     <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Périphérique d\'entrée"</string>
     <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Accès Internet"</string>
-    <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
-    <skip />
-    <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
-    <skip />
+    <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Partage des contacts et des appels"</string>
+    <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Sert à partager des contacts et l\'historique des appels"</string>
     <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Partage de connexion Internet"</string>
     <string name="bluetooth_profile_map" msgid="8907204701162107271">"Messages texte"</string>
     <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Accès à la carte SIM"</string>
@@ -662,6 +660,8 @@
     <string name="dream_complication_title_weather" msgid="598609151677172783">"Météo"</string>
     <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Qualité de l\'air"</string>
     <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Info diffusion"</string>
+    <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+    <skip />
     <string name="avatar_picker_title" msgid="8492884172713170652">"Choisir une photo de profil"</string>
     <string name="default_user_icon_description" msgid="6554047177298972638">"Icône d\'utilisateur par défaut"</string>
     <string name="physical_keyboard_title" msgid="4811935435315835220">"Clavier physique"</string>
diff --git a/packages/SettingsLib/res/values-fr/arrays.xml b/packages/SettingsLib/res/values-fr/arrays.xml
index 6343f0d..80ac7e4 100644
--- a/packages/SettingsLib/res/values-fr/arrays.xml
+++ b/packages/SettingsLib/res/values-fr/arrays.xml
@@ -85,22 +85,10 @@
     <item msgid="7073042887003102964">"map13"</item>
     <item msgid="8147982633566548515">"map14"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="2494959071796102843">"Utiliser la sélection du système (par défaut)"</item>
-    <item msgid="4055460186095649420">"SBC"</item>
-    <item msgid="720249083677397051">"AAC"</item>
-    <item msgid="1049450003868150455">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
-    <item msgid="2908219194098827570">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
-    <item msgid="3825367753087348007">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="8868109554557331312">"Utiliser la sélection du système (par défaut)"</item>
-    <item msgid="9024885861221697796">"SBC"</item>
-    <item msgid="4688890470703790013">"AAC"</item>
-    <item msgid="8627333814413492563">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
-    <item msgid="3517061573669307965">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
-    <item msgid="2553206901068987657">"LDAC"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
     <item msgid="926809261293414607">"Utiliser la sélection du système (par défaut)"</item>
     <item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml
index 19ea6dd..da1497a 100644
--- a/packages/SettingsLib/res/values-fr/strings.xml
+++ b/packages/SettingsLib/res/values-fr/strings.xml
@@ -117,10 +117,8 @@
     <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Transfert de fichiers"</string>
     <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Périphérique d\'entrée"</string>
     <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Accès Internet"</string>
-    <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
-    <skip />
-    <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
-    <skip />
+    <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Partage contacts/historique des appels"</string>
+    <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"À utiliser pour partage des contacts/historique des appels"</string>
     <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Partage de connexion Internet"</string>
     <string name="bluetooth_profile_map" msgid="8907204701162107271">"SMS"</string>
     <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Accès à la carte SIM"</string>
@@ -662,6 +660,8 @@
     <string name="dream_complication_title_weather" msgid="598609151677172783">"Météo"</string>
     <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Qualité de l\'air"</string>
     <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Infos distribution"</string>
+    <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+    <skip />
     <string name="avatar_picker_title" msgid="8492884172713170652">"Choisissez une photo de profil"</string>
     <string name="default_user_icon_description" msgid="6554047177298972638">"Icône de l\'utilisateur par défaut"</string>
     <string name="physical_keyboard_title" msgid="4811935435315835220">"Clavier physique"</string>
diff --git a/packages/SettingsLib/res/values-gl/arrays.xml b/packages/SettingsLib/res/values-gl/arrays.xml
index 22fb223..b6cf48e 100644
--- a/packages/SettingsLib/res/values-gl/arrays.xml
+++ b/packages/SettingsLib/res/values-gl/arrays.xml
@@ -85,22 +85,10 @@
     <item msgid="7073042887003102964">"map13"</item>
     <item msgid="8147982633566548515">"map14"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="2494959071796102843">"Usar selección do sistema (predeterminado)"</item>
-    <item msgid="4055460186095649420">"SBC"</item>
-    <item msgid="720249083677397051">"AAC"</item>
-    <item msgid="1049450003868150455">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
-    <item msgid="2908219194098827570">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
-    <item msgid="3825367753087348007">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="8868109554557331312">"Usa a selección do sistema (predeterminado)"</item>
-    <item msgid="9024885861221697796">"SBC"</item>
-    <item msgid="4688890470703790013">"AAC"</item>
-    <item msgid="8627333814413492563">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
-    <item msgid="3517061573669307965">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
-    <item msgid="2553206901068987657">"LDAC"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
     <item msgid="926809261293414607">"Usar selección do sistema (predeterminado)"</item>
     <item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml
index 94fa72a..7822749 100644
--- a/packages/SettingsLib/res/values-gl/strings.xml
+++ b/packages/SettingsLib/res/values-gl/strings.xml
@@ -117,10 +117,8 @@
     <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Transferencia de ficheiros"</string>
     <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Dispositivo de entrada"</string>
     <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Acceso a Internet"</string>
-    <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
-    <skip />
-    <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
-    <skip />
+    <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Compartir contactos e hist. de chamadas"</string>
+    <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Uso da opción de compartir contactos e historial de chamadas"</string>
     <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Uso compartido da conexión a Internet"</string>
     <string name="bluetooth_profile_map" msgid="8907204701162107271">"Mensaxes de texto"</string>
     <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Acceso á SIM"</string>
@@ -662,6 +660,8 @@
     <string name="dream_complication_title_weather" msgid="598609151677172783">"O tempo"</string>
     <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Calidade do aire"</string>
     <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Datos da emisión"</string>
+    <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+    <skip />
     <string name="avatar_picker_title" msgid="8492884172713170652">"Escolle unha imaxe do perfil"</string>
     <string name="default_user_icon_description" msgid="6554047177298972638">"Icona do usuario predeterminado"</string>
     <string name="physical_keyboard_title" msgid="4811935435315835220">"Teclado físico"</string>
diff --git a/packages/SettingsLib/res/values-gu/arrays.xml b/packages/SettingsLib/res/values-gu/arrays.xml
index 318b5f5..7e668e7 100644
--- a/packages/SettingsLib/res/values-gu/arrays.xml
+++ b/packages/SettingsLib/res/values-gu/arrays.xml
@@ -85,22 +85,10 @@
     <item msgid="7073042887003102964">"map13"</item>
     <item msgid="8147982633566548515">"map14"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="2494959071796102843">"સિસ્ટમ પસંદગીનો ઉપયોગ કરો (ડિફૉલ્ટ)"</item>
-    <item msgid="4055460186095649420">"SBC"</item>
-    <item msgid="720249083677397051">"AAC"</item>
-    <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> ઑડિયો"</item>
-    <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> ઑડિયો"</item>
-    <item msgid="3825367753087348007">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="8868109554557331312">"સિસ્ટમ પસંદગીનો ઉપયોગ કરો (ડિફૉલ્ટ)"</item>
-    <item msgid="9024885861221697796">"SBC"</item>
-    <item msgid="4688890470703790013">"AAC"</item>
-    <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> ઑડિયો"</item>
-    <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> ઑડિયો"</item>
-    <item msgid="2553206901068987657">"LDAC"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
     <item msgid="926809261293414607">"સિસ્ટમ પસંદગીનો ઉપયોગ કરો (ડિફૉલ્ટ)"</item>
     <item msgid="8003118270854840095">"44.1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml
index a474d82..cebb1fe 100644
--- a/packages/SettingsLib/res/values-gu/strings.xml
+++ b/packages/SettingsLib/res/values-gu/strings.xml
@@ -117,10 +117,8 @@
     <string name="bluetooth_profile_opp" msgid="6692618568149493430">"ફાઇલ સ્થાનાંતરણ"</string>
     <string name="bluetooth_profile_hid" msgid="2969922922664315866">"ઇનપુટ ડિવાઇસ"</string>
     <string name="bluetooth_profile_pan" msgid="1006235139308318188">"ઇન્ટરનેટ ઍક્સેસ"</string>
-    <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
-    <skip />
-    <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
-    <skip />
+    <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"સંપર્કો અને કૉલ ઇતિહાસની શેરિંગ"</string>
+    <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"સંપર્કો અને કૉલ ઇતિહાસની શેરિંગ માટે ઉપયોગ કરો"</string>
     <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"ઇન્ટરનેટ કનેક્શન શેરિંગ"</string>
     <string name="bluetooth_profile_map" msgid="8907204701162107271">"ટેક્સ્ટ સંદેશા"</string>
     <string name="bluetooth_profile_sap" msgid="8304170950447934386">"સિમ ઍક્સેસ"</string>
@@ -662,6 +660,8 @@
     <string name="dream_complication_title_weather" msgid="598609151677172783">"હવામાન"</string>
     <string name="dream_complication_title_aqi" msgid="4587552608957834110">"હવાની ક્વૉલિટી"</string>
     <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"કાસ્ટ વિશેની માહિતી"</string>
+    <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+    <skip />
     <string name="avatar_picker_title" msgid="8492884172713170652">"પ્રોફાઇલ ફોટો પસંદ કરો"</string>
     <string name="default_user_icon_description" msgid="6554047177298972638">"ડિફૉલ્ટ વપરાશકર્તાનું આઇકન"</string>
     <string name="physical_keyboard_title" msgid="4811935435315835220">"ભૌતિક કીબોર્ડ"</string>
diff --git a/packages/SettingsLib/res/values-hi/arrays.xml b/packages/SettingsLib/res/values-hi/arrays.xml
index 61a8f92..13da75b 100644
--- a/packages/SettingsLib/res/values-hi/arrays.xml
+++ b/packages/SettingsLib/res/values-hi/arrays.xml
@@ -85,22 +85,10 @@
     <item msgid="7073042887003102964">"map13"</item>
     <item msgid="8147982633566548515">"map14"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="2494959071796102843">"सिस्टम से चुने जाने का उपयोग करें (डिफ़ॉल्ट)"</item>
-    <item msgid="4055460186095649420">"SBC"</item>
-    <item msgid="720249083677397051">"AAC"</item>
-    <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> ऑडियो"</item>
-    <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> ऑडियो"</item>
-    <item msgid="3825367753087348007">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="8868109554557331312">"सिस्टम से चुने जाने का इस्तेमाल करें (डिफ़ॉल्ट)"</item>
-    <item msgid="9024885861221697796">"SBC"</item>
-    <item msgid="4688890470703790013">"AAC"</item>
-    <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> ऑडियो"</item>
-    <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> ऑडियो"</item>
-    <item msgid="2553206901068987657">"LDAC"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
     <item msgid="926809261293414607">"सिस्टम से चुने जाने का उपयोग करें (डिफ़ॉल्ट)"</item>
     <item msgid="8003118270854840095">"44.1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index 55fc547..417d2b2 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -117,10 +117,8 @@
     <string name="bluetooth_profile_opp" msgid="6692618568149493430">"फ़ाइल स्थानांतरण"</string>
     <string name="bluetooth_profile_hid" msgid="2969922922664315866">"इनपुट डिवाइस"</string>
     <string name="bluetooth_profile_pan" msgid="1006235139308318188">"इंटरनेट ऐक्सेस"</string>
-    <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
-    <skip />
-    <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
-    <skip />
+    <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"संपर्क और कॉल का इतिहास शेयर करें"</string>
+    <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"इसका इस्तेमाल संपर्क और कॉल का इतिहास शेयर करने के लिए करें"</string>
     <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"इंटरनेट कनेक्शन साझाकरण"</string>
     <string name="bluetooth_profile_map" msgid="8907204701162107271">"लेख संदेश"</string>
     <string name="bluetooth_profile_sap" msgid="8304170950447934386">"सिम ऐक्सेस"</string>
@@ -662,6 +660,8 @@
     <string name="dream_complication_title_weather" msgid="598609151677172783">"मौसम"</string>
     <string name="dream_complication_title_aqi" msgid="4587552608957834110">"हवा की क्वालिटी"</string>
     <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"कास्टिंग की जानकारी"</string>
+    <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+    <skip />
     <string name="avatar_picker_title" msgid="8492884172713170652">"प्रोफ़ाइल फ़ोटो चुनें"</string>
     <string name="default_user_icon_description" msgid="6554047177298972638">"उपयोगकर्ता के लिए डिफ़ॉल्ट आइकॉन"</string>
     <string name="physical_keyboard_title" msgid="4811935435315835220">"फ़िज़िकल कीबोर्ड"</string>
diff --git a/packages/SettingsLib/res/values-hr/arrays.xml b/packages/SettingsLib/res/values-hr/arrays.xml
index c979bc4..0e66858 100644
--- a/packages/SettingsLib/res/values-hr/arrays.xml
+++ b/packages/SettingsLib/res/values-hr/arrays.xml
@@ -85,22 +85,10 @@
     <item msgid="7073042887003102964">"map13"</item>
     <item msgid="8147982633566548515">"map14"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="2494959071796102843">"Upotreba odabira sustava (zadano)"</item>
-    <item msgid="4055460186095649420">"SBC"</item>
-    <item msgid="720249083677397051">"AAC"</item>
-    <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> audio"</item>
-    <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> audio"</item>
-    <item msgid="3825367753087348007">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="8868109554557331312">"Upotreba odabira sustava (zadano)"</item>
-    <item msgid="9024885861221697796">"SBC"</item>
-    <item msgid="4688890470703790013">"AAC"</item>
-    <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> audio"</item>
-    <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> audio"</item>
-    <item msgid="2553206901068987657">"LDAC"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
     <item msgid="926809261293414607">"Upotreba odabira sustava (zadano)"</item>
     <item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml
index 55a3e5e..267000f 100644
--- a/packages/SettingsLib/res/values-hr/strings.xml
+++ b/packages/SettingsLib/res/values-hr/strings.xml
@@ -117,10 +117,8 @@
     <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Prijenos datoteke"</string>
     <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Ulazni uređaj"</string>
     <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Pristup internetu"</string>
-    <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
-    <skip />
-    <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
-    <skip />
+    <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Dijeljenje kontakata i povijesti poziva"</string>
+    <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Upotreba za dijeljenje kontakata i povijesti poziva"</string>
     <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Dijeljenje internetske veze"</string>
     <string name="bluetooth_profile_map" msgid="8907204701162107271">"SMS-ovi"</string>
     <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Pristup SIM-u"</string>
@@ -662,6 +660,8 @@
     <string name="dream_complication_title_weather" msgid="598609151677172783">"Vremenska prognoza"</string>
     <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Kvaliteta zraka"</string>
     <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Inform. o emitiranju"</string>
+    <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+    <skip />
     <string name="avatar_picker_title" msgid="8492884172713170652">"Odabir profilne slike"</string>
     <string name="default_user_icon_description" msgid="6554047177298972638">"Ikona zadanog korisnika"</string>
     <string name="physical_keyboard_title" msgid="4811935435315835220">"Fizička tipkovnica"</string>
diff --git a/packages/SettingsLib/res/values-hu/arrays.xml b/packages/SettingsLib/res/values-hu/arrays.xml
index 230e554..a5f37ea 100644
--- a/packages/SettingsLib/res/values-hu/arrays.xml
+++ b/packages/SettingsLib/res/values-hu/arrays.xml
@@ -85,22 +85,10 @@
     <item msgid="7073042887003102964">"map13"</item>
     <item msgid="8147982633566548515">"map14"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="2494959071796102843">"Rendszerérték (alapértelmezett)"</item>
-    <item msgid="4055460186095649420">"SBC"</item>
-    <item msgid="720249083677397051">"AAC"</item>
-    <item msgid="1049450003868150455">"Hang: <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
-    <item msgid="2908219194098827570">"Hang: <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
-    <item msgid="3825367753087348007">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="8868109554557331312">"Rendszerérték (alapértelmezett)"</item>
-    <item msgid="9024885861221697796">"SBC"</item>
-    <item msgid="4688890470703790013">"AAC"</item>
-    <item msgid="8627333814413492563">"Hang: <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
-    <item msgid="3517061573669307965">"Hang: <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
-    <item msgid="2553206901068987657">"LDAC"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
     <item msgid="926809261293414607">"Rendszerérték (alapértelmezett)"</item>
     <item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml
index 2454d1c..69ddad8 100644
--- a/packages/SettingsLib/res/values-hu/strings.xml
+++ b/packages/SettingsLib/res/values-hu/strings.xml
@@ -117,10 +117,8 @@
     <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Fájlátvitel"</string>
     <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Beviteli eszköz"</string>
     <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Internetelérés"</string>
-    <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
-    <skip />
-    <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
-    <skip />
+    <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Névjegyek és hívásnapló megosztása"</string>
+    <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Névjegyek és hívásnapló megosztásához használható"</string>
     <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Internetkapcsolat megosztása"</string>
     <string name="bluetooth_profile_map" msgid="8907204701162107271">"Szöveges üzenetek"</string>
     <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM-elérés"</string>
@@ -662,6 +660,8 @@
     <string name="dream_complication_title_weather" msgid="598609151677172783">"Időjárás"</string>
     <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Levegőminőség"</string>
     <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Átküldési információ"</string>
+    <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+    <skip />
     <string name="avatar_picker_title" msgid="8492884172713170652">"Profilkép választása"</string>
     <string name="default_user_icon_description" msgid="6554047177298972638">"Alapértelmezett felhasználó ikonja"</string>
     <string name="physical_keyboard_title" msgid="4811935435315835220">"Fizikai billentyűzet"</string>
diff --git a/packages/SettingsLib/res/values-hy/arrays.xml b/packages/SettingsLib/res/values-hy/arrays.xml
index 2a9c544..50d7c7f 100644
--- a/packages/SettingsLib/res/values-hy/arrays.xml
+++ b/packages/SettingsLib/res/values-hy/arrays.xml
@@ -85,22 +85,10 @@
     <item msgid="7073042887003102964">"map13"</item>
     <item msgid="8147982633566548515">"map14"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="2494959071796102843">"Օգտագործել համակարգի կարգավորումը (կանխադրված)"</item>
-    <item msgid="4055460186095649420">"SBC"</item>
-    <item msgid="720249083677397051">"AAC"</item>
-    <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> աուդիո"</item>
-    <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> աուդիո"</item>
-    <item msgid="3825367753087348007">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="8868109554557331312">"Օգտագործել համակարգի կարգավորումը (կանխադրված)"</item>
-    <item msgid="9024885861221697796">"SBC"</item>
-    <item msgid="4688890470703790013">"AAC"</item>
-    <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> աուդիո"</item>
-    <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> աուդիո"</item>
-    <item msgid="2553206901068987657">"LDAC"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
     <item msgid="926809261293414607">"Օգտագործել համակարգի կարգավորումը (կանխադրված)"</item>
     <item msgid="8003118270854840095">"44,1 կՀց"</item>
diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml
index 8966551..d8efbd3 100644
--- a/packages/SettingsLib/res/values-hy/strings.xml
+++ b/packages/SettingsLib/res/values-hy/strings.xml
@@ -117,10 +117,8 @@
     <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Ֆայլերի փոխանցում"</string>
     <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Ներմուծման սարք"</string>
     <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Ինտերնետի հասանելիություն"</string>
-    <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
-    <skip />
-    <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
-    <skip />
+    <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Կիսվել կոնտակտներով/զանգերի պատմությամբ"</string>
+    <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Օգտագործել՝ կոնտակտներով/զանգերի պատմությամբ կիսվելու համար"</string>
     <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Ինտերնետ կապի տարածում"</string>
     <string name="bluetooth_profile_map" msgid="8907204701162107271">"SMS հաղորդագրություններ"</string>
     <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM քարտի հասանելիություն"</string>
@@ -662,6 +660,8 @@
     <string name="dream_complication_title_weather" msgid="598609151677172783">"Եղանակ"</string>
     <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Օդի որակը"</string>
     <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Հեռարձակման տվյալներ"</string>
+    <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+    <skip />
     <string name="avatar_picker_title" msgid="8492884172713170652">"Պրոֆիլի նկար ընտրեք"</string>
     <string name="default_user_icon_description" msgid="6554047177298972638">"Օգտատիրոջ կանխադրված պատկերակ"</string>
     <string name="physical_keyboard_title" msgid="4811935435315835220">"Ֆիզիկական ստեղնաշար"</string>
diff --git a/packages/SettingsLib/res/values-in/arrays.xml b/packages/SettingsLib/res/values-in/arrays.xml
index 6349e53..5b0ad98 100644
--- a/packages/SettingsLib/res/values-in/arrays.xml
+++ b/packages/SettingsLib/res/values-in/arrays.xml
@@ -85,22 +85,10 @@
     <item msgid="7073042887003102964">"map13"</item>
     <item msgid="8147982633566548515">"map14"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="2494959071796102843">"Gunakan Pilihan Sistem (Default)"</item>
-    <item msgid="4055460186095649420">"SBC"</item>
-    <item msgid="720249083677397051">"AAC"</item>
-    <item msgid="1049450003868150455">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
-    <item msgid="2908219194098827570">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
-    <item msgid="3825367753087348007">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="8868109554557331312">"Gunakan Pilihan Sistem (Default)"</item>
-    <item msgid="9024885861221697796">"SBC"</item>
-    <item msgid="4688890470703790013">"AAC"</item>
-    <item msgid="8627333814413492563">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
-    <item msgid="3517061573669307965">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
-    <item msgid="2553206901068987657">"LDAC"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
     <item msgid="926809261293414607">"Gunakan Pilihan Sistem (Default)"</item>
     <item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml
index c525317..029ad8b 100644
--- a/packages/SettingsLib/res/values-in/strings.xml
+++ b/packages/SettingsLib/res/values-in/strings.xml
@@ -117,10 +117,8 @@
     <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Transfer file"</string>
     <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Perangkat masukan"</string>
     <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Akses Internet"</string>
-    <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
-    <skip />
-    <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
-    <skip />
+    <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Berbagi kontak dan histori panggilan"</string>
+    <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Gunakan untuk berbagi kontak dan histori panggilan"</string>
     <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Berbagi koneksi internet"</string>
     <string name="bluetooth_profile_map" msgid="8907204701162107271">"SMS"</string>
     <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Akses SIM"</string>
@@ -662,6 +660,8 @@
     <string name="dream_complication_title_weather" msgid="598609151677172783">"Cuaca"</string>
     <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Kualitas Udara"</string>
     <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Info Transmisi"</string>
+    <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+    <skip />
     <string name="avatar_picker_title" msgid="8492884172713170652">"Pilih foto profil"</string>
     <string name="default_user_icon_description" msgid="6554047177298972638">"Ikon pengguna default"</string>
     <string name="physical_keyboard_title" msgid="4811935435315835220">"Keyboard fisik"</string>
diff --git a/packages/SettingsLib/res/values-is/arrays.xml b/packages/SettingsLib/res/values-is/arrays.xml
index 730fcaf..9d481f8 100644
--- a/packages/SettingsLib/res/values-is/arrays.xml
+++ b/packages/SettingsLib/res/values-is/arrays.xml
@@ -85,22 +85,10 @@
     <item msgid="7073042887003102964">"map13"</item>
     <item msgid="8147982633566548515">"map14"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="2494959071796102843">"Nota val kerfisins (sjálfgefið)"</item>
-    <item msgid="4055460186095649420">"SBC"</item>
-    <item msgid="720249083677397051">"AAC"</item>
-    <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> hljóð"</item>
-    <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> hljóð"</item>
-    <item msgid="3825367753087348007">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="8868109554557331312">"Nota val kerfisins (sjálfgefið)"</item>
-    <item msgid="9024885861221697796">"SBC"</item>
-    <item msgid="4688890470703790013">"AAC"</item>
-    <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> hljóð"</item>
-    <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> hljóð"</item>
-    <item msgid="2553206901068987657">"LDAC"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
     <item msgid="926809261293414607">"Nota val kerfisins (sjálfgefið)"</item>
     <item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml
index c06d2f0..ccf06b0 100644
--- a/packages/SettingsLib/res/values-is/strings.xml
+++ b/packages/SettingsLib/res/values-is/strings.xml
@@ -117,10 +117,8 @@
     <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Skráaflutningur"</string>
     <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Inntakstæki"</string>
     <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Internetaðgangur"</string>
-    <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
-    <skip />
-    <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
-    <skip />
+    <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Tengiliðir, SMS-skilaboð og símtalaferill"</string>
+    <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Nota til að deila tengiliðum og símtalaferli"</string>
     <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Deiling nettengingar"</string>
     <string name="bluetooth_profile_map" msgid="8907204701162107271">"Textaskilaboð"</string>
     <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Aðgangur að SIM-korti"</string>
@@ -662,6 +660,8 @@
     <string name="dream_complication_title_weather" msgid="598609151677172783">"Veður"</string>
     <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Loftgæði"</string>
     <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Útsendingaruppl."</string>
+    <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+    <skip />
     <string name="avatar_picker_title" msgid="8492884172713170652">"Veldu prófílmynd"</string>
     <string name="default_user_icon_description" msgid="6554047177298972638">"Tákn sjálfgefins notanda"</string>
     <string name="physical_keyboard_title" msgid="4811935435315835220">"Vélbúnaðarlyklaborð"</string>
diff --git a/packages/SettingsLib/res/values-it/arrays.xml b/packages/SettingsLib/res/values-it/arrays.xml
index b7e114f..62450da3 100644
--- a/packages/SettingsLib/res/values-it/arrays.xml
+++ b/packages/SettingsLib/res/values-it/arrays.xml
@@ -85,22 +85,10 @@
     <item msgid="7073042887003102964">"map13"</item>
     <item msgid="8147982633566548515">"map14"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="2494959071796102843">"Usa selezione di sistema (predefinita)"</item>
-    <item msgid="4055460186095649420">"SBC"</item>
-    <item msgid="720249083677397051">"AAC"</item>
-    <item msgid="1049450003868150455">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
-    <item msgid="2908219194098827570">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
-    <item msgid="3825367753087348007">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="8868109554557331312">"Usa selezione di sistema (predefinita)"</item>
-    <item msgid="9024885861221697796">"SBC"</item>
-    <item msgid="4688890470703790013">"AAC"</item>
-    <item msgid="8627333814413492563">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
-    <item msgid="3517061573669307965">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
-    <item msgid="2553206901068987657">"LDAC"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
     <item msgid="926809261293414607">"Usa selezione di sistema (predefinita)"</item>
     <item msgid="8003118270854840095">"44.1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml
index b3ac25a..3811152 100644
--- a/packages/SettingsLib/res/values-it/strings.xml
+++ b/packages/SettingsLib/res/values-it/strings.xml
@@ -117,10 +117,8 @@
     <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Trasferimento file"</string>
     <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Dispositivo di input"</string>
     <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Accesso a Internet"</string>
-    <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
-    <skip />
-    <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
-    <skip />
+    <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Condivis. contatti e cronologia chiamate"</string>
+    <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Usa per condivisione di contatti e cronologia chiamate"</string>
     <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Condivisione connessione Internet"</string>
     <string name="bluetooth_profile_map" msgid="8907204701162107271">"SMS"</string>
     <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Accesso alla SIM"</string>
@@ -662,6 +660,8 @@
     <string name="dream_complication_title_weather" msgid="598609151677172783">"Meteo"</string>
     <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Qualità dell\'aria"</string>
     <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Info sul cast"</string>
+    <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+    <skip />
     <string name="avatar_picker_title" msgid="8492884172713170652">"Scegli un\'immagine del profilo"</string>
     <string name="default_user_icon_description" msgid="6554047177298972638">"Icona dell\'utente predefinito"</string>
     <string name="physical_keyboard_title" msgid="4811935435315835220">"Tastiera fisica"</string>
diff --git a/packages/SettingsLib/res/values-iw/arrays.xml b/packages/SettingsLib/res/values-iw/arrays.xml
index 587016f..49f3fcf 100644
--- a/packages/SettingsLib/res/values-iw/arrays.xml
+++ b/packages/SettingsLib/res/values-iw/arrays.xml
@@ -85,22 +85,10 @@
     <item msgid="7073042887003102964">"map13"</item>
     <item msgid="8147982633566548515">"map14"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="2494959071796102843">"שימוש בבחירת המערכת (ברירת המחדל)"</item>
-    <item msgid="4055460186095649420">"SBC"</item>
-    <item msgid="720249083677397051">"AAC"</item>
-    <item msgid="1049450003868150455">"אודיו <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
-    <item msgid="2908219194098827570">"אודיו <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
-    <item msgid="3825367753087348007">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="8868109554557331312">"שימוש בבחירת המערכת (ברירת המחדל)"</item>
-    <item msgid="9024885861221697796">"SBC"</item>
-    <item msgid="4688890470703790013">"AAC"</item>
-    <item msgid="8627333814413492563">"אודיו <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
-    <item msgid="3517061573669307965">"אודיו <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
-    <item msgid="2553206901068987657">"LDAC"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
     <item msgid="926809261293414607">"שימוש בבחירת המערכת (ברירת המחדל)"</item>
     <item msgid="8003118270854840095">"44.1 קילו-הרץ"</item>
diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml
index f021b37..3421fb5 100644
--- a/packages/SettingsLib/res/values-iw/strings.xml
+++ b/packages/SettingsLib/res/values-iw/strings.xml
@@ -117,10 +117,8 @@
     <string name="bluetooth_profile_opp" msgid="6692618568149493430">"העברת קבצים"</string>
     <string name="bluetooth_profile_hid" msgid="2969922922664315866">"מכשיר קלט"</string>
     <string name="bluetooth_profile_pan" msgid="1006235139308318188">"גישה לאינטרנט"</string>
-    <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
-    <skip />
-    <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
-    <skip />
+    <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"שיתוף אנשי הקשר והיסטוריית השיחות"</string>
+    <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"ההגדרה משמשת לשיתוף של אנשי הקשר והיסטוריית השיחות"</string>
     <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"שיתוף חיבור לאינטרנט"</string>
     <string name="bluetooth_profile_map" msgid="8907204701162107271">"הודעות טקסט"</string>
     <string name="bluetooth_profile_sap" msgid="8304170950447934386">"‏גישה ל-SIM"</string>
@@ -662,6 +660,7 @@
     <string name="dream_complication_title_weather" msgid="598609151677172783">"מזג אוויר"</string>
     <string name="dream_complication_title_aqi" msgid="4587552608957834110">"איכות האוויר"</string>
     <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"פרטי ההעברה"</string>
+    <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"בית חכם"</string>
     <string name="avatar_picker_title" msgid="8492884172713170652">"בחירה של תמונת פרופיל"</string>
     <string name="default_user_icon_description" msgid="6554047177298972638">"סמל המשתמש שמוגדר כברירת מחדל"</string>
     <string name="physical_keyboard_title" msgid="4811935435315835220">"מקלדת פיזית"</string>
diff --git a/packages/SettingsLib/res/values-ja/arrays.xml b/packages/SettingsLib/res/values-ja/arrays.xml
index ad84d9e..d73cc43 100644
--- a/packages/SettingsLib/res/values-ja/arrays.xml
+++ b/packages/SettingsLib/res/values-ja/arrays.xml
@@ -85,22 +85,10 @@
     <item msgid="7073042887003102964">"map13"</item>
     <item msgid="8147982633566548515">"map14"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="2494959071796102843">"システムの選択(デフォルト)を使用"</item>
-    <item msgid="4055460186095649420">"SBC"</item>
-    <item msgid="720249083677397051">"AAC"</item>
-    <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> オーディオ"</item>
-    <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> オーディオ"</item>
-    <item msgid="3825367753087348007">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="8868109554557331312">"システムの選択(デフォルト)を使用"</item>
-    <item msgid="9024885861221697796">"SBC"</item>
-    <item msgid="4688890470703790013">"AAC"</item>
-    <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> オーディオ"</item>
-    <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> オーディオ"</item>
-    <item msgid="2553206901068987657">"LDAC"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
     <item msgid="926809261293414607">"システムの選択(デフォルト)を使用"</item>
     <item msgid="8003118270854840095">"44.1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml
index 2994e4d..e7d2cf8 100644
--- a/packages/SettingsLib/res/values-ja/strings.xml
+++ b/packages/SettingsLib/res/values-ja/strings.xml
@@ -117,10 +117,8 @@
     <string name="bluetooth_profile_opp" msgid="6692618568149493430">"ファイル転送"</string>
     <string name="bluetooth_profile_hid" msgid="2969922922664315866">"入力デバイス"</string>
     <string name="bluetooth_profile_pan" msgid="1006235139308318188">"インターネットアクセス"</string>
-    <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
-    <skip />
-    <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
-    <skip />
+    <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"連絡先と通話履歴の共有"</string>
+    <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"連絡先と通話履歴の共有に使用します"</string>
     <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"インターネット接続の共有"</string>
     <string name="bluetooth_profile_map" msgid="8907204701162107271">"テキスト メッセージ"</string>
     <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIMアクセス"</string>
@@ -662,6 +660,8 @@
     <string name="dream_complication_title_weather" msgid="598609151677172783">"天気"</string>
     <string name="dream_complication_title_aqi" msgid="4587552608957834110">"大気質"</string>
     <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"キャスト情報"</string>
+    <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+    <skip />
     <string name="avatar_picker_title" msgid="8492884172713170652">"プロフィール写真の選択"</string>
     <string name="default_user_icon_description" msgid="6554047177298972638">"デフォルト ユーザー アイコン"</string>
     <string name="physical_keyboard_title" msgid="4811935435315835220">"物理キーボード"</string>
diff --git a/packages/SettingsLib/res/values-ka/arrays.xml b/packages/SettingsLib/res/values-ka/arrays.xml
index 01f1dcc7..c0d6f97 100644
--- a/packages/SettingsLib/res/values-ka/arrays.xml
+++ b/packages/SettingsLib/res/values-ka/arrays.xml
@@ -85,22 +85,10 @@
     <item msgid="7073042887003102964">"map13"</item>
     <item msgid="8147982633566548515">"map14"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="2494959071796102843">"სისტემის არჩეულის გამოყენება (ნაგულისხმევი)"</item>
-    <item msgid="4055460186095649420">"SBC"</item>
-    <item msgid="720249083677397051">"AAC"</item>
-    <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> აუდიო"</item>
-    <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> აუდიო"</item>
-    <item msgid="3825367753087348007">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="8868109554557331312">"სისტემის არჩეულის გამოყენება (ნაგულისხმევი)"</item>
-    <item msgid="9024885861221697796">"SBC"</item>
-    <item msgid="4688890470703790013">"AAC"</item>
-    <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> აუდიო"</item>
-    <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> აუდიო"</item>
-    <item msgid="2553206901068987657">"LDAC"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
     <item msgid="926809261293414607">"სისტემის არჩეულის გამოყენება (ნაგულისხმევი)"</item>
     <item msgid="8003118270854840095">"44,1 კჰც"</item>
diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml
index 0c3277b..09f5471 100644
--- a/packages/SettingsLib/res/values-ka/strings.xml
+++ b/packages/SettingsLib/res/values-ka/strings.xml
@@ -117,10 +117,8 @@
     <string name="bluetooth_profile_opp" msgid="6692618568149493430">"ფაილების გადაცემა"</string>
     <string name="bluetooth_profile_hid" msgid="2969922922664315866">"შეყვანის მოწყობილობა"</string>
     <string name="bluetooth_profile_pan" msgid="1006235139308318188">"ინტერნეტზე წვდომა"</string>
-    <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
-    <skip />
-    <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
-    <skip />
+    <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"კონტაქტ. და საუბრის ისტორიის გაზიარება"</string>
+    <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"გამოიყენეთ კონტაქტებისა და საუბრის ისტორიის გასაზიარებლად"</string>
     <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"ინტერნეტ კავშირის გაზიარება"</string>
     <string name="bluetooth_profile_map" msgid="8907204701162107271">"ტექსტური შეტყობინებები"</string>
     <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM წვდომა"</string>
@@ -662,6 +660,7 @@
     <string name="dream_complication_title_weather" msgid="598609151677172783">"ამინდი"</string>
     <string name="dream_complication_title_aqi" msgid="4587552608957834110">"ჰაერის ხარისხი"</string>
     <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"ტრანსლირების ინფო"</string>
+    <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"სახლის მართვა"</string>
     <string name="avatar_picker_title" msgid="8492884172713170652">"აირჩიეთ პროფილის სურათი"</string>
     <string name="default_user_icon_description" msgid="6554047177298972638">"მომხმარებლის ნაგულისხმევი ხატულა"</string>
     <string name="physical_keyboard_title" msgid="4811935435315835220">"ფიზიკური კლავიატურა"</string>
diff --git a/packages/SettingsLib/res/values-kk/arrays.xml b/packages/SettingsLib/res/values-kk/arrays.xml
index 33fd25b..fc998e7 100644
--- a/packages/SettingsLib/res/values-kk/arrays.xml
+++ b/packages/SettingsLib/res/values-kk/arrays.xml
@@ -85,22 +85,10 @@
     <item msgid="7073042887003102964">"map13"</item>
     <item msgid="8147982633566548515">"map14"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="2494959071796102843">"Жүйенің таңдағанын алу (әдепкі)"</item>
-    <item msgid="4055460186095649420">"SBC"</item>
-    <item msgid="720249083677397051">"AAC"</item>
-    <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> аудиокодегі"</item>
-    <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> аудиокодегі"</item>
-    <item msgid="3825367753087348007">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="8868109554557331312">"Жүйенің таңдағанын алу (әдепкі)"</item>
-    <item msgid="9024885861221697796">"SBC"</item>
-    <item msgid="4688890470703790013">"AAC"</item>
-    <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> аудиокодегі"</item>
-    <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> аудиокодегі"</item>
-    <item msgid="2553206901068987657">"LDAC"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
     <item msgid="926809261293414607">"Жүйенің таңдағанын алу (әдепкі)"</item>
     <item msgid="8003118270854840095">"44,1 кГц"</item>
diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml
index 63c9575..3281303 100644
--- a/packages/SettingsLib/res/values-kk/strings.xml
+++ b/packages/SettingsLib/res/values-kk/strings.xml
@@ -117,10 +117,8 @@
     <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Файл жіберу"</string>
     <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Кіріс құрылғысы"</string>
     <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Интернетке қосылу"</string>
-    <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
-    <skip />
-    <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
-    <skip />
+    <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Контактілер мен қоңыраулар тарихын бөлісу"</string>
+    <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Контактілер мен қоңыраулар тарихын бөлісу үшін пайдалану"</string>
     <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Интернет байланысын ортақ қолдану"</string>
     <string name="bluetooth_profile_map" msgid="8907204701162107271">"Мәтіндік хабарлар"</string>
     <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM картасына кіру"</string>
@@ -662,6 +660,8 @@
     <string name="dream_complication_title_weather" msgid="598609151677172783">"Ауа райы"</string>
     <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Ауа сапасы"</string>
     <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Трансляция ақпараты"</string>
+    <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+    <skip />
     <string name="avatar_picker_title" msgid="8492884172713170652">"Профиль суретін таңдау"</string>
     <string name="default_user_icon_description" msgid="6554047177298972638">"Әдепкі пайдаланушы белгішесі"</string>
     <string name="physical_keyboard_title" msgid="4811935435315835220">"Пернетақта"</string>
diff --git a/packages/SettingsLib/res/values-km/arrays.xml b/packages/SettingsLib/res/values-km/arrays.xml
index ef3aa52..a005f4d 100644
--- a/packages/SettingsLib/res/values-km/arrays.xml
+++ b/packages/SettingsLib/res/values-km/arrays.xml
@@ -85,22 +85,10 @@
     <item msgid="7073042887003102964">"map13"</item>
     <item msgid="8147982633566548515">"map14"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="2494959071796102843">"ប្រើ​ការ​ជ្រើសរើស​ប្រព័ន្ធ (លំនាំ​ដើម)"</item>
-    <item msgid="4055460186095649420">"SBC"</item>
-    <item msgid="720249083677397051">"AAC"</item>
-    <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g>សំឡេង <xliff:g id="APTX">aptX™</xliff:g>"</item>
-    <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g>សំឡេង <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
-    <item msgid="3825367753087348007">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="8868109554557331312">"ប្រើ​ការ​ជ្រើសរើស​ប្រព័ន្ធ (លំនាំ​ដើម)"</item>
-    <item msgid="9024885861221697796">"SBC"</item>
-    <item msgid="4688890470703790013">"AAC"</item>
-    <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g>សំឡេង <xliff:g id="APTX">aptX™</xliff:g>"</item>
-    <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g>សំឡេង <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
-    <item msgid="2553206901068987657">"LDAC"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
     <item msgid="926809261293414607">"ប្រើ​ការ​ជ្រើសរើស​ប្រព័ន្ធ (លំនាំ​ដើម)"</item>
     <item msgid="8003118270854840095">"44.1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml
index 9b9228a..87c1aa2 100644
--- a/packages/SettingsLib/res/values-km/strings.xml
+++ b/packages/SettingsLib/res/values-km/strings.xml
@@ -117,10 +117,8 @@
     <string name="bluetooth_profile_opp" msgid="6692618568149493430">"ផ្ទេរ​ឯកសារ"</string>
     <string name="bluetooth_profile_hid" msgid="2969922922664315866">"ឧបករណ៍​បញ្ចូល"</string>
     <string name="bluetooth_profile_pan" msgid="1006235139308318188">"ការចូលប្រើ​អ៊ីនធឺណិត"</string>
-    <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
-    <skip />
-    <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
-    <skip />
+    <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"ការចែករំលែកទំនាក់ទំនង និងប្រវត្តិហៅទូរសព្ទ"</string>
+    <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"ការប្រើសម្រាប់ការចែករំលែកទំនាក់ទំនង និងប្រវត្តិហៅទូរសព្ទ"</string>
     <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"ចែករំលែក​ការ​តភ្ជាប់​អ៊ីនធឺណិត"</string>
     <string name="bluetooth_profile_map" msgid="8907204701162107271">"សារ​ជាអក្សរ"</string>
     <string name="bluetooth_profile_sap" msgid="8304170950447934386">"ការចូលដំណើរការស៊ីម"</string>
@@ -662,6 +660,8 @@
     <string name="dream_complication_title_weather" msgid="598609151677172783">"អាកាសធាតុ"</string>
     <string name="dream_complication_title_aqi" msgid="4587552608957834110">"គុណភាព​ខ្យល់"</string>
     <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"ព័ត៌មានអំពីការបញ្ជូន"</string>
+    <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+    <skip />
     <string name="avatar_picker_title" msgid="8492884172713170652">"ជ្រើសរើស​រូបភាព​កម្រង​ព័ត៌មាន"</string>
     <string name="default_user_icon_description" msgid="6554047177298972638">"រូបអ្នកប្រើប្រាស់លំនាំដើម"</string>
     <string name="physical_keyboard_title" msgid="4811935435315835220">"ក្ដារចុច​រូបវន្ត"</string>
diff --git a/packages/SettingsLib/res/values-kn/arrays.xml b/packages/SettingsLib/res/values-kn/arrays.xml
index 6623564..b6014ce 100644
--- a/packages/SettingsLib/res/values-kn/arrays.xml
+++ b/packages/SettingsLib/res/values-kn/arrays.xml
@@ -85,22 +85,10 @@
     <item msgid="7073042887003102964">"map13"</item>
     <item msgid="8147982633566548515">"map14"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="2494959071796102843">"ಸಿಸ್ಟಂ ಆಯ್ಕೆಯನ್ನು ಬಳಸಿ (ಡಿಫಾಲ್ಟ್)"</item>
-    <item msgid="4055460186095649420">"SBC"</item>
-    <item msgid="720249083677397051">"AAC"</item>
-    <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> ಆಡಿಯೋ"</item>
-    <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> ಆಡಿಯೋ"</item>
-    <item msgid="3825367753087348007">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="8868109554557331312">"ಸಿಸ್ಟಂ ಆಯ್ಕೆಯನ್ನು ಬಳಸಿ (ಡಿಫಾಲ್ಟ್)"</item>
-    <item msgid="9024885861221697796">"SBC"</item>
-    <item msgid="4688890470703790013">"AAC"</item>
-    <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> ಆಡಿಯೋ"</item>
-    <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> ಆಡಿಯೋ"</item>
-    <item msgid="2553206901068987657">"LDAC"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
     <item msgid="926809261293414607">"ಸಿಸ್ಟಂ ಆಯ್ಕೆಯನ್ನು ಬಳಸಿ (ಡಿಫಾಲ್ಟ್)"</item>
     <item msgid="8003118270854840095">"44.1 kHz"</item>
@@ -184,7 +172,7 @@
     <item msgid="7300881231043255746">"ಕೆರ್ನಲ್ ಮಾತ್ರ"</item>
   </string-array>
   <string-array name="select_logpersist_summaries">
-    <item msgid="97587758561106269">"ಆಫ್ ಆಗಿದೆ"</item>
+    <item msgid="97587758561106269">"ಆಫ್"</item>
     <item msgid="7126170197336963369">"ಎಲ್ಲಾ ಲಾಗ್ ಬಫರ್‌ಗಳು"</item>
     <item msgid="7167543126036181392">"ಎಲ್ಲಾ ಆದರೆ ರೇಡಿಯೊ ಲಾಗ್ ಬಫರ್‌ಗಳು"</item>
     <item msgid="5135340178556563979">"ಕೆರ್ನಲ್ ಲಾಗ್ ಬಫರ್ ಮಾತ್ರ"</item>
diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml
index 7071598..ab437e8 100644
--- a/packages/SettingsLib/res/values-kn/strings.xml
+++ b/packages/SettingsLib/res/values-kn/strings.xml
@@ -117,10 +117,8 @@
     <string name="bluetooth_profile_opp" msgid="6692618568149493430">"ಫೈಲ್ ವರ್ಗಾವಣೆ"</string>
     <string name="bluetooth_profile_hid" msgid="2969922922664315866">"ಇನ್‌ಪುಟ್‌ ಸಾಧನ"</string>
     <string name="bluetooth_profile_pan" msgid="1006235139308318188">"ಇಂಟರ್ನೆಟ್ ಪ್ರವೇಶ"</string>
-    <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
-    <skip />
-    <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
-    <skip />
+    <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"ಸಂಪರ್ಕಗಳು ಹಾಗೂ ಕರೆ ಇತಿಹಾಸ ಹಂಚಿಕೊಳ್ಳುವಿಕೆ"</string>
+    <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"ಸಂಪರ್ಕಗಳು ಮತ್ತು ಕರೆ ಇತಿಹಾಸ ಹಂಚಿಕೆಗಾಗಿ ಬಳಸಿ"</string>
     <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"ಇಂಟರ್ನೆಟ್ ಸಂಪರ್ಕ ಹಂಚಿಕೊಳ್ಳುವಿಕೆ"</string>
     <string name="bluetooth_profile_map" msgid="8907204701162107271">"ಪಠ್ಯ ಸಂದೇಶಗಳು"</string>
     <string name="bluetooth_profile_sap" msgid="8304170950447934386">"ಸಿಮ್ ಪ್ರವೇಶ"</string>
@@ -662,6 +660,8 @@
     <string name="dream_complication_title_weather" msgid="598609151677172783">"ಹವಾಮಾನ"</string>
     <string name="dream_complication_title_aqi" msgid="4587552608957834110">"ವಾಯು ಗುಣಮಟ್ಟ"</string>
     <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"ಬಿತ್ತರಿಸಿದ ಮಾಹಿತಿ"</string>
+    <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+    <skip />
     <string name="avatar_picker_title" msgid="8492884172713170652">"ಪ್ರೊಫೈಲ್ ಚಿತ್ರವನ್ನು ಆಯ್ಕೆ ಮಾಡಿ"</string>
     <string name="default_user_icon_description" msgid="6554047177298972638">"ಡೀಫಾಲ್ಟ್ ಬಳಕೆದಾರರ ಐಕಾನ್"</string>
     <string name="physical_keyboard_title" msgid="4811935435315835220">"ಭೌತಿಕ ಕೀಬೋರ್ಡ್"</string>
diff --git a/packages/SettingsLib/res/values-ko/arrays.xml b/packages/SettingsLib/res/values-ko/arrays.xml
index a207706..7138113 100644
--- a/packages/SettingsLib/res/values-ko/arrays.xml
+++ b/packages/SettingsLib/res/values-ko/arrays.xml
@@ -85,22 +85,10 @@
     <item msgid="7073042887003102964">"map13"</item>
     <item msgid="8147982633566548515">"map14"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="2494959071796102843">"시스템 설정 사용(기본)"</item>
-    <item msgid="4055460186095649420">"SBC"</item>
-    <item msgid="720249083677397051">"AAC"</item>
-    <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> 오디오"</item>
-    <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> 오디오"</item>
-    <item msgid="3825367753087348007">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="8868109554557331312">"시스템 설정 사용(기본)"</item>
-    <item msgid="9024885861221697796">"SBC"</item>
-    <item msgid="4688890470703790013">"AAC"</item>
-    <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> 오디오"</item>
-    <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> 오디오"</item>
-    <item msgid="2553206901068987657">"LDAC"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
     <item msgid="926809261293414607">"시스템 설정 사용(기본)"</item>
     <item msgid="8003118270854840095">"44.1kHz"</item>
diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml
index 6ba289d..2b8ab75 100644
--- a/packages/SettingsLib/res/values-ko/strings.xml
+++ b/packages/SettingsLib/res/values-ko/strings.xml
@@ -117,10 +117,8 @@
     <string name="bluetooth_profile_opp" msgid="6692618568149493430">"파일 전송"</string>
     <string name="bluetooth_profile_hid" msgid="2969922922664315866">"입력 장치"</string>
     <string name="bluetooth_profile_pan" msgid="1006235139308318188">"인터넷 액세스"</string>
-    <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
-    <skip />
-    <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
-    <skip />
+    <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"연락처 및 통화 기록 공유"</string>
+    <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"연락처 및 통화 기록 공유에 사용"</string>
     <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"인터넷 연결 공유"</string>
     <string name="bluetooth_profile_map" msgid="8907204701162107271">"문자 메시지"</string>
     <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM 액세스"</string>
@@ -662,6 +660,8 @@
     <string name="dream_complication_title_weather" msgid="598609151677172783">"날씨"</string>
     <string name="dream_complication_title_aqi" msgid="4587552608957834110">"대기 상태"</string>
     <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"전송 정보"</string>
+    <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+    <skip />
     <string name="avatar_picker_title" msgid="8492884172713170652">"프로필 사진 선택하기"</string>
     <string name="default_user_icon_description" msgid="6554047177298972638">"기본 사용자 아이콘"</string>
     <string name="physical_keyboard_title" msgid="4811935435315835220">"물리적 키보드"</string>
diff --git a/packages/SettingsLib/res/values-ky/arrays.xml b/packages/SettingsLib/res/values-ky/arrays.xml
index 67cdc7c..40271f7 100644
--- a/packages/SettingsLib/res/values-ky/arrays.xml
+++ b/packages/SettingsLib/res/values-ky/arrays.xml
@@ -85,22 +85,10 @@
     <item msgid="7073042887003102964">"карта13"</item>
     <item msgid="8147982633566548515">"карта14"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="2494959071796102843">"Система тандаганды колдонуу (демейки)"</item>
-    <item msgid="4055460186095649420">"SBC"</item>
-    <item msgid="720249083677397051">"AAC"</item>
-    <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> аудио"</item>
-    <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> аудио"</item>
-    <item msgid="3825367753087348007">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="8868109554557331312">"Система тандаганды колдонуу (демейки)"</item>
-    <item msgid="9024885861221697796">"SBC"</item>
-    <item msgid="4688890470703790013">"AAC"</item>
-    <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> аудио"</item>
-    <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> аудио"</item>
-    <item msgid="2553206901068987657">"LDAC"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
     <item msgid="926809261293414607">"Система тандаганды колдонуу (демейки)"</item>
     <item msgid="8003118270854840095">"44,1 кГц"</item>
diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml
index 3eb43fa..e993e3f 100644
--- a/packages/SettingsLib/res/values-ky/strings.xml
+++ b/packages/SettingsLib/res/values-ky/strings.xml
@@ -117,10 +117,8 @@
     <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Файл алмашуу"</string>
     <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Киргизүү түзмөгү"</string>
     <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Интернетке мүмкүнчүлүк алуу"</string>
-    <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
-    <skip />
-    <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
-    <skip />
+    <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Байланыштарды жана чалуу таржымалын бөлүшүү"</string>
+    <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Байланыштарды жана чалуу таржымалын бөлүшүү үчүн колдонуу"</string>
     <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Интернет байланышын бөлүшүү"</string>
     <string name="bluetooth_profile_map" msgid="8907204701162107271">"SMS билдирүүлөрү"</string>
     <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM картаны пайдалануу мүмкүнчүлүгү"</string>
@@ -662,6 +660,8 @@
     <string name="dream_complication_title_weather" msgid="598609151677172783">"Аба ырайы"</string>
     <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Абанын сапаты"</string>
     <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Тышкы экранга чыгаруу маалыматы"</string>
+    <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+    <skip />
     <string name="avatar_picker_title" msgid="8492884172713170652">"Профилдин сүрөтүн тандоо"</string>
     <string name="default_user_icon_description" msgid="6554047177298972638">"Демейки колдонуучунун сүрөтчөсү"</string>
     <string name="physical_keyboard_title" msgid="4811935435315835220">"Аппараттык баскычтоп"</string>
diff --git a/packages/SettingsLib/res/values-lo/arrays.xml b/packages/SettingsLib/res/values-lo/arrays.xml
index 56a9741..792ca39 100644
--- a/packages/SettingsLib/res/values-lo/arrays.xml
+++ b/packages/SettingsLib/res/values-lo/arrays.xml
@@ -85,22 +85,10 @@
     <item msgid="7073042887003102964">"map13"</item>
     <item msgid="8147982633566548515">"map14"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="2494959071796102843">"ໃຊ້ການເລືອກຂອງລະບົບ (ຄ່າເລີ່ມຕົ້ນ)"</item>
-    <item msgid="4055460186095649420">"SBC"</item>
-    <item msgid="720249083677397051">"AAC"</item>
-    <item msgid="1049450003868150455">"ສຽງ <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
-    <item msgid="2908219194098827570">"ສຽງ <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
-    <item msgid="3825367753087348007">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="8868109554557331312">"ໃຊ້ການເລືອກຂອງລະບົບ (ຄ່າເລີ່ມຕົ້ນ)"</item>
-    <item msgid="9024885861221697796">"SBC"</item>
-    <item msgid="4688890470703790013">"AAC"</item>
-    <item msgid="8627333814413492563">"ສຽງ <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
-    <item msgid="3517061573669307965">"ສຽງ <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
-    <item msgid="2553206901068987657">"LDAC"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
     <item msgid="926809261293414607">"ໃຊ້ການເລືອກຂອງລະບົບ (ຄ່າເລີ່ມຕົ້ນ)"</item>
     <item msgid="8003118270854840095">"44.1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml
index 2dcad1f..d295932 100644
--- a/packages/SettingsLib/res/values-lo/strings.xml
+++ b/packages/SettingsLib/res/values-lo/strings.xml
@@ -117,10 +117,8 @@
     <string name="bluetooth_profile_opp" msgid="6692618568149493430">"ການໂອນຍ້າຍໄຟລ໌"</string>
     <string name="bluetooth_profile_hid" msgid="2969922922664315866">"ອຸປະກອນປ້ອນຂໍ້ມູນ"</string>
     <string name="bluetooth_profile_pan" msgid="1006235139308318188">"ການເຂົ້າເຖິງອິນເຕີເນັດ"</string>
-    <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
-    <skip />
-    <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
-    <skip />
+    <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"ແບ່ງປັນລາຍຊື່ຜູ້ຕິດຕໍ່ ແລະ ປະຫວັດການໂທ"</string>
+    <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"ໃຊ້ສໍາລັບແບ່ງປັນລາຍຊື່ຜູ້ຕິດຕໍ່ ແລະ ປະຫວັດການໂທ"</string>
     <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"ການແບ່ງປັນການເຊື່ອມຕໍ່ອິນເຕີເນັດ"</string>
     <string name="bluetooth_profile_map" msgid="8907204701162107271">"ຂໍ້ຄວາມ"</string>
     <string name="bluetooth_profile_sap" msgid="8304170950447934386">"ການ​ເຂົ້າ​ເຖິງ SIM"</string>
@@ -662,6 +660,8 @@
     <string name="dream_complication_title_weather" msgid="598609151677172783">"ສະພາບອາກາດ"</string>
     <string name="dream_complication_title_aqi" msgid="4587552608957834110">"ຄຸນນະພາບ​ອາກາດ"</string>
     <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"ຂໍ້ມູນການສົ່ງສັນຍານ"</string>
+    <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+    <skip />
     <string name="avatar_picker_title" msgid="8492884172713170652">"ເລືອກຮູບໂປຣໄຟລ໌"</string>
     <string name="default_user_icon_description" msgid="6554047177298972638">"ໄອຄອນຜູ້ໃຊ້ເລີ່ມຕົ້ນ"</string>
     <string name="physical_keyboard_title" msgid="4811935435315835220">"ແປ້ນພິມພາຍນອກ"</string>
diff --git a/packages/SettingsLib/res/values-lt/arrays.xml b/packages/SettingsLib/res/values-lt/arrays.xml
index a7aba78..946f69c 100644
--- a/packages/SettingsLib/res/values-lt/arrays.xml
+++ b/packages/SettingsLib/res/values-lt/arrays.xml
@@ -85,22 +85,10 @@
     <item msgid="7073042887003102964">"map13"</item>
     <item msgid="8147982633566548515">"map14"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="2494959071796102843">"Naudoti sistemos pasirink. (numatytasis)"</item>
-    <item msgid="4055460186095649420">"SBC"</item>
-    <item msgid="720249083677397051">"AAC"</item>
-    <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> garsas"</item>
-    <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> garsas"</item>
-    <item msgid="3825367753087348007">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="8868109554557331312">"Naudoti sistemos pasirink. (numatytasis)"</item>
-    <item msgid="9024885861221697796">"SBC"</item>
-    <item msgid="4688890470703790013">"AAC"</item>
-    <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> garsas"</item>
-    <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> garsas"</item>
-    <item msgid="2553206901068987657">"LDAC"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
     <item msgid="926809261293414607">"Naudoti sistemos pasirink. (numatytasis)"</item>
     <item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml
index 8aaec47..e38889b 100644
--- a/packages/SettingsLib/res/values-lt/strings.xml
+++ b/packages/SettingsLib/res/values-lt/strings.xml
@@ -117,10 +117,8 @@
     <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Failo perkėlimas"</string>
     <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Įvesties įrenginys"</string>
     <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Prieiga prie interneto"</string>
-    <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
-    <skip />
-    <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
-    <skip />
+    <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Kontaktų ir skambučių istorijos bendrinimas"</string>
+    <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Naudoti kontaktams ir skambučių istorijai bendrinti"</string>
     <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Interneto ryšio bendrinimas"</string>
     <string name="bluetooth_profile_map" msgid="8907204701162107271">"Teksto pranešimai"</string>
     <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM prieiga"</string>
@@ -662,6 +660,7 @@
     <string name="dream_complication_title_weather" msgid="598609151677172783">"Orai"</string>
     <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Oro kokybė"</string>
     <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Perdav. informacija"</string>
+    <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Namų sist. valdikl."</string>
     <string name="avatar_picker_title" msgid="8492884172713170652">"Pasirinkite profilio nuotrauką"</string>
     <string name="default_user_icon_description" msgid="6554047177298972638">"Numatytojo naudotojo piktograma"</string>
     <string name="physical_keyboard_title" msgid="4811935435315835220">"Fizinė klaviatūra"</string>
diff --git a/packages/SettingsLib/res/values-lv/arrays.xml b/packages/SettingsLib/res/values-lv/arrays.xml
index 840d794..f4ae452 100644
--- a/packages/SettingsLib/res/values-lv/arrays.xml
+++ b/packages/SettingsLib/res/values-lv/arrays.xml
@@ -85,22 +85,10 @@
     <item msgid="7073042887003102964">"map13"</item>
     <item msgid="8147982633566548515">"map14"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="2494959071796102843">"Sistēmas atlases izmantošana (nokl.)"</item>
-    <item msgid="4055460186095649420">"SBC"</item>
-    <item msgid="720249083677397051">"AAC"</item>
-    <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> audio"</item>
-    <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> audio"</item>
-    <item msgid="3825367753087348007">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="8868109554557331312">"Sistēmas atlases izmantošana (nokl.)"</item>
-    <item msgid="9024885861221697796">"SBC"</item>
-    <item msgid="4688890470703790013">"AAC"</item>
-    <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> audio"</item>
-    <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> audio"</item>
-    <item msgid="2553206901068987657">"LDAC"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
     <item msgid="926809261293414607">"Sistēmas atlases izmantošana (nokl.)"</item>
     <item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml
index fcfb9b89..d899ada 100644
--- a/packages/SettingsLib/res/values-lv/strings.xml
+++ b/packages/SettingsLib/res/values-lv/strings.xml
@@ -117,10 +117,8 @@
     <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Failu pārsūtīšana"</string>
     <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Ievades ierīce"</string>
     <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Interneta piekļuve"</string>
-    <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
-    <skip />
-    <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
-    <skip />
+    <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Kontaktpersonu un zvanu vēst. kopīgošana"</string>
+    <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Paredzēts kontaktpersonu un zvanu vēstures kopīgošanai"</string>
     <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Interneta savienojuma koplietošana"</string>
     <string name="bluetooth_profile_map" msgid="8907204701162107271">"Īsziņas"</string>
     <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Piekļuve SIM kartei"</string>
@@ -662,6 +660,8 @@
     <string name="dream_complication_title_weather" msgid="598609151677172783">"Laikapstākļi"</string>
     <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Gaisa kvalitāte"</string>
     <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Apraides informācija"</string>
+    <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+    <skip />
     <string name="avatar_picker_title" msgid="8492884172713170652">"Profila attēla izvēle"</string>
     <string name="default_user_icon_description" msgid="6554047177298972638">"Noklusējuma lietotāja ikona"</string>
     <string name="physical_keyboard_title" msgid="4811935435315835220">"Fiziskā tastatūra"</string>
diff --git a/packages/SettingsLib/res/values-mk/arrays.xml b/packages/SettingsLib/res/values-mk/arrays.xml
index 2f22165..9c46f21 100644
--- a/packages/SettingsLib/res/values-mk/arrays.xml
+++ b/packages/SettingsLib/res/values-mk/arrays.xml
@@ -85,22 +85,10 @@
     <item msgid="7073042887003102964">"map13"</item>
     <item msgid="8147982633566548515">"map14"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="2494959071796102843">"Користи избор на системот (стандардно)"</item>
-    <item msgid="4055460186095649420">"SBC"</item>
-    <item msgid="720249083677397051">"AAC"</item>
-    <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> аудио"</item>
-    <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> аудио"</item>
-    <item msgid="3825367753087348007">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="8868109554557331312">"Користи избор на системот (стандардно)"</item>
-    <item msgid="9024885861221697796">"SBC"</item>
-    <item msgid="4688890470703790013">"AAC"</item>
-    <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> аудио"</item>
-    <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> аудио"</item>
-    <item msgid="2553206901068987657">"LDAC"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
     <item msgid="926809261293414607">"Користи избор на системот (стандардно)"</item>
     <item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml
index 144de00..c8033e3 100644
--- a/packages/SettingsLib/res/values-mk/strings.xml
+++ b/packages/SettingsLib/res/values-mk/strings.xml
@@ -117,10 +117,8 @@
     <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Пренос на датотека"</string>
     <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Влезен уред"</string>
     <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Пристап до интернет"</string>
-    <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
-    <skip />
-    <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
-    <skip />
+    <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Споделување контакти и историја на повици"</string>
+    <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Користење на споделувањето контакти и историја на повици"</string>
     <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Споделување конекција на интернет"</string>
     <string name="bluetooth_profile_map" msgid="8907204701162107271">"Текстуални пораки"</string>
     <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Пристап до SIM"</string>
@@ -662,6 +660,7 @@
     <string name="dream_complication_title_weather" msgid="598609151677172783">"Временска прогноза"</string>
     <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Квалитет на воздух"</string>
     <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Инфо за улогите"</string>
+    <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Контроли за домот"</string>
     <string name="avatar_picker_title" msgid="8492884172713170652">"Изберете профилна слика"</string>
     <string name="default_user_icon_description" msgid="6554047177298972638">"Икона за стандарден корисник"</string>
     <string name="physical_keyboard_title" msgid="4811935435315835220">"Физичка тастатура"</string>
diff --git a/packages/SettingsLib/res/values-ml/arrays.xml b/packages/SettingsLib/res/values-ml/arrays.xml
index 3aa9472..4715e2a 100644
--- a/packages/SettingsLib/res/values-ml/arrays.xml
+++ b/packages/SettingsLib/res/values-ml/arrays.xml
@@ -85,22 +85,10 @@
     <item msgid="7073042887003102964">"map13"</item>
     <item msgid="8147982633566548515">"map14"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="2494959071796102843">"സിസ്റ്റം സെലക്ഷൻ ഉപയോഗിക്കൂ ‌(ഡിഫോൾട്ട്)"</item>
-    <item msgid="4055460186095649420">"SBC"</item>
-    <item msgid="720249083677397051">"AAC"</item>
-    <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> ഓഡിയോ"</item>
-    <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> ഓഡിയോ"</item>
-    <item msgid="3825367753087348007">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="8868109554557331312">"സിസ്റ്റം സെലക്ഷൻ ഉപയോഗിക്കൂ ‌(ഡിഫോൾട്ട്)"</item>
-    <item msgid="9024885861221697796">"SBC"</item>
-    <item msgid="4688890470703790013">"AAC"</item>
-    <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> ഓഡിയോ"</item>
-    <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> ഓഡിയോ"</item>
-    <item msgid="2553206901068987657">"LDAC"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
     <item msgid="926809261293414607">"സിസ്റ്റം സെലക്ഷൻ ഉപയോഗിക്കൂ ‌(ഡിഫോൾട്ട്)"</item>
     <item msgid="8003118270854840095">"44.1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml
index 59cd6bc..089f6af 100644
--- a/packages/SettingsLib/res/values-ml/strings.xml
+++ b/packages/SettingsLib/res/values-ml/strings.xml
@@ -117,10 +117,8 @@
     <string name="bluetooth_profile_opp" msgid="6692618568149493430">"ഫയൽ കൈമാറൽ"</string>
     <string name="bluetooth_profile_hid" msgid="2969922922664315866">"ഇൻപുട്ട് ഉപകരണം"</string>
     <string name="bluetooth_profile_pan" msgid="1006235139308318188">"ഇന്‍റർനെറ്റ് ആക്‌സസ്"</string>
-    <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
-    <skip />
-    <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
-    <skip />
+    <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"കോ‌ൺടാക്‌റ്റുകളും കോൾ ചരിത്രം പങ്കിടലും"</string>
+    <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"കോൺടാക്‌റ്റുകളുടെയും കോൾ ചരിത്രം പങ്കിടലിന്റെയും ഉപയോഗം"</string>
     <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"ഇന്റർനെറ്റ് കണക്ഷൻ പങ്കിടൽ"</string>
     <string name="bluetooth_profile_map" msgid="8907204701162107271">"അക്ഷര സന്ദേശങ്ങൾ"</string>
     <string name="bluetooth_profile_sap" msgid="8304170950447934386">"സിം ആക്സസ്"</string>
@@ -662,6 +660,7 @@
     <string name="dream_complication_title_weather" msgid="598609151677172783">"കാലാവസ്ഥ"</string>
     <string name="dream_complication_title_aqi" msgid="4587552608957834110">"വായു നിലവാരം"</string>
     <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"കാസ്റ്റ് വിവരങ്ങൾ"</string>
+    <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"ഹോം കൺട്രോളുകൾ"</string>
     <string name="avatar_picker_title" msgid="8492884172713170652">"പ്രൊഫൈൽ ചിത്രം തിരഞ്ഞെടുക്കുക"</string>
     <string name="default_user_icon_description" msgid="6554047177298972638">"ഡിഫോൾട്ട് ഉപയോക്തൃ ഐക്കൺ"</string>
     <string name="physical_keyboard_title" msgid="4811935435315835220">"ഫിസിക്കൽ കീബോർഡ്"</string>
diff --git a/packages/SettingsLib/res/values-mn/arrays.xml b/packages/SettingsLib/res/values-mn/arrays.xml
index 9bd10a78..63fa53b 100644
--- a/packages/SettingsLib/res/values-mn/arrays.xml
+++ b/packages/SettingsLib/res/values-mn/arrays.xml
@@ -85,22 +85,10 @@
     <item msgid="7073042887003102964">"map13"</item>
     <item msgid="8147982633566548515">"map14"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="2494959071796102843">"Системийн сонголтыг ашиглах (Өгөгдмөл)"</item>
-    <item msgid="4055460186095649420">"SBC"</item>
-    <item msgid="720249083677397051">"AAC"</item>
-    <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> аудио"</item>
-    <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> аудио"</item>
-    <item msgid="3825367753087348007">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="8868109554557331312">"Системийн сонголтыг ашиглах (Өгөгдмөл)"</item>
-    <item msgid="9024885861221697796">"SBC"</item>
-    <item msgid="4688890470703790013">"AAC"</item>
-    <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> аудио"</item>
-    <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> аудио"</item>
-    <item msgid="2553206901068987657">"LDAC"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
     <item msgid="926809261293414607">"Системийн сонголтыг ашиглах (Өгөгдмөл)"</item>
     <item msgid="8003118270854840095">"44.1 кГц"</item>
diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml
index 1bd6bea..cc35053 100644
--- a/packages/SettingsLib/res/values-mn/strings.xml
+++ b/packages/SettingsLib/res/values-mn/strings.xml
@@ -117,10 +117,8 @@
     <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Файл дамжуулалт"</string>
     <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Оруулах төхөөрөмж"</string>
     <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Интернэт хандалт"</string>
-    <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
-    <skip />
-    <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
-    <skip />
+    <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Харилцагчид ба дуудлагын түүх хуваалцах"</string>
+    <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Харилцагчид ба дуудлагын түүх хуваалцахад ашиглах"</string>
     <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Интернэт холболтыг хуваалцах"</string>
     <string name="bluetooth_profile_map" msgid="8907204701162107271">"Мессеж"</string>
     <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM Хандалт"</string>
@@ -662,6 +660,7 @@
     <string name="dream_complication_title_weather" msgid="598609151677172783">"Цаг агаар"</string>
     <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Агаарын чанар"</string>
     <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Дамжуулах мэдээлэл"</string>
+    <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Гэрийн удирдлага"</string>
     <string name="avatar_picker_title" msgid="8492884172713170652">"Профайл зураг сонгох"</string>
     <string name="default_user_icon_description" msgid="6554047177298972638">"Өгөгдмөл хэрэглэгчийн дүрс тэмдэг"</string>
     <string name="physical_keyboard_title" msgid="4811935435315835220">"Биет гар"</string>
diff --git a/packages/SettingsLib/res/values-mr/arrays.xml b/packages/SettingsLib/res/values-mr/arrays.xml
index 45bc5b8..a54f990 100644
--- a/packages/SettingsLib/res/values-mr/arrays.xml
+++ b/packages/SettingsLib/res/values-mr/arrays.xml
@@ -85,22 +85,10 @@
     <item msgid="7073042887003102964">"map13"</item>
     <item msgid="8147982633566548515">"map14"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="2494959071796102843">"सिस्टीम निवड वापरा (डीफॉल्ट)"</item>
-    <item msgid="4055460186095649420">"SBC"</item>
-    <item msgid="720249083677397051">"AAC"</item>
-    <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> ऑडिओ"</item>
-    <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> ऑडिओ"</item>
-    <item msgid="3825367753087348007">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="8868109554557331312">"सिस्टम निवड वापरा (डीफॉल्ट)"</item>
-    <item msgid="9024885861221697796">"SBC"</item>
-    <item msgid="4688890470703790013">"AAC"</item>
-    <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> ऑडिओ"</item>
-    <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> ऑडिओ"</item>
-    <item msgid="2553206901068987657">"LDAC"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
     <item msgid="926809261293414607">"सिस्टम निवड वापरा (डीफॉल्ट)"</item>
     <item msgid="8003118270854840095">"44.1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml
index c163219..1907d00 100644
--- a/packages/SettingsLib/res/values-mr/strings.xml
+++ b/packages/SettingsLib/res/values-mr/strings.xml
@@ -117,10 +117,8 @@
     <string name="bluetooth_profile_opp" msgid="6692618568149493430">"फाइल स्थानांतरण"</string>
     <string name="bluetooth_profile_hid" msgid="2969922922664315866">"इनपुट डिव्हाइस"</string>
     <string name="bluetooth_profile_pan" msgid="1006235139308318188">"इंटरनेट अ‍ॅक्सेस"</string>
-    <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
-    <skip />
-    <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
-    <skip />
+    <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"संपर्क आणि कॉल इतिहास शेअरिंग"</string>
+    <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"संपर्क आणि कॉल इतिहास शेअरिंगसाठी वापरा"</string>
     <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"इंटरनेट कनेक्शन शेअररण"</string>
     <string name="bluetooth_profile_map" msgid="8907204701162107271">"मजकूर मेसेज"</string>
     <string name="bluetooth_profile_sap" msgid="8304170950447934386">"सिम अ‍ॅक्सेस"</string>
@@ -662,6 +660,7 @@
     <string name="dream_complication_title_weather" msgid="598609151677172783">"हवामान"</string>
     <string name="dream_complication_title_aqi" msgid="4587552608957834110">"हवेची गुणवत्ता"</string>
     <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"कास्टसंबंधित माहिती"</string>
+    <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"होम कंट्रोल"</string>
     <string name="avatar_picker_title" msgid="8492884172713170652">"प्रोफाइल फोटो निवडा"</string>
     <string name="default_user_icon_description" msgid="6554047177298972638">"डीफॉल्ट वापरकर्ता आयकन"</string>
     <string name="physical_keyboard_title" msgid="4811935435315835220">"वास्तविक कीबोर्ड"</string>
diff --git a/packages/SettingsLib/res/values-ms/arrays.xml b/packages/SettingsLib/res/values-ms/arrays.xml
index d88ea76..b26ed23 100644
--- a/packages/SettingsLib/res/values-ms/arrays.xml
+++ b/packages/SettingsLib/res/values-ms/arrays.xml
@@ -85,22 +85,10 @@
     <item msgid="7073042887003102964">"map13"</item>
     <item msgid="8147982633566548515">"map14"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="2494959071796102843">"Gunakan Pilihan Sistem (Lalai)"</item>
-    <item msgid="4055460186095649420">"SBC"</item>
-    <item msgid="720249083677397051">"AAC"</item>
-    <item msgid="1049450003868150455">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
-    <item msgid="2908219194098827570">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
-    <item msgid="3825367753087348007">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="8868109554557331312">"Gunakan Pilihan Sistem (Lalai)"</item>
-    <item msgid="9024885861221697796">"SBC"</item>
-    <item msgid="4688890470703790013">"AAC"</item>
-    <item msgid="8627333814413492563">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
-    <item msgid="3517061573669307965">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
-    <item msgid="2553206901068987657">"LDAC"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
     <item msgid="926809261293414607">"Gunakan Pilihan Sistem (Lalai)"</item>
     <item msgid="8003118270854840095">"44.1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml
index 55c8814..b3929c9 100644
--- a/packages/SettingsLib/res/values-ms/strings.xml
+++ b/packages/SettingsLib/res/values-ms/strings.xml
@@ -117,10 +117,8 @@
     <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Pemindahan fail"</string>
     <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Peranti input"</string>
     <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Akses Internet"</string>
-    <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
-    <skip />
-    <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
-    <skip />
+    <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Perkongsian kenalan &amp; sejarah panggilan"</string>
+    <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Digunakan untuk perkongsian kenalan dan sejarah panggilan"</string>
     <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Perkongsian sambungan Internet"</string>
     <string name="bluetooth_profile_map" msgid="8907204701162107271">"Mesej Teks"</string>
     <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Akses SIM"</string>
@@ -662,6 +660,8 @@
     <string name="dream_complication_title_weather" msgid="598609151677172783">"Cuaca"</string>
     <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Kualiti Udara"</string>
     <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Maklumat Pelakon"</string>
+    <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+    <skip />
     <string name="avatar_picker_title" msgid="8492884172713170652">"Pilih gambar profil"</string>
     <string name="default_user_icon_description" msgid="6554047177298972638">"Ikon pengguna lalai"</string>
     <string name="physical_keyboard_title" msgid="4811935435315835220">"Papan kekunci fizikal"</string>
diff --git a/packages/SettingsLib/res/values-my/arrays.xml b/packages/SettingsLib/res/values-my/arrays.xml
index 857a6ae..ed95dfe 100644
--- a/packages/SettingsLib/res/values-my/arrays.xml
+++ b/packages/SettingsLib/res/values-my/arrays.xml
@@ -85,22 +85,10 @@
     <item msgid="7073042887003102964">"map13"</item>
     <item msgid="8147982633566548515">"map14"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="2494959071796102843">"စနစ်ရွေးချယ်မှုကို အသုံးပြုပါ (မူရင်း)"</item>
-    <item msgid="4055460186095649420">"SBC"</item>
-    <item msgid="720249083677397051">"AAC"</item>
-    <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> အသံ"</item>
-    <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> အသံ"</item>
-    <item msgid="3825367753087348007">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="8868109554557331312">"စနစ်ရွေးချယ်မှုကို အသုံးပြုပါ (မူရင်း)"</item>
-    <item msgid="9024885861221697796">"SBC"</item>
-    <item msgid="4688890470703790013">"AAC"</item>
-    <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> အသံ"</item>
-    <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> အသံ"</item>
-    <item msgid="2553206901068987657">"LDAC"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
     <item msgid="926809261293414607">"စနစ်ရွေးချယ်မှုကို အသုံးပြုပါ (မူရင်း)"</item>
     <item msgid="8003118270854840095">"၄၄.၁ kHz"</item>
diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml
index 38b1fe8..f7a33a9 100644
--- a/packages/SettingsLib/res/values-my/strings.xml
+++ b/packages/SettingsLib/res/values-my/strings.xml
@@ -117,10 +117,8 @@
     <string name="bluetooth_profile_opp" msgid="6692618568149493430">"ဖိုင်လွဲပြောင်းခြင်း"</string>
     <string name="bluetooth_profile_hid" msgid="2969922922664315866">"ထည့်သွင်းသော စက်"</string>
     <string name="bluetooth_profile_pan" msgid="1006235139308318188">"အင်တာနက်ချိတ်ဆက်ခြင်း"</string>
-    <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
-    <skip />
-    <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
-    <skip />
+    <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"အဆက်အသွယ်၊ ယခင်ခေါ်ဆိုမှုများ မျှဝေခြင်း"</string>
+    <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"အဆက်အသွယ်နှင့် ယခင်ခေါ်ဆိုမှုများ မျှဝေရန် သုံးသည်"</string>
     <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"အင်တာနက်ဆက်သွယ်မှု မျှဝေခြင်း"</string>
     <string name="bluetooth_profile_map" msgid="8907204701162107271">"မိုဘိုင်းမက်ဆေ့ဂျ်များ"</string>
     <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM အသုံးပြုခြင်း"</string>
@@ -662,6 +660,8 @@
     <string name="dream_complication_title_weather" msgid="598609151677172783">"မိုးလေဝသ"</string>
     <string name="dream_complication_title_aqi" msgid="4587552608957834110">"လေထုအရည်အသွေး"</string>
     <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"ကာစ် အချက်အလက်"</string>
+    <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+    <skip />
     <string name="avatar_picker_title" msgid="8492884172713170652">"ပရိုဖိုင်ပုံ ရွေးပါ"</string>
     <string name="default_user_icon_description" msgid="6554047177298972638">"မူရင်းအသုံးပြုသူ သင်္ကေတ"</string>
     <string name="physical_keyboard_title" msgid="4811935435315835220">"ပကတိ ကီးဘုတ်"</string>
diff --git a/packages/SettingsLib/res/values-nb/arrays.xml b/packages/SettingsLib/res/values-nb/arrays.xml
index 0f6e1fc..317c2db 100644
--- a/packages/SettingsLib/res/values-nb/arrays.xml
+++ b/packages/SettingsLib/res/values-nb/arrays.xml
@@ -85,22 +85,10 @@
     <item msgid="7073042887003102964">"map13"</item>
     <item msgid="8147982633566548515">"map14"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="2494959071796102843">"Bruk systemvalg (standard)"</item>
-    <item msgid="4055460186095649420">"SBC"</item>
-    <item msgid="720249083677397051">"AAC"</item>
-    <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>-lyd"</item>
-    <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>-lyd"</item>
-    <item msgid="3825367753087348007">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="8868109554557331312">"Bruk systemvalg (standard)"</item>
-    <item msgid="9024885861221697796">"SBC"</item>
-    <item msgid="4688890470703790013">"AAC"</item>
-    <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>-lyd"</item>
-    <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>-lyd"</item>
-    <item msgid="2553206901068987657">"LDAC"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
     <item msgid="926809261293414607">"Bruk systemvalg (standard)"</item>
     <item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml
index ef2111d..9b99631 100644
--- a/packages/SettingsLib/res/values-nb/strings.xml
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -117,10 +117,8 @@
     <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Filoverføring"</string>
     <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Inndataenhet"</string>
     <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Internett-tilgang"</string>
-    <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
-    <skip />
-    <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
-    <skip />
+    <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Deling av kontakter og anropslogg"</string>
+    <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Bruk for deling av kontakter og anropslogg"</string>
     <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Deling av internettilkobling"</string>
     <string name="bluetooth_profile_map" msgid="8907204701162107271">"Tekstmeldinger"</string>
     <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Tilgang til SIM-kortet"</string>
@@ -662,6 +660,8 @@
     <string name="dream_complication_title_weather" msgid="598609151677172783">"Vær"</string>
     <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Luftkvalitet"</string>
     <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Castinformasjon"</string>
+    <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+    <skip />
     <string name="avatar_picker_title" msgid="8492884172713170652">"Velg et profilbilde"</string>
     <string name="default_user_icon_description" msgid="6554047177298972638">"Standard brukerikon"</string>
     <string name="physical_keyboard_title" msgid="4811935435315835220">"Fysisk tastatur"</string>
diff --git a/packages/SettingsLib/res/values-ne/arrays.xml b/packages/SettingsLib/res/values-ne/arrays.xml
index 611066e..b3c3ee2 100644
--- a/packages/SettingsLib/res/values-ne/arrays.xml
+++ b/packages/SettingsLib/res/values-ne/arrays.xml
@@ -85,22 +85,10 @@
     <item msgid="7073042887003102964">"map13"</item>
     <item msgid="8147982633566548515">"map14"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="2494959071796102843">"सिस्टमको छनौट प्रयोग गरियोस् (डिफल्ट)"</item>
-    <item msgid="4055460186095649420">"SBC"</item>
-    <item msgid="720249083677397051">"AAC"</item>
-    <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> अडियो"</item>
-    <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> अडियो"</item>
-    <item msgid="3825367753087348007">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="8868109554557331312">"सिस्टमको छनौट प्रयोग गरियोस् (डिफल्ट)"</item>
-    <item msgid="9024885861221697796">"SBC"</item>
-    <item msgid="4688890470703790013">"AAC"</item>
-    <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> अडियो"</item>
-    <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> अडियो"</item>
-    <item msgid="2553206901068987657">"LDAC"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
     <item msgid="926809261293414607">"सिस्टमको छनौट प्रयोग गरियोस् (डिफल्ट)"</item>
     <item msgid="8003118270854840095">"४४.१ kHz"</item>
diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml
index 9fa7749..1534cba 100644
--- a/packages/SettingsLib/res/values-ne/strings.xml
+++ b/packages/SettingsLib/res/values-ne/strings.xml
@@ -117,10 +117,8 @@
     <string name="bluetooth_profile_opp" msgid="6692618568149493430">"फाइल स्थानान्तरण"</string>
     <string name="bluetooth_profile_hid" msgid="2969922922664315866">"इनपुट उपकरण"</string>
     <string name="bluetooth_profile_pan" msgid="1006235139308318188">"इन्टरनेट पहुँच"</string>
-    <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
-    <skip />
-    <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
-    <skip />
+    <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"कन्ट्याक्ट र कलको इतिहास सेयर गर्ने"</string>
+    <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"कन्ट्याक्ट र कलको इतिहास सेयर गर्न प्रयोग गरियोस्"</string>
     <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"इन्टरनेट जडान साझेदारी गर्दै"</string>
     <string name="bluetooth_profile_map" msgid="8907204701162107271">"टेक्स्ट म्यासेजहरू"</string>
     <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM पहुँच"</string>
@@ -662,6 +660,8 @@
     <string name="dream_complication_title_weather" msgid="598609151677172783">"मौसम"</string>
     <string name="dream_complication_title_aqi" msgid="4587552608957834110">"वायुको गुणस्तर"</string>
     <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"कास्टसम्बन्धी जानकारी"</string>
+    <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+    <skip />
     <string name="avatar_picker_title" msgid="8492884172713170652">"प्रोफाइल फोटो छान्नुहोस्"</string>
     <string name="default_user_icon_description" msgid="6554047177298972638">"प्रयोगकर्ताको डिफल्ट आइकन"</string>
     <string name="physical_keyboard_title" msgid="4811935435315835220">"भौतिक किबोर्ड"</string>
diff --git a/packages/SettingsLib/res/values-nl/arrays.xml b/packages/SettingsLib/res/values-nl/arrays.xml
index e0690df..e809452 100644
--- a/packages/SettingsLib/res/values-nl/arrays.xml
+++ b/packages/SettingsLib/res/values-nl/arrays.xml
@@ -85,22 +85,10 @@
     <item msgid="7073042887003102964">"map13"</item>
     <item msgid="8147982633566548515">"map14"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="2494959071796102843">"Systeemselectie gebruiken (standaard)"</item>
-    <item msgid="4055460186095649420">"SBC"</item>
-    <item msgid="720249083677397051">"AAC"</item>
-    <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> audio"</item>
-    <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> audio"</item>
-    <item msgid="3825367753087348007">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="8868109554557331312">"Gebruik systeemselectie (standaard)"</item>
-    <item msgid="9024885861221697796">"SBC"</item>
-    <item msgid="4688890470703790013">"AAC"</item>
-    <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> audio"</item>
-    <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> audio"</item>
-    <item msgid="2553206901068987657">"LDAC"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
     <item msgid="926809261293414607">"Systeemselectie gebruiken (standaard)"</item>
     <item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml
index f4cbfd5..ba11b46 100644
--- a/packages/SettingsLib/res/values-nl/strings.xml
+++ b/packages/SettingsLib/res/values-nl/strings.xml
@@ -117,10 +117,8 @@
     <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Bestandsoverdracht"</string>
     <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Invoerapparaat"</string>
     <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Internettoegang"</string>
-    <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
-    <skip />
-    <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
-    <skip />
+    <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Contacten en gespreksgeschiedenis delen"</string>
+    <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Gebruiken om contacten en gespreksgeschiedenis te delen"</string>
     <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Internetverbinding delen"</string>
     <string name="bluetooth_profile_map" msgid="8907204701162107271">"Sms-berichten"</string>
     <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Sim-toegang"</string>
@@ -662,6 +660,7 @@
     <string name="dream_complication_title_weather" msgid="598609151677172783">"Weer"</string>
     <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Luchtkwaliteit"</string>
     <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Castinformatie"</string>
+    <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Bediening voor in huis"</string>
     <string name="avatar_picker_title" msgid="8492884172713170652">"Kies een profielfoto"</string>
     <string name="default_user_icon_description" msgid="6554047177298972638">"Standaard gebruikersicoon"</string>
     <string name="physical_keyboard_title" msgid="4811935435315835220">"Fysiek toetsenbord"</string>
diff --git a/packages/SettingsLib/res/values-or/arrays.xml b/packages/SettingsLib/res/values-or/arrays.xml
index dd25b3e..d42aeaf 100644
--- a/packages/SettingsLib/res/values-or/arrays.xml
+++ b/packages/SettingsLib/res/values-or/arrays.xml
@@ -85,22 +85,10 @@
     <item msgid="7073042887003102964">"map13"</item>
     <item msgid="8147982633566548515">"map14"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="2494959071796102843">"ସିଷ୍ଟମ୍ ଚୟନ ବ୍ୟବହାର କରନ୍ତୁ (ଡିଫଲ୍ଟ)"</item>
-    <item msgid="4055460186095649420">"SBC"</item>
-    <item msgid="720249083677397051">"AAC"</item>
-    <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> ଅଡିଓ"</item>
-    <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> ଅଡିଓ"</item>
-    <item msgid="3825367753087348007">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="8868109554557331312">"ସିଷ୍ଟମ୍‌ର ଚୟନ (ଡିଫଲ୍ଟ୍) ବ୍ୟବହାର କରନ୍ତୁ"</item>
-    <item msgid="9024885861221697796">"SBC"</item>
-    <item msgid="4688890470703790013">"AAC"</item>
-    <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> ଅଡିଓ"</item>
-    <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> ଅଡିଓ"</item>
-    <item msgid="2553206901068987657">"LDAC"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
     <item msgid="926809261293414607">"ସିଷ୍ଟମ୍‌ର ଚୟନ (ଡିଫଲ୍ଟ୍) ବ୍ୟବହାର କରନ୍ତୁ"</item>
     <item msgid="8003118270854840095">"44.1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml
index 76c3671..09fa59d 100644
--- a/packages/SettingsLib/res/values-or/strings.xml
+++ b/packages/SettingsLib/res/values-or/strings.xml
@@ -117,10 +117,8 @@
     <string name="bluetooth_profile_opp" msgid="6692618568149493430">"ଫାଇଲ୍‌ ଟ୍ରାନ୍ସଫର୍‌"</string>
     <string name="bluetooth_profile_hid" msgid="2969922922664315866">"ଇନ୍‌ପୁଟ୍‌ ଡିଭାଇସ୍"</string>
     <string name="bluetooth_profile_pan" msgid="1006235139308318188">"ଇଣ୍ଟର୍‌ନେଟ୍‌ ଆକ୍ସେସ୍"</string>
-    <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
-    <skip />
-    <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
-    <skip />
+    <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"କଣ୍ଟାକ୍ଟ ଏବଂ କଲ ଇତିହାସ ସେୟାରିଂ"</string>
+    <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"କଣ୍ଟାକ୍ଟ ଏବଂ କଲ ଇତିହାସ ସେୟାରିଂ ପାଇଁ ବ୍ୟବହାର କରନ୍ତୁ"</string>
     <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"ଇଣ୍ଟର୍‌ନେଟ୍‌ ସଂଯୋଗ ଶେୟାରିଙ୍ଗ"</string>
     <string name="bluetooth_profile_map" msgid="8907204701162107271">"ଟେକ୍ସଟ୍ ମେସେଜ୍"</string>
     <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM ଆକ୍ସେସ୍‌"</string>
@@ -662,6 +660,8 @@
     <string name="dream_complication_title_weather" msgid="598609151677172783">"ପାଣିପାଗ"</string>
     <string name="dream_complication_title_aqi" msgid="4587552608957834110">"ବାୟୁର ଗୁଣବତ୍ତା"</string>
     <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"କାଷ୍ଟ ସୂଚନା"</string>
+    <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+    <skip />
     <string name="avatar_picker_title" msgid="8492884172713170652">"ଏକ ପ୍ରୋଫାଇଲ ଛବି ବାଛନ୍ତୁ"</string>
     <string name="default_user_icon_description" msgid="6554047177298972638">"ଡିଫଲ୍ଟ ଉପଯୋଗକର୍ତ୍ତା ଆଇକନ"</string>
     <string name="physical_keyboard_title" msgid="4811935435315835220">"ଫିଜିକାଲ କୀବୋର୍ଡ"</string>
diff --git a/packages/SettingsLib/res/values-pa/arrays.xml b/packages/SettingsLib/res/values-pa/arrays.xml
index 6ba2145..a3fae22 100644
--- a/packages/SettingsLib/res/values-pa/arrays.xml
+++ b/packages/SettingsLib/res/values-pa/arrays.xml
@@ -85,22 +85,10 @@
     <item msgid="7073042887003102964">"map13"</item>
     <item msgid="8147982633566548515">"map14"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="2494959071796102843">"ਸਿਸਟਮ ਚੋਣ ਦੀ ਵਰਤੋਂ ਕਰੋ (ਪੂਰਵ-ਨਿਰਧਾਰਿਤ)"</item>
-    <item msgid="4055460186095649420">"SBC"</item>
-    <item msgid="720249083677397051">"AAC"</item>
-    <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> ਆਡੀਓ"</item>
-    <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> ਆਡੀਓ"</item>
-    <item msgid="3825367753087348007">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="8868109554557331312">"ਸਿਸਟਮ ਚੋਣ ਦੀ ਵਰਤੋਂ ਕਰੋ (ਪੂਰਵ-ਨਿਰਧਾਰਿਤ)"</item>
-    <item msgid="9024885861221697796">"SBC"</item>
-    <item msgid="4688890470703790013">"AAC"</item>
-    <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> ਆਡੀਓ"</item>
-    <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> ਆਡੀਓ"</item>
-    <item msgid="2553206901068987657">"LDAC"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
     <item msgid="926809261293414607">"ਸਿਸਟਮ ਚੋਣ ਦੀ ਵਰਤੋਂ ਕਰੋ (ਪੂਰਵ-ਨਿਰਧਾਰਿਤ)"</item>
     <item msgid="8003118270854840095">"44.1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml
index e34ece8..9df0fef 100644
--- a/packages/SettingsLib/res/values-pa/strings.xml
+++ b/packages/SettingsLib/res/values-pa/strings.xml
@@ -117,10 +117,8 @@
     <string name="bluetooth_profile_opp" msgid="6692618568149493430">"ਫਾਈਲ ਟ੍ਰਾਂਸਫਰ"</string>
     <string name="bluetooth_profile_hid" msgid="2969922922664315866">"ਇਨਪੁੱਟ ਡੀਵਾਈਸ"</string>
     <string name="bluetooth_profile_pan" msgid="1006235139308318188">"ਇੰਟਰਨੈੱਟ ਪਹੁੰਚ"</string>
-    <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
-    <skip />
-    <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
-    <skip />
+    <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"ਸੰਪਰਕ ਅਤੇ ਕਾਲ ਇਤਿਹਾਸ ਸਾਂਝਾ ਕਰਨਾ"</string>
+    <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"ਸੰਪਰਕ ਅਤੇ ਕਾਲ ਇਤਿਹਾਸ ਸਾਂਝਾ ਕਰਨ ਲਈ ਵਰਤੋਂ ਕਰੋ"</string>
     <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"ਇੰਟਰਨੈੱਟ ਕਨੈਕਸ਼ਨ ਸਾਂਝਾਕਰਨ"</string>
     <string name="bluetooth_profile_map" msgid="8907204701162107271">"ਲਿਖਤ ਸੁਨੇਹੇ"</string>
     <string name="bluetooth_profile_sap" msgid="8304170950447934386">"ਸਿਮ ਪਹੁੰਚ"</string>
@@ -662,6 +660,8 @@
     <string name="dream_complication_title_weather" msgid="598609151677172783">"ਮੌਸਮ"</string>
     <string name="dream_complication_title_aqi" msgid="4587552608957834110">"ਹਵਾ ਦੀ ਕੁਆਲਿਟੀ"</string>
     <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"ਕਾਸਟ ਸੰਬੰਧੀ ਜਾਣਕਾਰੀ"</string>
+    <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+    <skip />
     <string name="avatar_picker_title" msgid="8492884172713170652">"ਕੋਈ ਪ੍ਰੋਫਾਈਲ ਤਸਵੀਰ ਚੁਣੋ"</string>
     <string name="default_user_icon_description" msgid="6554047177298972638">"ਪੂਰਵ-ਨਿਰਧਾਰਿਤ ਵਰਤੋਂਕਾਰ ਪ੍ਰਤੀਕ"</string>
     <string name="physical_keyboard_title" msgid="4811935435315835220">"ਭੌਤਿਕ ਕੀ-ਬੋਰਡ"</string>
diff --git a/packages/SettingsLib/res/values-pl/arrays.xml b/packages/SettingsLib/res/values-pl/arrays.xml
index 5f91d35..f0453d1 100644
--- a/packages/SettingsLib/res/values-pl/arrays.xml
+++ b/packages/SettingsLib/res/values-pl/arrays.xml
@@ -85,22 +85,10 @@
     <item msgid="7073042887003102964">"map13"</item>
     <item msgid="8147982633566548515">"map14"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="2494959071796102843">"Używaj wyboru systemu (domyślnie)"</item>
-    <item msgid="4055460186095649420">"SBC"</item>
-    <item msgid="720249083677397051">"AAC"</item>
-    <item msgid="1049450003868150455">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
-    <item msgid="2908219194098827570">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
-    <item msgid="3825367753087348007">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="8868109554557331312">"Używaj wyboru systemu (domyślnie)"</item>
-    <item msgid="9024885861221697796">"SBC"</item>
-    <item msgid="4688890470703790013">"AAC"</item>
-    <item msgid="8627333814413492563">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
-    <item msgid="3517061573669307965">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
-    <item msgid="2553206901068987657">"LDAC"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
     <item msgid="926809261293414607">"Używaj wyboru systemu (domyślnie)"</item>
     <item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml
index fe56c61..f4599fd 100644
--- a/packages/SettingsLib/res/values-pl/strings.xml
+++ b/packages/SettingsLib/res/values-pl/strings.xml
@@ -117,10 +117,8 @@
     <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Przesyłanie pliku"</string>
     <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Urządzenie wejściowe"</string>
     <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Dostęp do internetu"</string>
-    <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
-    <skip />
-    <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
-    <skip />
+    <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Udostępnianie kontaktów i historii połączeń"</string>
+    <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Używaj w przypadku udostępniania kontaktów i historii połączeń"</string>
     <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Udostępnianie połączenia internetowego"</string>
     <string name="bluetooth_profile_map" msgid="8907204701162107271">"SMS-y"</string>
     <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Dostęp do karty SIM"</string>
@@ -228,7 +226,7 @@
     <string name="category_personal" msgid="6236798763159385225">"Osobiste"</string>
     <string name="category_work" msgid="4014193632325996115">"Służbowe"</string>
     <string name="development_settings_title" msgid="140296922921597393">"Opcje programisty"</string>
-    <string name="development_settings_enable" msgid="4285094651288242183">"Włącz opcje dla programistów"</string>
+    <string name="development_settings_enable" msgid="4285094651288242183">"Włącz opcje programisty"</string>
     <string name="development_settings_summary" msgid="8718917813868735095">"Ustaw opcje związane z programowaniem aplikacji."</string>
     <string name="development_settings_not_available" msgid="355070198089140951">"Opcje programisty są niedostępne dla tego użytkownika"</string>
     <string name="vpn_settings_not_available" msgid="2894137119965668920">"Ustawienia VPN są niedostępne dla tego użytkownika"</string>
@@ -662,6 +660,8 @@
     <string name="dream_complication_title_weather" msgid="598609151677172783">"Pogoda"</string>
     <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Jakość powietrza"</string>
     <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Obsada"</string>
+    <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+    <skip />
     <string name="avatar_picker_title" msgid="8492884172713170652">"Wybierz zdjęcie profilowe"</string>
     <string name="default_user_icon_description" msgid="6554047177298972638">"Ikona domyślnego użytkownika"</string>
     <string name="physical_keyboard_title" msgid="4811935435315835220">"Klawiatura fizyczna"</string>
diff --git a/packages/SettingsLib/res/values-pt-rBR/arrays.xml b/packages/SettingsLib/res/values-pt-rBR/arrays.xml
index c5a847f..1883ef3 100644
--- a/packages/SettingsLib/res/values-pt-rBR/arrays.xml
+++ b/packages/SettingsLib/res/values-pt-rBR/arrays.xml
@@ -85,22 +85,10 @@
     <item msgid="7073042887003102964">"map13"</item>
     <item msgid="8147982633566548515">"map14"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="2494959071796102843">"Usar seleção do sistema (padrão)"</item>
-    <item msgid="4055460186095649420">"SBC"</item>
-    <item msgid="720249083677397051">"AAC"</item>
-    <item msgid="1049450003868150455">"Áudio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
-    <item msgid="2908219194098827570">"Áudio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
-    <item msgid="3825367753087348007">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="8868109554557331312">"Usar seleção do sistema (padrão)"</item>
-    <item msgid="9024885861221697796">"SBC"</item>
-    <item msgid="4688890470703790013">"AAC"</item>
-    <item msgid="8627333814413492563">"Áudio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
-    <item msgid="3517061573669307965">"Áudio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
-    <item msgid="2553206901068987657">"LDAC"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
     <item msgid="926809261293414607">"Usar seleção do sistema (padrão)"</item>
     <item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml
index aa66c6043..12507c5 100644
--- a/packages/SettingsLib/res/values-pt-rBR/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml
@@ -117,10 +117,8 @@
     <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Transferência de arquivo"</string>
     <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Dispositivo de entrada"</string>
     <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Acesso à Internet"</string>
-    <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
-    <skip />
-    <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
-    <skip />
+    <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Compartilhar contatos e histórico de chamadas"</string>
+    <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Use para compartilhar contatos e o histórico de chamadas"</string>
     <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Compartilhamento de conexão à Internet"</string>
     <string name="bluetooth_profile_map" msgid="8907204701162107271">"Mensagens de texto"</string>
     <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Acesso ao chip"</string>
@@ -662,6 +660,8 @@
     <string name="dream_complication_title_weather" msgid="598609151677172783">"Clima"</string>
     <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Qualidade ­do ar"</string>
     <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Info. de transmissão"</string>
+    <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+    <skip />
     <string name="avatar_picker_title" msgid="8492884172713170652">"Escolher a foto do perfil"</string>
     <string name="default_user_icon_description" msgid="6554047177298972638">"Ícone de usuário padrão"</string>
     <string name="physical_keyboard_title" msgid="4811935435315835220">"Teclado físico"</string>
diff --git a/packages/SettingsLib/res/values-pt-rPT/arrays.xml b/packages/SettingsLib/res/values-pt-rPT/arrays.xml
index 3e7ee05..985bd51 100644
--- a/packages/SettingsLib/res/values-pt-rPT/arrays.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/arrays.xml
@@ -85,22 +85,10 @@
     <item msgid="7073042887003102964">"map13"</item>
     <item msgid="8147982633566548515">"map14"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="2494959071796102843">"Utilizar seleção do sistema (predefinido)"</item>
-    <item msgid="4055460186095649420">"SBC"</item>
-    <item msgid="720249083677397051">"AAC"</item>
-    <item msgid="1049450003868150455">"Áudio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
-    <item msgid="2908219194098827570">"Áudio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
-    <item msgid="3825367753087348007">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="8868109554557331312">"Utilizar seleção do sistema (predefinido)"</item>
-    <item msgid="9024885861221697796">"SBC"</item>
-    <item msgid="4688890470703790013">"AAC"</item>
-    <item msgid="8627333814413492563">"Áudio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
-    <item msgid="3517061573669307965">"Áudio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
-    <item msgid="2553206901068987657">"LDAC"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
     <item msgid="926809261293414607">"Utilizar seleção do sistema (predefinido)"</item>
     <item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml
index cf76780..4988136 100644
--- a/packages/SettingsLib/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml
@@ -117,10 +117,8 @@
     <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Transferência do ficheiro"</string>
     <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Dispositivo de entrada"</string>
     <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Acesso à internet"</string>
-    <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
-    <skip />
-    <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
-    <skip />
+    <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Part. histórico de chamadas e contactos"</string>
+    <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Usar para partilha do histórico de chamadas e dos contactos"</string>
     <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Partilha da ligação à internet"</string>
     <string name="bluetooth_profile_map" msgid="8907204701162107271">"Mensagens de texto"</string>
     <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Acesso ao SIM"</string>
@@ -662,6 +660,7 @@
     <string name="dream_complication_title_weather" msgid="598609151677172783">"Meteorologia"</string>
     <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Qualidade do ar"</string>
     <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Info. de transmissão"</string>
+    <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Ctr. domésticos"</string>
     <string name="avatar_picker_title" msgid="8492884172713170652">"Escolha uma imagem do perfil"</string>
     <string name="default_user_icon_description" msgid="6554047177298972638">"Ícone do utilizador predefinido"</string>
     <string name="physical_keyboard_title" msgid="4811935435315835220">"Teclado físico"</string>
diff --git a/packages/SettingsLib/res/values-pt/arrays.xml b/packages/SettingsLib/res/values-pt/arrays.xml
index c5a847f..1883ef3 100644
--- a/packages/SettingsLib/res/values-pt/arrays.xml
+++ b/packages/SettingsLib/res/values-pt/arrays.xml
@@ -85,22 +85,10 @@
     <item msgid="7073042887003102964">"map13"</item>
     <item msgid="8147982633566548515">"map14"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="2494959071796102843">"Usar seleção do sistema (padrão)"</item>
-    <item msgid="4055460186095649420">"SBC"</item>
-    <item msgid="720249083677397051">"AAC"</item>
-    <item msgid="1049450003868150455">"Áudio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
-    <item msgid="2908219194098827570">"Áudio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
-    <item msgid="3825367753087348007">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="8868109554557331312">"Usar seleção do sistema (padrão)"</item>
-    <item msgid="9024885861221697796">"SBC"</item>
-    <item msgid="4688890470703790013">"AAC"</item>
-    <item msgid="8627333814413492563">"Áudio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
-    <item msgid="3517061573669307965">"Áudio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
-    <item msgid="2553206901068987657">"LDAC"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
     <item msgid="926809261293414607">"Usar seleção do sistema (padrão)"</item>
     <item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml
index aa66c6043..12507c5 100644
--- a/packages/SettingsLib/res/values-pt/strings.xml
+++ b/packages/SettingsLib/res/values-pt/strings.xml
@@ -117,10 +117,8 @@
     <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Transferência de arquivo"</string>
     <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Dispositivo de entrada"</string>
     <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Acesso à Internet"</string>
-    <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
-    <skip />
-    <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
-    <skip />
+    <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Compartilhar contatos e histórico de chamadas"</string>
+    <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Use para compartilhar contatos e o histórico de chamadas"</string>
     <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Compartilhamento de conexão à Internet"</string>
     <string name="bluetooth_profile_map" msgid="8907204701162107271">"Mensagens de texto"</string>
     <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Acesso ao chip"</string>
@@ -662,6 +660,8 @@
     <string name="dream_complication_title_weather" msgid="598609151677172783">"Clima"</string>
     <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Qualidade ­do ar"</string>
     <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Info. de transmissão"</string>
+    <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+    <skip />
     <string name="avatar_picker_title" msgid="8492884172713170652">"Escolher a foto do perfil"</string>
     <string name="default_user_icon_description" msgid="6554047177298972638">"Ícone de usuário padrão"</string>
     <string name="physical_keyboard_title" msgid="4811935435315835220">"Teclado físico"</string>
diff --git a/packages/SettingsLib/res/values-ro/arrays.xml b/packages/SettingsLib/res/values-ro/arrays.xml
index 454867e..f1e9986 100644
--- a/packages/SettingsLib/res/values-ro/arrays.xml
+++ b/packages/SettingsLib/res/values-ro/arrays.xml
@@ -85,22 +85,10 @@
     <item msgid="7073042887003102964">"map13"</item>
     <item msgid="8147982633566548515">"map14"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="2494959071796102843">"Folosiți selectarea sistemului (prestabilit)"</item>
-    <item msgid="4055460186095649420">"SBC"</item>
-    <item msgid="720249083677397051">"AAC"</item>
-    <item msgid="1049450003868150455">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
-    <item msgid="2908219194098827570">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
-    <item msgid="3825367753087348007">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="8868109554557331312">"Folosiți selectarea sistemului (prestabilit)"</item>
-    <item msgid="9024885861221697796">"SBC"</item>
-    <item msgid="4688890470703790013">"AAC"</item>
-    <item msgid="8627333814413492563">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
-    <item msgid="3517061573669307965">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
-    <item msgid="2553206901068987657">"LDAC"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
     <item msgid="926809261293414607">"Folosiți selectarea sistemului (prestabilit)"</item>
     <item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml
index 946f4b7..f49fcdd 100644
--- a/packages/SettingsLib/res/values-ro/strings.xml
+++ b/packages/SettingsLib/res/values-ro/strings.xml
@@ -117,10 +117,8 @@
     <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Transfer de fișiere"</string>
     <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Dispozitiv de intrare"</string>
     <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Acces la internet"</string>
-    <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
-    <skip />
-    <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
-    <skip />
+    <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Acces la agendă și istoricul apelurilor"</string>
+    <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Folosiți pentru accesul la agendă și istoricul apelurilor"</string>
     <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Distribuirea conexiunii la internet"</string>
     <string name="bluetooth_profile_map" msgid="8907204701162107271">"Mesaje text"</string>
     <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Acces la SIM"</string>
@@ -662,6 +660,8 @@
     <string name="dream_complication_title_weather" msgid="598609151677172783">"Meteo"</string>
     <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Calitatea aerului"</string>
     <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Informații artiști"</string>
+    <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+    <skip />
     <string name="avatar_picker_title" msgid="8492884172713170652">"Alegeți o fotografie de profil"</string>
     <string name="default_user_icon_description" msgid="6554047177298972638">"Pictograma prestabilită a utilizatorului"</string>
     <string name="physical_keyboard_title" msgid="4811935435315835220">"Tastatură fizică"</string>
diff --git a/packages/SettingsLib/res/values-ru/arrays.xml b/packages/SettingsLib/res/values-ru/arrays.xml
index 28b4695..b1211a5 100644
--- a/packages/SettingsLib/res/values-ru/arrays.xml
+++ b/packages/SettingsLib/res/values-ru/arrays.xml
@@ -85,22 +85,10 @@
     <item msgid="7073042887003102964">"map13"</item>
     <item msgid="8147982633566548515">"map14"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="2494959071796102843">"Выбор системы (по умолчанию)"</item>
-    <item msgid="4055460186095649420">"SBC"</item>
-    <item msgid="720249083677397051">"AAC"</item>
-    <item msgid="1049450003868150455">"Аудиокодек: <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
-    <item msgid="2908219194098827570">"Аудиокодек: <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
-    <item msgid="3825367753087348007">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="8868109554557331312">"Выбор системы (по умолчанию)"</item>
-    <item msgid="9024885861221697796">"SBC"</item>
-    <item msgid="4688890470703790013">"AAC"</item>
-    <item msgid="8627333814413492563">"Аудиокодек: <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
-    <item msgid="3517061573669307965">"Аудиокодек: <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
-    <item msgid="2553206901068987657">"LDAC"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
     <item msgid="926809261293414607">"Выбор системы (по умолчанию)"</item>
     <item msgid="8003118270854840095">"44,1 кГц"</item>
diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml
index bcd83cf..38930a5 100644
--- a/packages/SettingsLib/res/values-ru/strings.xml
+++ b/packages/SettingsLib/res/values-ru/strings.xml
@@ -117,10 +117,8 @@
     <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Профиль OPP"</string>
     <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Профиль HID"</string>
     <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Доступ к интернету"</string>
-    <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
-    <skip />
-    <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
-    <skip />
+    <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Доступ к контактам и журналу звонков"</string>
+    <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Использовать для доступа к контактам и журналу звонков"</string>
     <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Профиль PAN"</string>
     <string name="bluetooth_profile_map" msgid="8907204701162107271">"Текстовые сообщения"</string>
     <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Доступ к SIM-карте"</string>
@@ -662,6 +660,8 @@
     <string name="dream_complication_title_weather" msgid="598609151677172783">"Погода"</string>
     <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Качество воздуха"</string>
     <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Данные о трансляции"</string>
+    <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+    <skip />
     <string name="avatar_picker_title" msgid="8492884172713170652">"Выберите фото профиля"</string>
     <string name="default_user_icon_description" msgid="6554047177298972638">"Значок пользователя по умолчанию"</string>
     <string name="physical_keyboard_title" msgid="4811935435315835220">"Физическая клавиатура"</string>
diff --git a/packages/SettingsLib/res/values-si/arrays.xml b/packages/SettingsLib/res/values-si/arrays.xml
index c2dbf60..8386c1a 100644
--- a/packages/SettingsLib/res/values-si/arrays.xml
+++ b/packages/SettingsLib/res/values-si/arrays.xml
@@ -85,22 +85,10 @@
     <item msgid="7073042887003102964">"map13"</item>
     <item msgid="8147982633566548515">"map14"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="2494959071796102843">"පද්ධති තේරීම භාවිත කරන්න (පෙරනිමි)"</item>
-    <item msgid="4055460186095649420">"SBC"</item>
-    <item msgid="720249083677397051">"AAC"</item>
-    <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> ශ්‍රව්‍යය"</item>
-    <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> ශ්‍රව්‍යය"</item>
-    <item msgid="3825367753087348007">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="8868109554557331312">"පද්ධති තේරීම භාවිත කරන්න (පෙරනිමි)"</item>
-    <item msgid="9024885861221697796">"SBC"</item>
-    <item msgid="4688890470703790013">"AAC"</item>
-    <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> ශ්‍රව්‍යය"</item>
-    <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> ශ්‍රව්‍යය"</item>
-    <item msgid="2553206901068987657">"LDAC"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
     <item msgid="926809261293414607">"පද්ධති තේරීම භාවිත කරන්න (පෙරනිමි)"</item>
     <item msgid="8003118270854840095">"44.1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml
index 7f5eb49..fa0296f 100644
--- a/packages/SettingsLib/res/values-si/strings.xml
+++ b/packages/SettingsLib/res/values-si/strings.xml
@@ -117,10 +117,8 @@
     <string name="bluetooth_profile_opp" msgid="6692618568149493430">"ගොනු හුවමාරුව"</string>
     <string name="bluetooth_profile_hid" msgid="2969922922664315866">"ආදාන උපාංගය"</string>
     <string name="bluetooth_profile_pan" msgid="1006235139308318188">"අන්තර්ජාල ප්‍රවේශය"</string>
-    <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
-    <skip />
-    <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
-    <skip />
+    <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"සම්බන්ධතා සහ ඇමතුම් ඉතිහාසය බෙදා ගැනීම"</string>
+    <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"සම්බන්ධතා සහ ඇමතුම් ඉතිහාසය බෙදා ගැනීම සඳහා භාවිතා කරන්න"</string>
     <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"අන්තර්ජාල සම්බන්ධතා බෙදාගැනීම"</string>
     <string name="bluetooth_profile_map" msgid="8907204701162107271">"පෙළ පණිවිඩ"</string>
     <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM ප්‍රවේශය"</string>
@@ -662,6 +660,8 @@
     <string name="dream_complication_title_weather" msgid="598609151677172783">"කාලගුණය"</string>
     <string name="dream_complication_title_aqi" msgid="4587552608957834110">"වායු ගුණත්වය"</string>
     <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"විකාශ තතු"</string>
+    <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+    <skip />
     <string name="avatar_picker_title" msgid="8492884172713170652">"පැතිකඩ පින්තූරයක් තේරීම"</string>
     <string name="default_user_icon_description" msgid="6554047177298972638">"පෙරනිමි පරිශීලක නිරූපකය"</string>
     <string name="physical_keyboard_title" msgid="4811935435315835220">"භෞතික යතුරු පුවරුව"</string>
diff --git a/packages/SettingsLib/res/values-sk/arrays.xml b/packages/SettingsLib/res/values-sk/arrays.xml
index b285888..370b23f 100644
--- a/packages/SettingsLib/res/values-sk/arrays.xml
+++ b/packages/SettingsLib/res/values-sk/arrays.xml
@@ -85,22 +85,10 @@
     <item msgid="7073042887003102964">"map13"</item>
     <item msgid="8147982633566548515">"map14"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="2494959071796102843">"Použiť voľbu systému (predvolené)"</item>
-    <item msgid="4055460186095649420">"SBC"</item>
-    <item msgid="720249083677397051">"AAC"</item>
-    <item msgid="1049450003868150455">"Zvuk: <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
-    <item msgid="2908219194098827570">"Zvuk: <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
-    <item msgid="3825367753087348007">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="8868109554557331312">"Použiť voľbu systému (predvolené)"</item>
-    <item msgid="9024885861221697796">"SBC"</item>
-    <item msgid="4688890470703790013">"AAC"</item>
-    <item msgid="8627333814413492563">"Zvuk: <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
-    <item msgid="3517061573669307965">"Zvuk: <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
-    <item msgid="2553206901068987657">"LDAC"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
     <item msgid="926809261293414607">"Použiť voľbu systému (predvolené)"</item>
     <item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml
index 31925ea..a7c69c8 100644
--- a/packages/SettingsLib/res/values-sk/strings.xml
+++ b/packages/SettingsLib/res/values-sk/strings.xml
@@ -117,10 +117,8 @@
     <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Prenos súborov"</string>
     <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Vstupné zariadenie"</string>
     <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Prístup na internet"</string>
-    <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
-    <skip />
-    <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
-    <skip />
+    <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Zdieľanie kontaktov a histórie hovorov"</string>
+    <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Používané pri zdieľaní kontaktov a histórie hovorov"</string>
     <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Zdieľanie pripojenia na Internet"</string>
     <string name="bluetooth_profile_map" msgid="8907204701162107271">"Textové správy"</string>
     <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Prístup k SIM karte"</string>
@@ -662,6 +660,7 @@
     <string name="dream_complication_title_weather" msgid="598609151677172783">"Počasie"</string>
     <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Kvalita vzduchu"</string>
     <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Informácie o prenose"</string>
+    <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Ovládanie domácnosti"</string>
     <string name="avatar_picker_title" msgid="8492884172713170652">"Výber profilovej fotky"</string>
     <string name="default_user_icon_description" msgid="6554047177298972638">"Predvolená ikona používateľa"</string>
     <string name="physical_keyboard_title" msgid="4811935435315835220">"Fyzická klávesnica"</string>
diff --git a/packages/SettingsLib/res/values-sl/arrays.xml b/packages/SettingsLib/res/values-sl/arrays.xml
index 8db3e99..6e33e38 100644
--- a/packages/SettingsLib/res/values-sl/arrays.xml
+++ b/packages/SettingsLib/res/values-sl/arrays.xml
@@ -85,22 +85,10 @@
     <item msgid="7073042887003102964">"map13"</item>
     <item msgid="8147982633566548515">"map14"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="2494959071796102843">"Uporabi sistemsko izbiro (privzeto)"</item>
-    <item msgid="4055460186095649420">"SBC"</item>
-    <item msgid="720249083677397051">"AAC"</item>
-    <item msgid="1049450003868150455">"Zvok <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
-    <item msgid="2908219194098827570">"Zvok <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
-    <item msgid="3825367753087348007">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="8868109554557331312">"Uporabi sistemsko izbiro (privzeto)"</item>
-    <item msgid="9024885861221697796">"SBC"</item>
-    <item msgid="4688890470703790013">"AAC"</item>
-    <item msgid="8627333814413492563">"Zvok <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
-    <item msgid="3517061573669307965">"Zvok <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
-    <item msgid="2553206901068987657">"LDAC"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
     <item msgid="926809261293414607">"Uporabi sistemsko izbiro (privzeto)"</item>
     <item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml
index 687ff38..7334cea 100644
--- a/packages/SettingsLib/res/values-sl/strings.xml
+++ b/packages/SettingsLib/res/values-sl/strings.xml
@@ -117,10 +117,8 @@
     <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Prenos datoteke"</string>
     <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Vnosna naprava"</string>
     <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Internetni dostop"</string>
-    <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
-    <skip />
-    <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
-    <skip />
+    <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Deljenje stikov in zgodovine klicev"</string>
+    <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Uporabite za deljenje stikov in zgodovine klicev."</string>
     <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Deljenje internetne povezave"</string>
     <string name="bluetooth_profile_map" msgid="8907204701162107271">"Sporočila SMS"</string>
     <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Dostop do kartice SIM"</string>
@@ -662,6 +660,7 @@
     <string name="dream_complication_title_weather" msgid="598609151677172783">"Vreme"</string>
     <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Kakovost zraka"</string>
     <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"O zasedbi"</string>
+    <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Nadzor doma"</string>
     <string name="avatar_picker_title" msgid="8492884172713170652">"Izbira profilne slike"</string>
     <string name="default_user_icon_description" msgid="6554047177298972638">"Privzeta ikona uporabnika"</string>
     <string name="physical_keyboard_title" msgid="4811935435315835220">"Fizična tipkovnica"</string>
diff --git a/packages/SettingsLib/res/values-sq/arrays.xml b/packages/SettingsLib/res/values-sq/arrays.xml
index a82f716..8a6d853 100644
--- a/packages/SettingsLib/res/values-sq/arrays.xml
+++ b/packages/SettingsLib/res/values-sq/arrays.xml
@@ -85,22 +85,10 @@
     <item msgid="7073042887003102964">"map13"</item>
     <item msgid="8147982633566548515">"map14"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="2494959071796102843">"Përdor përzgjedhjen e sistemit (e parazgjedhur)"</item>
-    <item msgid="4055460186095649420">"SBC"</item>
-    <item msgid="720249083677397051">"AAC"</item>
-    <item msgid="1049450003868150455">"Audioja e <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
-    <item msgid="2908219194098827570">"Audioja e <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
-    <item msgid="3825367753087348007">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="8868109554557331312">"Përdor përzgjedhjen e sistemit (e parazgjedhur)"</item>
-    <item msgid="9024885861221697796">"SBC"</item>
-    <item msgid="4688890470703790013">"AAC"</item>
-    <item msgid="8627333814413492563">"Audioja e <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
-    <item msgid="3517061573669307965">"Audioja e <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
-    <item msgid="2553206901068987657">"LDAC"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
     <item msgid="926809261293414607">"Përdor përzgjedhjen e sistemit (e parazgjedhur)"</item>
     <item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml
index 7e96990..59359cdf 100644
--- a/packages/SettingsLib/res/values-sq/strings.xml
+++ b/packages/SettingsLib/res/values-sq/strings.xml
@@ -117,10 +117,8 @@
     <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Transferimi i skedarëve"</string>
     <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Pajisja e hyrjes"</string>
     <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Qasje në internet"</string>
-    <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
-    <skip />
-    <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
-    <skip />
+    <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Ndarje: kontakte e historik telefonatash"</string>
+    <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Përdor për ndarje kontaktesh e të historikut të telefonatave"</string>
     <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Ndarja e lidhjes së internetit"</string>
     <string name="bluetooth_profile_map" msgid="8907204701162107271">"Mesazhet me tekst"</string>
     <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Qasje në kartën SIM"</string>
@@ -662,6 +660,8 @@
     <string name="dream_complication_title_weather" msgid="598609151677172783">"Moti"</string>
     <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Cilësia e ajrit"</string>
     <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Të dhënat e aktorëve"</string>
+    <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+    <skip />
     <string name="avatar_picker_title" msgid="8492884172713170652">"Zgjidh një fotografi profili"</string>
     <string name="default_user_icon_description" msgid="6554047177298972638">"Ikona e parazgjedhur e përdoruesit"</string>
     <string name="physical_keyboard_title" msgid="4811935435315835220">"Tastiera fizike"</string>
diff --git a/packages/SettingsLib/res/values-sr/arrays.xml b/packages/SettingsLib/res/values-sr/arrays.xml
index 4cd83606..7e198bf 100644
--- a/packages/SettingsLib/res/values-sr/arrays.xml
+++ b/packages/SettingsLib/res/values-sr/arrays.xml
@@ -85,22 +85,10 @@
     <item msgid="7073042887003102964">"map13"</item>
     <item msgid="8147982633566548515">"map14"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="2494959071796102843">"Користи избор система (подразумевано)"</item>
-    <item msgid="4055460186095649420">"SBC"</item>
-    <item msgid="720249083677397051">"AAC"</item>
-    <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> аудио"</item>
-    <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> аудио"</item>
-    <item msgid="3825367753087348007">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="8868109554557331312">"Користи избор система (подразумевано)"</item>
-    <item msgid="9024885861221697796">"SBC"</item>
-    <item msgid="4688890470703790013">"AAC"</item>
-    <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> аудио"</item>
-    <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> аудио"</item>
-    <item msgid="2553206901068987657">"LDAC"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
     <item msgid="926809261293414607">"Користи избор система (подразумевано)"</item>
     <item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml
index 2842223..eee3f89 100644
--- a/packages/SettingsLib/res/values-sr/strings.xml
+++ b/packages/SettingsLib/res/values-sr/strings.xml
@@ -117,10 +117,8 @@
     <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Пренос датотеке"</string>
     <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Улазни уређај"</string>
     <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Приступ Интернету"</string>
-    <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
-    <skip />
-    <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
-    <skip />
+    <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Дељење контаката и историје позива"</string>
+    <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Користите за дељење контаката и историје позива"</string>
     <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Дељење интернет везе"</string>
     <string name="bluetooth_profile_map" msgid="8907204701162107271">"SMS-ови"</string>
     <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Приступ SIM картици"</string>
@@ -662,6 +660,8 @@
     <string name="dream_complication_title_weather" msgid="598609151677172783">"Време"</string>
     <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Квалитет ваздуха"</string>
     <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Подаци о пребацивању"</string>
+    <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+    <skip />
     <string name="avatar_picker_title" msgid="8492884172713170652">"Одаберите слику профила"</string>
     <string name="default_user_icon_description" msgid="6554047177298972638">"Подразумевана икона корисника"</string>
     <string name="physical_keyboard_title" msgid="4811935435315835220">"Физичка тастатура"</string>
diff --git a/packages/SettingsLib/res/values-sv/arrays.xml b/packages/SettingsLib/res/values-sv/arrays.xml
index 1432ec2..f99a85b 100644
--- a/packages/SettingsLib/res/values-sv/arrays.xml
+++ b/packages/SettingsLib/res/values-sv/arrays.xml
@@ -85,22 +85,10 @@
     <item msgid="7073042887003102964">"map13"</item>
     <item msgid="8147982633566548515">"map14"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="2494959071796102843">"Använd systemval (standardinställning)"</item>
-    <item msgid="4055460186095649420">"SBC"</item>
-    <item msgid="720249083677397051">"AAC"</item>
-    <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>-ljud"</item>
-    <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>-ljud"</item>
-    <item msgid="3825367753087348007">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="8868109554557331312">"Använd systemval (standardinställning)"</item>
-    <item msgid="9024885861221697796">"SBC"</item>
-    <item msgid="4688890470703790013">"AAC"</item>
-    <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>-ljud"</item>
-    <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>-ljud"</item>
-    <item msgid="2553206901068987657">"LDAC"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
     <item msgid="926809261293414607">"Använd systemval (standardinställning)"</item>
     <item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml
index b0f8df5..1a8815c 100644
--- a/packages/SettingsLib/res/values-sv/strings.xml
+++ b/packages/SettingsLib/res/values-sv/strings.xml
@@ -117,10 +117,8 @@
     <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Filöverföring"</string>
     <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Indataenhet"</string>
     <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Internetåtkomst"</string>
-    <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
-    <skip />
-    <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
-    <skip />
+    <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Delning av kontakter och samtalshistorik"</string>
+    <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Använd för delning av kontakter och samtalshistorik"</string>
     <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Delning av Internetanslutning"</string>
     <string name="bluetooth_profile_map" msgid="8907204701162107271">"Sms"</string>
     <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM-åtkomst"</string>
@@ -662,6 +660,8 @@
     <string name="dream_complication_title_weather" msgid="598609151677172783">"Väder"</string>
     <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Luftkvalitet"</string>
     <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Info om rollistan"</string>
+    <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+    <skip />
     <string name="avatar_picker_title" msgid="8492884172713170652">"Välj en profilbild"</string>
     <string name="default_user_icon_description" msgid="6554047177298972638">"Ikon för standardanvändare"</string>
     <string name="physical_keyboard_title" msgid="4811935435315835220">"Fysiskt tangentbord"</string>
diff --git a/packages/SettingsLib/res/values-sw/arrays.xml b/packages/SettingsLib/res/values-sw/arrays.xml
index cb74761..dab4279 100644
--- a/packages/SettingsLib/res/values-sw/arrays.xml
+++ b/packages/SettingsLib/res/values-sw/arrays.xml
@@ -85,22 +85,10 @@
     <item msgid="7073042887003102964">"ramani ya 13"</item>
     <item msgid="8147982633566548515">"ramani ya 14"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="2494959071796102843">"Tumia Uteuzi wa Mfumo (Chaguomsingi)"</item>
-    <item msgid="4055460186095649420">"SBC"</item>
-    <item msgid="720249083677397051">"AAC"</item>
-    <item msgid="1049450003868150455">"Sauti ya <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
-    <item msgid="2908219194098827570">"Sauti ya <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
-    <item msgid="3825367753087348007">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="8868109554557331312">"Tumia Uteuzi wa Mfumo (Chaguomsingi)"</item>
-    <item msgid="9024885861221697796">"SBC"</item>
-    <item msgid="4688890470703790013">"AAC"</item>
-    <item msgid="8627333814413492563">"Sauti ya <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
-    <item msgid="3517061573669307965">"Sauti ya <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
-    <item msgid="2553206901068987657">"LDAC"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
     <item msgid="926809261293414607">"Tumia Uteuzi wa Mfumo (Chaguomsingi)"</item>
     <item msgid="8003118270854840095">"kHz 44.1"</item>
diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml
index 7e62d3f..5f1d141 100644
--- a/packages/SettingsLib/res/values-sw/strings.xml
+++ b/packages/SettingsLib/res/values-sw/strings.xml
@@ -117,10 +117,8 @@
     <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Uhamishaji wa faili"</string>
     <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Kifaa cha kuingiza"</string>
     <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Ufikiaji wa intaneti"</string>
-    <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
-    <skip />
-    <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
-    <skip />
+    <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Kushiriki anwani na rekodi ya simu zilizopigwa"</string>
+    <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Tumia kushiriki anwani na rekodi ya simu zilizopigwa"</string>
     <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Kushiriki muunganisho wa tovuti"</string>
     <string name="bluetooth_profile_map" msgid="8907204701162107271">"SMS"</string>
     <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Ufikiaji wa SIM"</string>
@@ -662,6 +660,8 @@
     <string name="dream_complication_title_weather" msgid="598609151677172783">"Hali ya Hewa"</string>
     <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Ubora wa Hewa"</string>
     <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Maelezo ya Wahusika"</string>
+    <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+    <skip />
     <string name="avatar_picker_title" msgid="8492884172713170652">"Chagua picha ya wasifu"</string>
     <string name="default_user_icon_description" msgid="6554047177298972638">"Aikoni chaguomsingi ya mtumiaji"</string>
     <string name="physical_keyboard_title" msgid="4811935435315835220">"Kibodi halisi"</string>
diff --git a/packages/SettingsLib/res/values-ta/arrays.xml b/packages/SettingsLib/res/values-ta/arrays.xml
index 1f5380f..a0f1fa6 100644
--- a/packages/SettingsLib/res/values-ta/arrays.xml
+++ b/packages/SettingsLib/res/values-ta/arrays.xml
@@ -85,22 +85,10 @@
     <item msgid="7073042887003102964">"map13"</item>
     <item msgid="8147982633566548515">"map14"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="2494959071796102843">"சாதனத் தேர்வைப் பயன்படுத்து (இயல்பு)"</item>
-    <item msgid="4055460186095649420">"SBC"</item>
-    <item msgid="720249083677397051">"AAC"</item>
-    <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> ஆடியோ"</item>
-    <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> ஆடியோ"</item>
-    <item msgid="3825367753087348007">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="8868109554557331312">"சாதனத் தேர்வைப் பயன்படுத்து (இயல்பு)"</item>
-    <item msgid="9024885861221697796">"SBC"</item>
-    <item msgid="4688890470703790013">"AAC"</item>
-    <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> ஆடியோ"</item>
-    <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> ஆடியோ"</item>
-    <item msgid="2553206901068987657">"LDAC"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
     <item msgid="926809261293414607">"சாதனத் தேர்வைப் பயன்படுத்து (இயல்பு)"</item>
     <item msgid="8003118270854840095">"44.1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml
index abab3fb..b8c37b0 100644
--- a/packages/SettingsLib/res/values-ta/strings.xml
+++ b/packages/SettingsLib/res/values-ta/strings.xml
@@ -117,10 +117,8 @@
     <string name="bluetooth_profile_opp" msgid="6692618568149493430">"ஃபைல் இடமாற்றம்"</string>
     <string name="bluetooth_profile_hid" msgid="2969922922664315866">"உள்ளீட்டுச் சாதனம்"</string>
     <string name="bluetooth_profile_pan" msgid="1006235139308318188">"இணைய அணுகல்"</string>
-    <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
-    <skip />
-    <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
-    <skip />
+    <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"தொடர்புகள் &amp; அழைப்புப் பதிவைப் பகிர்தல்"</string>
+    <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"தொடர்புகளையும் அழைப்புப் பதிவையும் பகிர்வதற்குப் பயன்படுத்து"</string>
     <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"இணைய இணைப்பு பகிர்தல்"</string>
     <string name="bluetooth_profile_map" msgid="8907204701162107271">"உரைச் செய்திகள்"</string>
     <string name="bluetooth_profile_sap" msgid="8304170950447934386">"சிம் அணுகல்"</string>
@@ -662,6 +660,8 @@
     <string name="dream_complication_title_weather" msgid="598609151677172783">"வானிலை"</string>
     <string name="dream_complication_title_aqi" msgid="4587552608957834110">"காற்றின் தரம்"</string>
     <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"அலைபரப்புத் தகவல்"</string>
+    <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+    <skip />
     <string name="avatar_picker_title" msgid="8492884172713170652">"சுயவிவரப் படத்தைத் தேர்வுசெய்யுங்கள்"</string>
     <string name="default_user_icon_description" msgid="6554047177298972638">"இயல்புநிலைப் பயனர் ஐகான்"</string>
     <string name="physical_keyboard_title" msgid="4811935435315835220">"கீபோர்டு"</string>
diff --git a/packages/SettingsLib/res/values-te/arrays.xml b/packages/SettingsLib/res/values-te/arrays.xml
index 513fb6e..18a2758 100644
--- a/packages/SettingsLib/res/values-te/arrays.xml
+++ b/packages/SettingsLib/res/values-te/arrays.xml
@@ -85,22 +85,10 @@
     <item msgid="7073042887003102964">"map13"</item>
     <item msgid="8147982633566548515">"map14"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="2494959071796102843">"సిస్టమ్ ఎంపికను ఉపయోగించండి (ఆటోమేటిక్)"</item>
-    <item msgid="4055460186095649420">"SBC"</item>
-    <item msgid="720249083677397051">"AAC"</item>
-    <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> ఆడియో"</item>
-    <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> ఆడియో"</item>
-    <item msgid="3825367753087348007">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="8868109554557331312">"సిస్టమ్ ఎంపికను ఉపయోగించండి (ఆటోమేటిక్)"</item>
-    <item msgid="9024885861221697796">"SBC"</item>
-    <item msgid="4688890470703790013">"AAC"</item>
-    <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> ఆడియో"</item>
-    <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> ఆడియో"</item>
-    <item msgid="2553206901068987657">"LDAC"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
     <item msgid="926809261293414607">"సిస్టమ్ ఎంపికను ఉపయోగించండి (ఆటోమేటిక్)"</item>
     <item msgid="8003118270854840095">"44.1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml
index a362f24..504c827 100644
--- a/packages/SettingsLib/res/values-te/strings.xml
+++ b/packages/SettingsLib/res/values-te/strings.xml
@@ -117,10 +117,8 @@
     <string name="bluetooth_profile_opp" msgid="6692618568149493430">"ఫైల్ బదిలీ"</string>
     <string name="bluetooth_profile_hid" msgid="2969922922664315866">"ఇన్‌పుట్ పరికరం"</string>
     <string name="bluetooth_profile_pan" msgid="1006235139308318188">"ఇంటర్నెట్ యాక్సెస్"</string>
-    <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
-    <skip />
-    <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
-    <skip />
+    <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Contacts and call history sharing"</string>
+    <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Use for contacts and call history sharing"</string>
     <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"ఇంటర్నెట్ కనెక్షన్ షేరింగ్"</string>
     <string name="bluetooth_profile_map" msgid="8907204701162107271">"టెక్స్ట్ మెసేజ్‌లు"</string>
     <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM యాక్సెస్"</string>
@@ -662,6 +660,7 @@
     <string name="dream_complication_title_weather" msgid="598609151677172783">"వాతావరణం"</string>
     <string name="dream_complication_title_aqi" msgid="4587552608957834110">"గాలి క్వాలిటీ"</string>
     <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"కాస్ట్ సమాచారం"</string>
+    <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"హోమ్ కంట్రోల్స్"</string>
     <string name="avatar_picker_title" msgid="8492884172713170652">"ప్రొఫైల్ ఫోటోను ఎంచుకోండి"</string>
     <string name="default_user_icon_description" msgid="6554047177298972638">"ఆటోమేటిక్ సెట్టింగ్ యూజర్ చిహ్నం"</string>
     <string name="physical_keyboard_title" msgid="4811935435315835220">"భౌతిక కీబోర్డ్"</string>
diff --git a/packages/SettingsLib/res/values-th/arrays.xml b/packages/SettingsLib/res/values-th/arrays.xml
index 732124a..04a5f4d 100644
--- a/packages/SettingsLib/res/values-th/arrays.xml
+++ b/packages/SettingsLib/res/values-th/arrays.xml
@@ -85,22 +85,10 @@
     <item msgid="7073042887003102964">"map13"</item>
     <item msgid="8147982633566548515">"map14"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="2494959071796102843">"ใช้การเลือกของระบบ (ค่าเริ่มต้น)"</item>
-    <item msgid="4055460186095649420">"SBC"</item>
-    <item msgid="720249083677397051">"AAC"</item>
-    <item msgid="1049450003868150455">"เสียง <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
-    <item msgid="2908219194098827570">"เสียง <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
-    <item msgid="3825367753087348007">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="8868109554557331312">"ใช้การเลือกของระบบ (ค่าเริ่มต้น)"</item>
-    <item msgid="9024885861221697796">"SBC"</item>
-    <item msgid="4688890470703790013">"AAC"</item>
-    <item msgid="8627333814413492563">"เสียง <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
-    <item msgid="3517061573669307965">"เสียง <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
-    <item msgid="2553206901068987657">"LDAC"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
     <item msgid="926809261293414607">"ใช้การเลือกของระบบ (ค่าเริ่มต้น)"</item>
     <item msgid="8003118270854840095">"44.1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml
index a4e0508..f33630d 100644
--- a/packages/SettingsLib/res/values-th/strings.xml
+++ b/packages/SettingsLib/res/values-th/strings.xml
@@ -117,10 +117,8 @@
     <string name="bluetooth_profile_opp" msgid="6692618568149493430">"การถ่ายโอนไฟล์"</string>
     <string name="bluetooth_profile_hid" msgid="2969922922664315866">"อุปกรณ์อินพุต"</string>
     <string name="bluetooth_profile_pan" msgid="1006235139308318188">"การเข้าถึงอินเทอร์เน็ต"</string>
-    <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
-    <skip />
-    <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
-    <skip />
+    <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"การแชร์รายชื่อติดต่อและประวัติการโทร"</string>
+    <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"ใช้สำหรับการแชร์รายชื่อติดต่อและประวัติการโทร"</string>
     <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"การแชร์การเชื่อมต่ออินเทอร์เน็ต"</string>
     <string name="bluetooth_profile_map" msgid="8907204701162107271">"ข้อความ"</string>
     <string name="bluetooth_profile_sap" msgid="8304170950447934386">"การเข้าถึงซิม"</string>
@@ -662,6 +660,8 @@
     <string name="dream_complication_title_weather" msgid="598609151677172783">"สภาพอากาศ"</string>
     <string name="dream_complication_title_aqi" msgid="4587552608957834110">"คุณภาพอากาศ"</string>
     <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"ข้อมูลแคสต์"</string>
+    <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+    <skip />
     <string name="avatar_picker_title" msgid="8492884172713170652">"เลือกรูปโปรไฟล์"</string>
     <string name="default_user_icon_description" msgid="6554047177298972638">"ไอคอนผู้ใช้เริ่มต้น"</string>
     <string name="physical_keyboard_title" msgid="4811935435315835220">"แป้นพิมพ์จริง"</string>
diff --git a/packages/SettingsLib/res/values-tl/arrays.xml b/packages/SettingsLib/res/values-tl/arrays.xml
index bdd8706..59cb1f3 100644
--- a/packages/SettingsLib/res/values-tl/arrays.xml
+++ b/packages/SettingsLib/res/values-tl/arrays.xml
@@ -85,22 +85,10 @@
     <item msgid="7073042887003102964">"map13"</item>
     <item msgid="8147982633566548515">"map14"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="2494959071796102843">"Gamitin ang Pagpili ng System (Default)"</item>
-    <item msgid="4055460186095649420">"SBC"</item>
-    <item msgid="720249083677397051">"AAC"</item>
-    <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> na audio"</item>
-    <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> na audio"</item>
-    <item msgid="3825367753087348007">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="8868109554557331312">"Gamitin ang Pagpili ng System (Default)"</item>
-    <item msgid="9024885861221697796">"SBC"</item>
-    <item msgid="4688890470703790013">"AAC"</item>
-    <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> na audio"</item>
-    <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> na audio"</item>
-    <item msgid="2553206901068987657">"LDAC"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
     <item msgid="926809261293414607">"Gamitin ang Pagpili ng System (Default)"</item>
     <item msgid="8003118270854840095">"44.1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml
index e28fd96..f95f0e50 100644
--- a/packages/SettingsLib/res/values-tl/strings.xml
+++ b/packages/SettingsLib/res/values-tl/strings.xml
@@ -117,10 +117,8 @@
     <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Paglilipat ng file"</string>
     <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Device sa pag-input"</string>
     <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Access sa internet"</string>
-    <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
-    <skip />
-    <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
-    <skip />
+    <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Contacts at pagbabahagi ng call history"</string>
+    <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Gamitin para sa contacts at pagbabahagi ng call history"</string>
     <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Pagbabahagi ng koneksyon sa internet"</string>
     <string name="bluetooth_profile_map" msgid="8907204701162107271">"Mga Text Message"</string>
     <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Access sa SIM"</string>
@@ -662,6 +660,8 @@
     <string name="dream_complication_title_weather" msgid="598609151677172783">"Lagay ng Panahon"</string>
     <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Kalidad ng Hangin"</string>
     <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Impormasyon ng Cast"</string>
+    <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+    <skip />
     <string name="avatar_picker_title" msgid="8492884172713170652">"Pumili ng larawan sa profile"</string>
     <string name="default_user_icon_description" msgid="6554047177298972638">"Icon ng default na user"</string>
     <string name="physical_keyboard_title" msgid="4811935435315835220">"Pisikal na keyboard"</string>
diff --git a/packages/SettingsLib/res/values-tr/arrays.xml b/packages/SettingsLib/res/values-tr/arrays.xml
index 0d0c69a..5ed35fa 100644
--- a/packages/SettingsLib/res/values-tr/arrays.xml
+++ b/packages/SettingsLib/res/values-tr/arrays.xml
@@ -85,22 +85,10 @@
     <item msgid="7073042887003102964">"map13"</item>
     <item msgid="8147982633566548515">"map14"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="2494959071796102843">"Sistem Seçimini Kullan (Varsayılan)"</item>
-    <item msgid="4055460186095649420">"SBC"</item>
-    <item msgid="720249083677397051">"AAC"</item>
-    <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> ses"</item>
-    <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> ses"</item>
-    <item msgid="3825367753087348007">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="8868109554557331312">"Sistem Seçimini Kullan (Varsayılan)"</item>
-    <item msgid="9024885861221697796">"SBC"</item>
-    <item msgid="4688890470703790013">"AAC"</item>
-    <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> ses"</item>
-    <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> ses"</item>
-    <item msgid="2553206901068987657">"LDAC"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
     <item msgid="926809261293414607">"Sistem Seçimini Kullan (Varsayılan)"</item>
     <item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml
index 05ec1cc..2fa2bd1 100644
--- a/packages/SettingsLib/res/values-tr/strings.xml
+++ b/packages/SettingsLib/res/values-tr/strings.xml
@@ -117,10 +117,8 @@
     <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Dosya aktarımı"</string>
     <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Giriş cihazı"</string>
     <string name="bluetooth_profile_pan" msgid="1006235139308318188">"İnternet erişimi"</string>
-    <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
-    <skip />
-    <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
-    <skip />
+    <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Kişi ve çağrı geçmişi paylaşımı"</string>
+    <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Kişi ve çağrı geçmişi paylaşımı için kullanın"</string>
     <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"İnternet bağlantısı paylaşımı"</string>
     <string name="bluetooth_profile_map" msgid="8907204701162107271">"Kısa Mesajlar"</string>
     <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM Erişimi"</string>
@@ -662,6 +660,8 @@
     <string name="dream_complication_title_weather" msgid="598609151677172783">"Hava durumu"</string>
     <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Hava Kalitesi"</string>
     <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Yayın Bilgisi"</string>
+    <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+    <skip />
     <string name="avatar_picker_title" msgid="8492884172713170652">"Profil fotoğrafı seçin"</string>
     <string name="default_user_icon_description" msgid="6554047177298972638">"Varsayılan kullanıcı simgesi"</string>
     <string name="physical_keyboard_title" msgid="4811935435315835220">"Fiziksel klavye"</string>
diff --git a/packages/SettingsLib/res/values-uk/arrays.xml b/packages/SettingsLib/res/values-uk/arrays.xml
index 49a4635..0410bd6 100644
--- a/packages/SettingsLib/res/values-uk/arrays.xml
+++ b/packages/SettingsLib/res/values-uk/arrays.xml
@@ -85,22 +85,10 @@
     <item msgid="7073042887003102964">"map13"</item>
     <item msgid="8147982633566548515">"map14"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="2494959071796102843">"Використовувати вибір системи (за умовчанням)"</item>
-    <item msgid="4055460186095649420">"SBC"</item>
-    <item msgid="720249083677397051">"AAC"</item>
-    <item msgid="1049450003868150455">"Аудіо <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
-    <item msgid="2908219194098827570">"Аудіо <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
-    <item msgid="3825367753087348007">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="8868109554557331312">"Використовувати вибір системи (за умовчанням)"</item>
-    <item msgid="9024885861221697796">"SBC"</item>
-    <item msgid="4688890470703790013">"AAC"</item>
-    <item msgid="8627333814413492563">"Аудіо <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
-    <item msgid="3517061573669307965">"Аудіо <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
-    <item msgid="2553206901068987657">"LDAC"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
     <item msgid="926809261293414607">"Використовувати вибір системи (за умовчанням)"</item>
     <item msgid="8003118270854840095">"44,1 кГц"</item>
diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml
index 184d0c1..9be0855 100644
--- a/packages/SettingsLib/res/values-uk/strings.xml
+++ b/packages/SettingsLib/res/values-uk/strings.xml
@@ -117,10 +117,8 @@
     <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Передавання файлів"</string>
     <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Пристрій введення"</string>
     <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Доступ до Інтернету"</string>
-    <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
-    <skip />
-    <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
-    <skip />
+    <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Надсилання контактів та історії викликів"</string>
+    <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Використовуйте, щоб надсилати контакти й історію викликів"</string>
     <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Надання доступу до Інтернету"</string>
     <string name="bluetooth_profile_map" msgid="8907204701162107271">"Текстові повідомлення"</string>
     <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Доступ до SIM-карти"</string>
@@ -662,6 +660,8 @@
     <string name="dream_complication_title_weather" msgid="598609151677172783">"Погода"</string>
     <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Якість повітря"</string>
     <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Акторський склад"</string>
+    <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+    <skip />
     <string name="avatar_picker_title" msgid="8492884172713170652">"Виберіть зображення профілю"</string>
     <string name="default_user_icon_description" msgid="6554047177298972638">"Значок користувача за умовчанням"</string>
     <string name="physical_keyboard_title" msgid="4811935435315835220">"Фізична клавіатура"</string>
diff --git a/packages/SettingsLib/res/values-ur/arrays.xml b/packages/SettingsLib/res/values-ur/arrays.xml
index 5dc2123..d097458 100644
--- a/packages/SettingsLib/res/values-ur/arrays.xml
+++ b/packages/SettingsLib/res/values-ur/arrays.xml
@@ -85,22 +85,10 @@
     <item msgid="7073042887003102964">"map13"</item>
     <item msgid="8147982633566548515">"map14"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="2494959071796102843">"سسٹم انتخاب کا استعمال کریں (ڈیفالٹ)"</item>
-    <item msgid="4055460186095649420">"SBC"</item>
-    <item msgid="720249083677397051">"AAC"</item>
-    <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> آڈیو"</item>
-    <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> آڈیو"</item>
-    <item msgid="3825367753087348007">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="8868109554557331312">"سسٹم انتخاب کا استعمال کریں (ڈیفالٹ)"</item>
-    <item msgid="9024885861221697796">"SBC"</item>
-    <item msgid="4688890470703790013">"AAC"</item>
-    <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> آڈیو"</item>
-    <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> آڈیو"</item>
-    <item msgid="2553206901068987657">"LDAC"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
     <item msgid="926809261293414607">"سسٹم انتخاب کا استعمال کریں (ڈیفالٹ)"</item>
     <item msgid="8003118270854840095">"44.1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml
index 419833a..8eb9a11 100644
--- a/packages/SettingsLib/res/values-ur/strings.xml
+++ b/packages/SettingsLib/res/values-ur/strings.xml
@@ -117,10 +117,8 @@
     <string name="bluetooth_profile_opp" msgid="6692618568149493430">"فائل کی منتقلی"</string>
     <string name="bluetooth_profile_hid" msgid="2969922922664315866">"ان پٹ آلہ"</string>
     <string name="bluetooth_profile_pan" msgid="1006235139308318188">"انٹرنیٹ تک رسائی"</string>
-    <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
-    <skip />
-    <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
-    <skip />
+    <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"رابطے اور کال کی سرگزشت کا اشتراک"</string>
+    <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"رابطے اور کال کی سرگزشت کے اشتراک کے لیے استعمال کریں"</string>
     <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"انٹرنیٹ کنکشن کا اشتراک کرنا"</string>
     <string name="bluetooth_profile_map" msgid="8907204701162107271">"ٹیکسٹ پیغامات"</string>
     <string name="bluetooth_profile_sap" msgid="8304170950447934386">"‏SIM رسائی"</string>
@@ -662,6 +660,7 @@
     <string name="dream_complication_title_weather" msgid="598609151677172783">"موسم"</string>
     <string name="dream_complication_title_aqi" msgid="4587552608957834110">"ہوا کا معیار"</string>
     <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"کاسٹ کرنے کی معلومات"</string>
+    <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"ہوم کنٹرولز"</string>
     <string name="avatar_picker_title" msgid="8492884172713170652">"پروفائل کی تصویر منتخب کریں"</string>
     <string name="default_user_icon_description" msgid="6554047177298972638">"ڈیفالٹ صارف کا آئیکن"</string>
     <string name="physical_keyboard_title" msgid="4811935435315835220">"فزیکل کی بورڈ"</string>
diff --git a/packages/SettingsLib/res/values-uz/arrays.xml b/packages/SettingsLib/res/values-uz/arrays.xml
index 589c592..7d09027 100644
--- a/packages/SettingsLib/res/values-uz/arrays.xml
+++ b/packages/SettingsLib/res/values-uz/arrays.xml
@@ -85,22 +85,10 @@
     <item msgid="7073042887003102964">"map13"</item>
     <item msgid="8147982633566548515">"map14"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="2494959071796102843">"Tizim tanlovi (birlamchi)"</item>
-    <item msgid="4055460186095649420">"SBC"</item>
-    <item msgid="720249083677397051">"AAC"</item>
-    <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> audiokodeki"</item>
-    <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> audiokodeki"</item>
-    <item msgid="3825367753087348007">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="8868109554557331312">"Tizim tanlovi (birlamchi)"</item>
-    <item msgid="9024885861221697796">"SBC"</item>
-    <item msgid="4688890470703790013">"AAC"</item>
-    <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> audiokodeki"</item>
-    <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> audiokodeki"</item>
-    <item msgid="2553206901068987657">"LDAC"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
     <item msgid="926809261293414607">"Tizim tanlovi (birlamchi)"</item>
     <item msgid="8003118270854840095">"44.1 kGs"</item>
diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml
index b1e22af..a5c8683 100644
--- a/packages/SettingsLib/res/values-uz/strings.xml
+++ b/packages/SettingsLib/res/values-uz/strings.xml
@@ -117,10 +117,8 @@
     <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Fayl uzatish"</string>
     <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Kiritish qurilmasi"</string>
     <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Internetga ulanish"</string>
-    <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
-    <skip />
-    <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
-    <skip />
+    <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Kontakt va chaqiruvlar tarixiga kirish"</string>
+    <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Kontaktlar va chaqiruvlar tarixiga kirish uchun foydalaning"</string>
     <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Internet aloqasi ulashmasi"</string>
     <string name="bluetooth_profile_map" msgid="8907204701162107271">"SMS xabarlari"</string>
     <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM kartaga kirish"</string>
@@ -662,6 +660,8 @@
     <string name="dream_complication_title_weather" msgid="598609151677172783">"Ob-havo"</string>
     <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Havo sifati"</string>
     <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Translatsiya axboroti"</string>
+    <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+    <skip />
     <string name="avatar_picker_title" msgid="8492884172713170652">"Profil rasmini tanlash"</string>
     <string name="default_user_icon_description" msgid="6554047177298972638">"Foydalanuvchining standart belgisi"</string>
     <string name="physical_keyboard_title" msgid="4811935435315835220">"Tashqi klaviatura"</string>
diff --git a/packages/SettingsLib/res/values-vi/arrays.xml b/packages/SettingsLib/res/values-vi/arrays.xml
index 4cf8ff4..31867e2 100644
--- a/packages/SettingsLib/res/values-vi/arrays.xml
+++ b/packages/SettingsLib/res/values-vi/arrays.xml
@@ -85,22 +85,10 @@
     <item msgid="7073042887003102964">"map13"</item>
     <item msgid="8147982633566548515">"map14"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="2494959071796102843">"Sử dụng lựa chọn của hệ thống (Mặc định)"</item>
-    <item msgid="4055460186095649420">"SBC"</item>
-    <item msgid="720249083677397051">"AAC"</item>
-    <item msgid="1049450003868150455">"Âm thanh <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
-    <item msgid="2908219194098827570">"Âm thanh <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
-    <item msgid="3825367753087348007">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="8868109554557331312">"Sử dụng lựa chọn của hệ thống (Mặc định)"</item>
-    <item msgid="9024885861221697796">"SBC"</item>
-    <item msgid="4688890470703790013">"AAC"</item>
-    <item msgid="8627333814413492563">"Âm thanh <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
-    <item msgid="3517061573669307965">"Âm thanh <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
-    <item msgid="2553206901068987657">"LDAC"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
     <item msgid="926809261293414607">"Sử dụng lựa chọn của hệ thống (Mặc định)"</item>
     <item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml
index 71bb08e..45a0465 100644
--- a/packages/SettingsLib/res/values-vi/strings.xml
+++ b/packages/SettingsLib/res/values-vi/strings.xml
@@ -117,10 +117,8 @@
     <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Chuyển tệp"</string>
     <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Thiết bị đầu vào"</string>
     <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Truy cập Internet"</string>
-    <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
-    <skip />
-    <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
-    <skip />
+    <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Chia sẻ danh bạ và nhật ký cuộc gọi"</string>
+    <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Dùng để chia sẻ danh bạ và nhật ký cuộc gọi"</string>
     <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Chia sẻ kết nối internet"</string>
     <string name="bluetooth_profile_map" msgid="8907204701162107271">"Tin nhắn văn bản"</string>
     <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Truy cập SIM"</string>
@@ -662,6 +660,8 @@
     <string name="dream_complication_title_weather" msgid="598609151677172783">"Thời tiết"</string>
     <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Chất lượng không khí"</string>
     <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Thông tin về dàn nghệ sĩ"</string>
+    <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+    <skip />
     <string name="avatar_picker_title" msgid="8492884172713170652">"Chọn một ảnh hồ sơ"</string>
     <string name="default_user_icon_description" msgid="6554047177298972638">"Biểu tượng người dùng mặc định"</string>
     <string name="physical_keyboard_title" msgid="4811935435315835220">"Bàn phím thực"</string>
diff --git a/packages/SettingsLib/res/values-zh-rCN/arrays.xml b/packages/SettingsLib/res/values-zh-rCN/arrays.xml
index af87f6f..973d7d0 100644
--- a/packages/SettingsLib/res/values-zh-rCN/arrays.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/arrays.xml
@@ -85,22 +85,10 @@
     <item msgid="7073042887003102964">"map13"</item>
     <item msgid="8147982633566548515">"map14"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="2494959071796102843">"使用系统选择(默认)"</item>
-    <item msgid="4055460186095649420">"SBC"</item>
-    <item msgid="720249083677397051">"AAC"</item>
-    <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> 音频"</item>
-    <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> 音频"</item>
-    <item msgid="3825367753087348007">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="8868109554557331312">"使用系统选择(默认)"</item>
-    <item msgid="9024885861221697796">"SBC"</item>
-    <item msgid="4688890470703790013">"AAC"</item>
-    <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> 音频"</item>
-    <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> 音频"</item>
-    <item msgid="2553206901068987657">"LDAC"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
     <item msgid="926809261293414607">"使用系统选择(默认)"</item>
     <item msgid="8003118270854840095">"44.1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml
index f9e087b..2bc8a30 100644
--- a/packages/SettingsLib/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml
@@ -117,10 +117,8 @@
     <string name="bluetooth_profile_opp" msgid="6692618568149493430">"文件传输"</string>
     <string name="bluetooth_profile_hid" msgid="2969922922664315866">"输入设备"</string>
     <string name="bluetooth_profile_pan" msgid="1006235139308318188">"互联网连接"</string>
-    <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
-    <skip />
-    <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
-    <skip />
+    <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"联系人信息和通话记录分享"</string>
+    <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"用于联系人信息和通话记录分享"</string>
     <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"共享互联网连接"</string>
     <string name="bluetooth_profile_map" msgid="8907204701162107271">"短信"</string>
     <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM 卡存取权限"</string>
@@ -661,7 +659,9 @@
     <string name="dream_complication_title_date" msgid="8661176085446135789">"日期"</string>
     <string name="dream_complication_title_weather" msgid="598609151677172783">"天气"</string>
     <string name="dream_complication_title_aqi" msgid="4587552608957834110">"空气质量"</string>
-    <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"投射信息"</string>
+    <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"投放信息"</string>
+    <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+    <skip />
     <string name="avatar_picker_title" msgid="8492884172713170652">"选择个人资料照片"</string>
     <string name="default_user_icon_description" msgid="6554047177298972638">"默认用户图标"</string>
     <string name="physical_keyboard_title" msgid="4811935435315835220">"实体键盘"</string>
diff --git a/packages/SettingsLib/res/values-zh-rHK/arrays.xml b/packages/SettingsLib/res/values-zh-rHK/arrays.xml
index 2099410..87f3825 100644
--- a/packages/SettingsLib/res/values-zh-rHK/arrays.xml
+++ b/packages/SettingsLib/res/values-zh-rHK/arrays.xml
@@ -85,22 +85,10 @@
     <item msgid="7073042887003102964">"map13"</item>
     <item msgid="8147982633566548515">"map14"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="2494959071796102843">"使用系統選擇 (預設)"</item>
-    <item msgid="4055460186095649420">"SBC"</item>
-    <item msgid="720249083677397051">"AAC"</item>
-    <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> 音訊"</item>
-    <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> 音訊"</item>
-    <item msgid="3825367753087348007">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="8868109554557331312">"使用系統選擇 (預設)"</item>
-    <item msgid="9024885861221697796">"SBC"</item>
-    <item msgid="4688890470703790013">"AAC"</item>
-    <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> 音訊"</item>
-    <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> 音訊"</item>
-    <item msgid="2553206901068987657">"LDAC"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
     <item msgid="926809261293414607">"使用系統選擇 (預設)"</item>
     <item msgid="8003118270854840095">"44.1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml
index cd93f71..114bc64 100644
--- a/packages/SettingsLib/res/values-zh-rHK/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml
@@ -117,10 +117,8 @@
     <string name="bluetooth_profile_opp" msgid="6692618568149493430">"檔案傳輸"</string>
     <string name="bluetooth_profile_hid" msgid="2969922922664315866">"輸入裝置"</string>
     <string name="bluetooth_profile_pan" msgid="1006235139308318188">"互聯網連線"</string>
-    <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
-    <skip />
-    <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
-    <skip />
+    <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"分享通訊錄及通話記錄"</string>
+    <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"用於分享通訊錄及通話記錄"</string>
     <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"互聯網連線分享"</string>
     <string name="bluetooth_profile_map" msgid="8907204701162107271">"短訊"</string>
     <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM 卡存取"</string>
@@ -662,6 +660,8 @@
     <string name="dream_complication_title_weather" msgid="598609151677172783">"天氣"</string>
     <string name="dream_complication_title_aqi" msgid="4587552608957834110">"空氣質素"</string>
     <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"投放資料"</string>
+    <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+    <skip />
     <string name="avatar_picker_title" msgid="8492884172713170652">"選擇個人檔案相片"</string>
     <string name="default_user_icon_description" msgid="6554047177298972638">"預設使用者圖示"</string>
     <string name="physical_keyboard_title" msgid="4811935435315835220">"實體鍵盤"</string>
diff --git a/packages/SettingsLib/res/values-zh-rTW/arrays.xml b/packages/SettingsLib/res/values-zh-rTW/arrays.xml
index 24991e3..529287f 100644
--- a/packages/SettingsLib/res/values-zh-rTW/arrays.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/arrays.xml
@@ -85,22 +85,10 @@
     <item msgid="7073042887003102964">"map13"</item>
     <item msgid="8147982633566548515">"map14"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="2494959071796102843">"系統自動選擇 (預設)"</item>
-    <item msgid="4055460186095649420">"SBC"</item>
-    <item msgid="720249083677397051">"AAC"</item>
-    <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> 音訊"</item>
-    <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> 音訊"</item>
-    <item msgid="3825367753087348007">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="8868109554557331312">"系統自動選擇 (預設)"</item>
-    <item msgid="9024885861221697796">"SBC"</item>
-    <item msgid="4688890470703790013">"AAC"</item>
-    <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> 音訊"</item>
-    <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> 音訊"</item>
-    <item msgid="2553206901068987657">"LDAC"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
     <item msgid="926809261293414607">"系統自動選擇 (預設)"</item>
     <item msgid="8003118270854840095">"44.1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml
index 4f36742..a7ae213 100644
--- a/packages/SettingsLib/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml
@@ -117,10 +117,8 @@
     <string name="bluetooth_profile_opp" msgid="6692618568149493430">"檔案傳輸"</string>
     <string name="bluetooth_profile_hid" msgid="2969922922664315866">"輸入裝置"</string>
     <string name="bluetooth_profile_pan" msgid="1006235139308318188">"網際網路連線"</string>
-    <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
-    <skip />
-    <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
-    <skip />
+    <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"分享聯絡人和通話記錄"</string>
+    <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"用於分享聯絡人和通話記錄"</string>
     <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"網際網路連線分享"</string>
     <string name="bluetooth_profile_map" msgid="8907204701162107271">"簡訊"</string>
     <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM 卡存取權"</string>
@@ -662,6 +660,8 @@
     <string name="dream_complication_title_weather" msgid="598609151677172783">"天氣"</string>
     <string name="dream_complication_title_aqi" msgid="4587552608957834110">"空氣品質"</string>
     <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"演出者資訊"</string>
+    <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+    <skip />
     <string name="avatar_picker_title" msgid="8492884172713170652">"選擇個人資料相片"</string>
     <string name="default_user_icon_description" msgid="6554047177298972638">"預設使用者圖示"</string>
     <string name="physical_keyboard_title" msgid="4811935435315835220">"實體鍵盤"</string>
diff --git a/packages/SettingsLib/res/values-zu/arrays.xml b/packages/SettingsLib/res/values-zu/arrays.xml
index 8732b26..59ead86 100644
--- a/packages/SettingsLib/res/values-zu/arrays.xml
+++ b/packages/SettingsLib/res/values-zu/arrays.xml
@@ -85,22 +85,10 @@
     <item msgid="7073042887003102964">"Imephu13"</item>
     <item msgid="8147982633566548515">"Imephu14"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="2494959071796102843">"Sebenzisa ukukhetha kwesistimu (Okuzenzakalelayo)"</item>
-    <item msgid="4055460186095649420">"SBC"</item>
-    <item msgid="720249083677397051">"I-AAC"</item>
-    <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> umsindo"</item>
-    <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> umsindo"</item>
-    <item msgid="3825367753087348007">"I-LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="8868109554557331312">"Sebenzisa ukukhetha kwesistimu (Okuzenzakalelayo)"</item>
-    <item msgid="9024885861221697796">"SBC"</item>
-    <item msgid="4688890470703790013">"I-AAC"</item>
-    <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> umsindo"</item>
-    <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> umsindo"</item>
-    <item msgid="2553206901068987657">"I-LDAC"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
     <item msgid="926809261293414607">"Sebenzisa ukukhetha kwesistimu (Okuzenzakalelayo)"</item>
     <item msgid="8003118270854840095">"44.1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml
index 88c41c5..e037d12 100644
--- a/packages/SettingsLib/res/values-zu/strings.xml
+++ b/packages/SettingsLib/res/values-zu/strings.xml
@@ -117,10 +117,8 @@
     <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Dlulisa ifayela"</string>
     <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Idivaysi yokufakwayo"</string>
     <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Ukufinyelela i-Inthanethi"</string>
-    <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
-    <skip />
-    <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
-    <skip />
+    <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Ukwabelana ngoxhumana nabo nomlando wekholi"</string>
+    <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Ukusetshenziswa kokwabelana ngoxhumana nabo nomlando wekholi"</string>
     <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Ukwabelana ngoxhumano lwe-Inthanethi"</string>
     <string name="bluetooth_profile_map" msgid="8907204701162107271">"Imilayezo yombhalo"</string>
     <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Ukufinyelela kwe-SIM"</string>
@@ -662,6 +660,8 @@
     <string name="dream_complication_title_weather" msgid="598609151677172783">"Isimo sezulu"</string>
     <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Ikhwalithi Yomoya"</string>
     <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Ulwazi Lokusakaza"</string>
+    <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+    <skip />
     <string name="avatar_picker_title" msgid="8492884172713170652">"Khetha isithombe sephrofayela"</string>
     <string name="default_user_icon_description" msgid="6554047177298972638">"Isithonjana somsebenzisi sokuzenzakalelayo"</string>
     <string name="physical_keyboard_title" msgid="4811935435315835220">"Ikhibhodi ephathekayo"</string>
diff --git a/packages/SettingsLib/res/values/arrays.xml b/packages/SettingsLib/res/values/arrays.xml
index 29a1831..663e8e4 100644
--- a/packages/SettingsLib/res/values/arrays.xml
+++ b/packages/SettingsLib/res/values/arrays.xml
@@ -154,6 +154,8 @@
         <item><xliff:g id="qualcomm">Qualcomm®</xliff:g> <xliff:g id="aptx">aptX™</xliff:g> audio</item>
         <item><xliff:g id="qualcomm">Qualcomm®</xliff:g> <xliff:g id="aptx_hd">aptX™ HD</xliff:g> audio</item>
         <item>LDAC</item>
+        <item>LC3</item>
+        <item>Opus</item>
     </string-array>
 
     <!-- Values for Bluetooth Audio Codec selection preference. -->
@@ -164,6 +166,8 @@
         <item>2</item>
         <item>3</item>
         <item>4</item>
+        <item>5</item>
+        <item>6</item>
     </string-array>
 
     <!-- Summaries for Bluetooth Audio Codec selection preference. [CHAR LIMIT=50]-->
@@ -174,6 +178,8 @@
         <item><xliff:g id="qualcomm">Qualcomm®</xliff:g> <xliff:g id="aptx">aptX™</xliff:g> audio</item>
         <item><xliff:g id="qualcomm">Qualcomm®</xliff:g> <xliff:g id="aptx_hd">aptX™ HD</xliff:g> audio</item>
         <item>LDAC</item>
+        <item>LC3</item>
+        <item>Opus</item>
     </string-array>
 
     <!-- Titles for Bluetooth Audio Codec Sample Rate selection preference. [CHAR LIMIT=50] -->
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index a79cbb6..b879223 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1610,6 +1610,9 @@
     <string name="dream_complication_title_cast_info">Cast Info</string>
     <!-- Screensaver overlay which displays home controls. [CHAR LIMIT=20] -->
     <string name="dream_complication_title_home_controls">Home Controls</string>
+    <!-- Screensaver overlay which displays smartspace. [CHAR LIMIT=20] -->
+    <string name="dream_complication_title_smartspace">Smartspace</string>
+
 
     <!-- Title for a screen allowing the user to choose a profile picture. [CHAR LIMIT=NONE] -->
     <string name="avatar_picker_title">Choose a profile picture</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
index df19c67..1940986 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
@@ -43,6 +43,8 @@
 public class A2dpProfile implements LocalBluetoothProfile {
     private static final String TAG = "A2dpProfile";
 
+    private static final int SOURCE_CODEC_TYPE_OPUS = 6; // TODO remove in U
+
     private Context mContext;
 
     private BluetoothA2dp mService;
@@ -328,6 +330,12 @@
            case BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC:
                index = 5;
                break;
+            case BluetoothCodecConfig.SOURCE_CODEC_TYPE_LC3:
+                index = 6;
+                break;
+            case SOURCE_CODEC_TYPE_OPUS: // TODO update in U
+                index = 7;
+                break;
            }
 
         if (index < 0) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java b/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java
index a46e232..2258617 100644
--- a/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java
+++ b/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java
@@ -92,7 +92,8 @@
             COMPLICATION_TYPE_WEATHER,
             COMPLICATION_TYPE_AIR_QUALITY,
             COMPLICATION_TYPE_CAST_INFO,
-            COMPLICATION_TYPE_HOME_CONTROLS
+            COMPLICATION_TYPE_HOME_CONTROLS,
+            COMPLICATION_TYPE_SMARTSPACE
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface ComplicationType {}
@@ -103,6 +104,7 @@
     public static final int COMPLICATION_TYPE_AIR_QUALITY = 4;
     public static final int COMPLICATION_TYPE_CAST_INFO = 5;
     public static final int COMPLICATION_TYPE_HOME_CONTROLS = 6;
+    public static final int COMPLICATION_TYPE_SMARTSPACE = 7;
 
     private final Context mContext;
     private final IDreamManager mDreamManager;
@@ -351,6 +353,9 @@
             case COMPLICATION_TYPE_HOME_CONTROLS:
                 res = R.string.dream_complication_title_home_controls;
                 break;
+            case COMPLICATION_TYPE_SMARTSPACE:
+                res = R.string.dream_complication_title_smartspace;
+                break;
             default:
                 return null;
         }
diff --git a/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java b/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java
index 1a08366..b416738 100644
--- a/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java
@@ -44,6 +44,8 @@
     private final Handler mReceiverHandler;
     private final MobileTelephonyCallback mTelephonyCallback;
 
+    private boolean mListening = false;
+
     /**
      * MobileStatusTracker constructors
      *
@@ -76,6 +78,7 @@
      * Config the MobileStatusTracker to start or stop monitoring platform signals.
      */
     public void setListening(boolean listening) {
+        mListening = listening;
         if (listening) {
             mPhone.registerTelephonyCallback(mReceiverHandler::post, mTelephonyCallback);
         } else {
@@ -83,6 +86,10 @@
         }
     }
 
+    public boolean isListening() {
+        return mListening;
+    }
+
     private void updateDataSim() {
         int activeDataSubId = mDefaults.getActiveDataSubId();
         if (SubscriptionManager.isValidSubscriptionId(activeDataSubId)) {
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 2f36ab9..8f9ced6 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
@@ -23,7 +23,6 @@
 import android.graphics.Color
 import android.graphics.Rect
 import android.os.Looper
-import android.service.dreams.IDreamManager
 import android.util.Log
 import android.util.MathUtils
 import android.view.GhostView
@@ -54,7 +53,7 @@
 class DialogLaunchAnimator
 @JvmOverloads
 constructor(
-    private val dreamManager: IDreamManager,
+    private val callback: Callback,
     private val interactionJankMonitor: InteractionJankMonitor,
     private val launchAnimator: LaunchAnimator = LaunchAnimator(TIMINGS, INTERPOLATORS),
     private val isForTesting: Boolean = false
@@ -126,7 +125,7 @@
         val animatedDialog =
             AnimatedDialog(
                 launchAnimator,
-                dreamManager,
+                callback,
                 interactionJankMonitor,
                 animateFrom,
                 onDialogDismissed = { openedDialogs.remove(it) },
@@ -194,8 +193,12 @@
 
         val dialog = animatedDialog.dialog
 
-        // Don't animate if the dialog is not showing.
-        if (!dialog.isShowing) {
+        // Don't animate if the dialog is not showing or if we are locked and going to show the
+        // bouncer.
+        if (
+            !dialog.isShowing ||
+            (!callback.isUnlocked() && !callback.isShowingAlternateAuthOnUnlock())
+        ) {
             return null
         }
 
@@ -285,6 +288,23 @@
             ?.let { it.touchSurface = it.prepareForStackDismiss() }
         dialog.dismiss()
     }
+
+    interface Callback {
+        /** Whether the device is currently in dreaming (screensaver) mode. */
+        fun isDreaming(): Boolean
+
+        /**
+         * Whether the device is currently unlocked, i.e. if it is *not* on the keyguard or if the
+         * keyguard can be dismissed.
+         */
+        fun isUnlocked(): Boolean
+
+        /**
+         * Whether we are going to show alternate authentication (like UDFPS) instead of the
+         * traditional bouncer when unlocking the device.
+         */
+        fun isShowingAlternateAuthOnUnlock(): Boolean
+    }
 }
 
 /**
@@ -296,7 +316,7 @@
 
 private class AnimatedDialog(
     private val launchAnimator: LaunchAnimator,
-    private val dreamManager: IDreamManager,
+    private val callback: DialogLaunchAnimator.Callback,
     private val interactionJankMonitor: InteractionJankMonitor,
 
     /** The view that triggered the dialog after being tapped. */
@@ -850,7 +870,7 @@
 
         // If we are dreaming, the dialog was probably closed because of that so we don't animate
         // into the touchSurface.
-        if (dreamManager.isDreaming) {
+        if (callback.isDreaming()) {
             return false
         }
 
diff --git a/packages/SystemUI/shared/src/com/android/systemui/flags/Flag.kt b/packages/SystemUI/shared/src/com/android/systemui/flags/Flag.kt
index a8a526a..f7049cf 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/flags/Flag.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/flags/Flag.kt
@@ -48,9 +48,13 @@
     val default: T
 }
 
+/**
+ * Base class for most common boolean flags.
+ *
+ * See [UnreleasedFlag] and [ReleasedFlag] for useful implementations.
+ */
 // Consider using the "parcelize" kotlin library.
-
-data class BooleanFlag @JvmOverloads constructor(
+abstract class BooleanFlag constructor(
     override val id: Int,
     override val default: Boolean = false,
     override val teamfood: Boolean = false,
@@ -60,7 +64,7 @@
     companion object {
         @JvmField
         val CREATOR = object : Parcelable.Creator<BooleanFlag> {
-            override fun createFromParcel(parcel: Parcel) = BooleanFlag(parcel)
+            override fun createFromParcel(parcel: Parcel) = object : BooleanFlag(parcel) {}
             override fun newArray(size: Int) = arrayOfNulls<BooleanFlag>(size)
         }
     }
@@ -80,12 +84,46 @@
     }
 }
 
+/**
+ * A Flag that is is false by default.
+ *
+ * It can be changed or overridden in debug builds but not in release builds.
+ */
+data class UnreleasedFlag @JvmOverloads constructor(
+    override val id: Int,
+    override val teamfood: Boolean = false,
+    override val overridden: Boolean = false
+) : BooleanFlag(id, false, teamfood, overridden)
+
+/**
+ * A Flag that is is true by default.
+ *
+ * It can be changed or overridden in any build, meaning it can be turned off if needed.
+ */
+data class ReleasedFlag @JvmOverloads constructor(
+    override val id: Int,
+    override val teamfood: Boolean = false,
+    override val overridden: Boolean = false
+) : BooleanFlag(id, true, teamfood, overridden)
+
+/**
+ * A Flag that reads its default values from a resource overlay instead of code.
+ *
+ * Prefer [UnreleasedFlag] and [ReleasedFlag].
+ */
 data class ResourceBooleanFlag @JvmOverloads constructor(
     override val id: Int,
     @BoolRes override val resourceId: Int,
     override val teamfood: Boolean = false
 ) : ResourceFlag<Boolean>
 
+/**
+ * A Flag that can reads its overrides from DeviceConfig.
+ *
+ * This is generally useful for flags that come from or are used _outside_ of SystemUI.
+ *
+ * Prefer [UnreleasedFlag] and [ReleasedFlag].
+ */
 data class DeviceConfigBooleanFlag @JvmOverloads constructor(
     override val id: Int,
     override val name: String,
@@ -94,6 +132,13 @@
     override val teamfood: Boolean = false
 ) : DeviceConfigFlag<Boolean>
 
+/**
+ * A Flag that can reads its overrides from System Properties.
+ *
+ * This is generally useful for flags that come from or are used _outside_ of SystemUI.
+ *
+ * Prefer [UnreleasedFlag] and [ReleasedFlag].
+ */
 data class SysPropBooleanFlag @JvmOverloads constructor(
     override val id: Int,
     override val name: String,
diff --git a/packages/SystemUI/src-debug/com/android/systemui/flags/FlagsModule.kt b/packages/SystemUI/src-debug/com/android/systemui/flags/FlagsModule.kt
index 2560284..74bd9c6 100644
--- a/packages/SystemUI/src-debug/com/android/systemui/flags/FlagsModule.kt
+++ b/packages/SystemUI/src-debug/com/android/systemui/flags/FlagsModule.kt
@@ -27,7 +27,8 @@
 import javax.inject.Named
 
 @Module(includes = [
-    SettingsUtilModule::class
+    ServerFlagReaderModule::class,
+    SettingsUtilModule::class,
 ])
 abstract class FlagsModule {
     @Binds
@@ -46,4 +47,4 @@
         @Named(ALL_FLAGS)
         fun providesAllFlags(): Map<Int, Flag<*>> = Flags.collectFlags()
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src-release/com/android/systemui/flags/FlagsModule.kt b/packages/SystemUI/src-release/com/android/systemui/flags/FlagsModule.kt
index ab9e01e..38b5c9a 100644
--- a/packages/SystemUI/src-release/com/android/systemui/flags/FlagsModule.kt
+++ b/packages/SystemUI/src-release/com/android/systemui/flags/FlagsModule.kt
@@ -19,8 +19,8 @@
 import dagger.Binds
 import dagger.Module
 
-@Module
+@Module(includes = [ServerFlagReaderModule::class])
 abstract class FlagsModule {
     @Binds
     abstract fun bindsFeatureFlagRelease(impl: FeatureFlagsRelease): FeatureFlags
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java b/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java
index 23195af..00f1c01 100644
--- a/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java
+++ b/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java
@@ -33,6 +33,7 @@
 import android.view.ViewGroup;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
 import com.android.keyguard.dagger.KeyguardBouncerScope;
 import com.android.systemui.dagger.qualifiers.Main;
 
@@ -208,7 +209,7 @@
             hide();
             if (mKeyguardCallback != null) {
                 mKeyguardCallback.dismiss(/* securityVerified= */ true, userId,
-                        /* bypassSecondaryLockScreen= */true);
+                        /* bypassSecondaryLockScreen= */true, SecurityMode.Invalid);
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
index eb418ff..b8fcb10 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
@@ -179,7 +179,7 @@
             if (dismissKeyguard) {
                 mDismissing = true;
                 mLatencyTracker.onActionStart(LatencyTracker.ACTION_LOCKSCREEN_UNLOCK);
-                getKeyguardSecurityCallback().dismiss(true, userId);
+                getKeyguardSecurityCallback().dismiss(true, userId, getSecurityMode());
             }
         } else {
             if (isValidPassword) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java
index d32219a..db64f05 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java
@@ -90,7 +90,7 @@
                                 Log.i(TAG, "TrustAgent dismissed Keyguard.");
                             }
                             mSecurityCallback.dismiss(false /* authenticated */, userId,
-                                    /* bypassSecondaryLockScreen */ false);
+                                    /* bypassSecondaryLockScreen */ false, SecurityMode.Invalid);
                         } else {
                             mViewMediatorCallback.playTrustedSound();
                         }
@@ -102,9 +102,9 @@
 
         @Override
         public boolean dismiss(boolean authenticated, int targetUserId,
-                boolean bypassSecondaryLockScreen) {
+                boolean bypassSecondaryLockScreen, SecurityMode expectedSecurityMode) {
             return mKeyguardSecurityContainerController.showNextSecurityScreenOrFinish(
-                    authenticated, targetUserId, bypassSecondaryLockScreen);
+                    authenticated, targetUserId, bypassSecondaryLockScreen, expectedSecurityMode);
         }
 
         @Override
@@ -212,7 +212,8 @@
      * @return True if the keyguard is done.
      */
     public boolean dismiss(int targetUserId) {
-        return mSecurityCallback.dismiss(false, targetUserId, false);
+        return mSecurityCallback.dismiss(false, targetUserId, false,
+                getCurrentSecurityMode());
     }
 
     /**
@@ -360,10 +361,10 @@
     }
 
     public boolean handleBackKey() {
-        if (mKeyguardSecurityContainerController.getCurrentSecurityMode()
-                != SecurityMode.None) {
+        SecurityMode securityMode = mKeyguardSecurityContainerController.getCurrentSecurityMode();
+        if (securityMode != SecurityMode.None) {
             mKeyguardSecurityContainerController.dismiss(
-                    false, KeyguardUpdateMonitor.getCurrentUser());
+                    false, KeyguardUpdateMonitor.getCurrentUser(), securityMode);
             return true;
         }
         return false;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
index 98ac640..87300c3 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
@@ -59,10 +59,11 @@
             return false;
         }
         @Override
-        public void dismiss(boolean securityVerified, int targetUserId) { }
+        public void dismiss(boolean securityVerified, int targetUserId,
+                SecurityMode expectedSecurityMode) { }
         @Override
         public void dismiss(boolean authenticated, int targetId,
-                boolean bypassSecondaryLockScreen) { }
+                boolean bypassSecondaryLockScreen, SecurityMode expectedSecurityMode) { }
         @Override
         public void onUserInput() { }
         @Override
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt
index 58e0fb96..0ea1965 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt
@@ -55,10 +55,11 @@
     val faceAuthenticated: Boolean,
     val faceDisabled: Boolean,
     val goingToSleep: Boolean,
-    val keyguardAwake: Boolean,
+    val keyguardAwakeExcludingBouncerShowing: Boolean,
     val keyguardGoingAway: Boolean,
     val listeningForFaceAssistant: Boolean,
     val occludingAppRequestingFaceAuth: Boolean,
+    val onlyFaceEnrolled: Boolean,
     val primaryUser: Boolean,
     val scanningAllowedByStrongAuth: Boolean,
     val secureCameraLaunched: Boolean,
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
index b97d9671..9aa6f03 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
@@ -171,7 +171,7 @@
                 if (dismissKeyguard) {
                     mLockPatternView.setDisplayMode(LockPatternView.DisplayMode.Correct);
                     mLatencyTracker.onActionStart(LatencyTracker.ACTION_LOCKSCREEN_UNLOCK);
-                    getKeyguardSecurityCallback().dismiss(true, userId);
+                    getKeyguardSecurityCallback().dismiss(true, userId, SecurityMode.Pattern);
                 }
             } else {
                 mLockPatternView.setDisplayMode(LockPatternView.DisplayMode.Wrong);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityCallback.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityCallback.java
index e384727..bc72f79 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityCallback.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityCallback.java
@@ -15,14 +15,17 @@
  */
 package com.android.keyguard;
 
+import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
+
 public interface KeyguardSecurityCallback {
 
     /**
      * Dismiss the given security screen.
      * @param securityVerified true if the user correctly entered credentials for the given screen.
      * @param targetUserId a user that needs to be the foreground user at the dismissal completion.
+     * @param expectedSecurityMode The security mode that is invoking this dismiss.
      */
-    void dismiss(boolean securityVerified, int targetUserId);
+    void dismiss(boolean securityVerified, int targetUserId, SecurityMode expectedSecurityMode);
 
     /**
      * Dismiss the given security screen.
@@ -30,8 +33,10 @@
      * @param targetUserId a user that needs to be the foreground user at the dismissal completion.
      * @param bypassSecondaryLockScreen true if the user can bypass the secondary lock screen,
      *                                  if any, during this dismissal.
+     * @param expectedSecurityMode The security mode that is invoking this dismiss.
      */
-    void dismiss(boolean securityVerified, int targetUserId, boolean bypassSecondaryLockScreen);
+    void dismiss(boolean securityVerified, int targetUserId, boolean bypassSecondaryLockScreen,
+            SecurityMode expectedSecurityMode);
 
     /**
      * Manually report user activity to keep the device awake.
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index 8fb622a..f697e25 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -236,7 +236,12 @@
 
     // Used to notify the container when something interesting happens.
     public interface SecurityCallback {
-        boolean dismiss(boolean authenticated, int targetUserId, boolean bypassSecondaryLockScreen);
+        /**
+         * Potentially dismiss the current security screen, after validating that all device
+         * security has been unlocked. Otherwise show the next screen.
+         */
+        boolean dismiss(boolean authenticated, int targetUserId, boolean bypassSecondaryLockScreen,
+                SecurityMode expectedSecurityMode);
 
         void userActivity();
 
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index 0b3fe46..e1957c0 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -156,14 +156,17 @@
         }
 
         @Override
-        public void dismiss(boolean authenticated, int targetId) {
-            dismiss(authenticated, targetId, /* bypassSecondaryLockScreen */ false);
+        public void dismiss(boolean authenticated, int targetId,
+                SecurityMode expectedSecurityMode) {
+            dismiss(authenticated, targetId, /* bypassSecondaryLockScreen */ false,
+                    expectedSecurityMode);
         }
 
         @Override
         public void dismiss(boolean authenticated, int targetId,
-                boolean bypassSecondaryLockScreen) {
-            mSecurityCallback.dismiss(authenticated, targetId, bypassSecondaryLockScreen);
+                boolean bypassSecondaryLockScreen, SecurityMode expectedSecurityMode) {
+            mSecurityCallback.dismiss(authenticated, targetId, bypassSecondaryLockScreen,
+                    expectedSecurityMode);
         }
 
         public boolean isVerifyUnlockOnly() {
@@ -385,8 +388,13 @@
         return mCurrentSecurityMode;
     }
 
-    public void dismiss(boolean authenticated, int targetUserId) {
-        mKeyguardSecurityCallback.dismiss(authenticated, targetUserId);
+    /**
+     * Potentially dismiss the current security screen, after validating that all device
+     * security has been unlocked. Otherwise show the next screen.
+     */
+    public void dismiss(boolean authenticated, int targetUserId,
+            SecurityMode expectedSecurityMode) {
+        mKeyguardSecurityCallback.dismiss(authenticated, targetUserId, expectedSecurityMode);
     }
 
     public void reset() {
@@ -456,12 +464,21 @@
      *     completion.
      * @param bypassSecondaryLockScreen true if the user is allowed to bypass the secondary
      *     secondary lock screen requirement, if any.
+     * @param expectedSecurityMode SecurityMode that is invoking this request. SecurityMode.Invalid
+     *      indicates that no check should be done
      * @return true if keyguard is done
      */
     public boolean showNextSecurityScreenOrFinish(boolean authenticated, int targetUserId,
-            boolean bypassSecondaryLockScreen) {
+            boolean bypassSecondaryLockScreen, SecurityMode expectedSecurityMode) {
 
         if (DEBUG) Log.d(TAG, "showNextSecurityScreenOrFinish(" + authenticated + ")");
+        if (expectedSecurityMode != SecurityMode.Invalid
+                && expectedSecurityMode != getCurrentSecurityMode()) {
+            Log.w(TAG, "Attempted to invoke showNextSecurityScreenOrFinish with securityMode "
+                    + expectedSecurityMode + ", but current mode is " + getCurrentSecurityMode());
+            return false;
+        }
+
         boolean finish = false;
         boolean strongAuth = false;
         int eventSubtype = -1;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
index 2f6fa14..2a2e9bf 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
@@ -142,7 +142,9 @@
     protected void verifyPasswordAndUnlock() {
         String entry = mPasswordEntry.getText();
 
-        if (entry.length() < 4) {
+        // A SIM PIN is 4 to 8 decimal digits according to 
+        // GSM 02.17 version 5.0.1, Section 5.6 PIN Management
+        if ((entry.length() < 4) || (entry.length() > 8)) {
             // otherwise, display a message to the user, and don't submit.
             mMessageAreaController.setMessage(
                     com.android.systemui.R.string.kg_invalid_sim_pin_hint);
@@ -170,7 +172,8 @@
                             mRemainingAttempts = -1;
                             mShowDefaultMessage = true;
                             getKeyguardSecurityCallback().dismiss(
-                                    true, KeyguardUpdateMonitor.getCurrentUser());
+                                    true, KeyguardUpdateMonitor.getCurrentUser(),
+                                    SecurityMode.SimPin);
                         } else {
                             mShowDefaultMessage = false;
                             if (result.getResult() == PinResult.PIN_RESULT_TYPE_INCORRECT) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
index 47aa43b..203f9b6 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
@@ -69,7 +69,8 @@
             if (simState == TelephonyManager.SIM_STATE_READY) {
                 mRemainingAttempts = -1;
                 mShowDefaultMessage = true;
-                getKeyguardSecurityCallback().dismiss(true, KeyguardUpdateMonitor.getCurrentUser());
+                getKeyguardSecurityCallback().dismiss(true, KeyguardUpdateMonitor.getCurrentUser(),
+                        SecurityMode.SimPuk);
             } else {
                 resetState();
             }
@@ -278,7 +279,8 @@
                             mShowDefaultMessage = true;
 
                             getKeyguardSecurityCallback().dismiss(
-                                    true, KeyguardUpdateMonitor.getCurrentUser());
+                                    true, KeyguardUpdateMonitor.getCurrentUser(),
+                                    SecurityMode.SimPuk);
                         } else {
                             mShowDefaultMessage = false;
                             if (result.getResult() == PinResult.PIN_RESULT_TYPE_INCORRECT) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt
index 89d6fb5..acbea1b 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt
@@ -29,7 +29,7 @@
 
 /**
  * Translates items away/towards the hinge when the device is opened/closed. This is controlled by
- * the set of ids, which also dictact which direction to move and when, via a filter function.
+ * the set of ids, which also dictate which direction to move and when, via a filter function.
  */
 @SysUIUnfoldScope
 class KeyguardUnfoldTransition
@@ -55,7 +55,9 @@
                     ViewIdToTranslate(R.id.lockscreen_clock_view, LEFT, filterNever),
                     ViewIdToTranslate(
                         R.id.notification_stack_scroller, RIGHT, filterSplitShadeOnly),
-                    ViewIdToTranslate(R.id.wallet_button, RIGHT, filterNever)),
+                    ViewIdToTranslate(R.id.wallet_button, RIGHT, filterNever),
+                    ViewIdToTranslate(R.id.start_button, LEFT, filterNever),
+                    ViewIdToTranslate(R.id.end_button, RIGHT, filterNever)),
             progressProvider = unfoldProgressProvider)
     }
 
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 489b4be..8b5e3c1 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -213,7 +213,8 @@
      * If no cancel signal has been received after this amount of time, set the biometric running
      * state to stopped to allow Keyguard to retry authentication.
      */
-    private static final int DEFAULT_CANCEL_SIGNAL_TIMEOUT = 3000;
+    @VisibleForTesting
+    protected static final int DEFAULT_CANCEL_SIGNAL_TIMEOUT = 3000;
 
     private static final ComponentName FALLBACK_HOME_COMPONENT = new ComponentName(
             "com.android.settings", "com.android.settings.FallbackHome");
@@ -316,10 +317,15 @@
     private static final int HAL_ERROR_RETRY_TIMEOUT = 500; // ms
     private static final int HAL_ERROR_RETRY_MAX = 20;
 
-    private final Runnable mFpCancelNotReceived = this::onFingerprintCancelNotReceived;
+    @VisibleForTesting
+    protected final Runnable mFpCancelNotReceived = this::onFingerprintCancelNotReceived;
 
     private final Runnable mFaceCancelNotReceived = this::onFaceCancelNotReceived;
 
+    @VisibleForTesting
+    protected Handler getHandler() {
+        return mHandler;
+    }
     private final Handler mHandler;
 
     private SparseBooleanArray mBiometricEnabledForUser = new SparseBooleanArray();
@@ -707,6 +713,11 @@
 
     private void handleFingerprintAuthFailed() {
         Assert.isMainThread();
+        if (mHandler.hasCallbacks(mFpCancelNotReceived)) {
+            Log.d(TAG, "handleFingerprintAuthFailed()"
+                    + " triggered while waiting for cancellation, removing watchdog");
+            mHandler.removeCallbacks(mFpCancelNotReceived);
+        }
         for (int i = 0; i < mCallbacks.size(); i++) {
             KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
             if (cb != null) {
@@ -737,6 +748,11 @@
 
     private void handleFingerprintAuthenticated(int authUserId, boolean isStrongBiometric) {
         Trace.beginSection("KeyGuardUpdateMonitor#handlerFingerPrintAuthenticated");
+        if (mHandler.hasCallbacks(mFpCancelNotReceived)) {
+            Log.d(TAG, "handleFingerprintAuthenticated()"
+                    + " triggered while waiting for cancellation, removing watchdog");
+            mHandler.removeCallbacks(mFpCancelNotReceived);
+        }
         try {
             final int userId;
             try {
@@ -2458,8 +2474,11 @@
         }
 
         final boolean statusBarShadeLocked = mStatusBarState == StatusBarState.SHADE_LOCKED;
-        final boolean awakeKeyguard = mKeyguardIsVisible && mDeviceInteractive && !mGoingToSleep
-                && !statusBarShadeLocked;
+        // mKeyguardIsVisible is true even when the bouncer is shown, we don't want to run face auth
+        // on bouncer if both fp and fingerprint are enrolled.
+        final boolean awakeKeyguardExcludingBouncerShowing = mKeyguardIsVisible
+                && mDeviceInteractive && !mGoingToSleep
+                && !statusBarShadeLocked && !mBouncerFullyShown;
         final int user = getCurrentUser();
         final int strongAuth = mStrongAuthTracker.getStrongAuthForUser(user);
         final boolean isLockDown =
@@ -2499,14 +2518,15 @@
         final boolean faceDisabledForUser = isFaceDisabled(user);
         final boolean biometricEnabledForUser = mBiometricEnabledForUser.get(user);
         final boolean shouldListenForFaceAssistant = shouldListenForFaceAssistant();
+        final boolean onlyFaceEnrolled = isOnlyFaceEnrolled();
 
         // Only listen if this KeyguardUpdateMonitor belongs to the primary user. There is an
         // instance of KeyguardUpdateMonitor for each user but KeyguardUpdateMonitor is user-aware.
         final boolean shouldListen =
-                (mBouncerFullyShown && !mGoingToSleep
+                ((mBouncerFullyShown && !mGoingToSleep && onlyFaceEnrolled)
                         || mAuthInterruptActive
                         || mOccludingAppRequestingFace
-                        || awakeKeyguard
+                        || awakeKeyguardExcludingBouncerShowing
                         || shouldListenForFaceAssistant
                         || mAuthController.isUdfpsFingerDown()
                         || mUdfpsBouncerShowing)
@@ -2530,10 +2550,11 @@
                     faceAuthenticated,
                     faceDisabledForUser,
                     mGoingToSleep,
-                    awakeKeyguard,
+                    awakeKeyguardExcludingBouncerShowing,
                     mKeyguardGoingAway,
                     shouldListenForFaceAssistant,
                     mOccludingAppRequestingFace,
+                    onlyFaceEnrolled,
                     mIsPrimaryUser,
                     strongAuthAllowsScanning,
                     mSecureCameraLaunched,
@@ -2543,6 +2564,11 @@
         return shouldListen;
     }
 
+    private boolean isOnlyFaceEnrolled() {
+        return isFaceAuthEnabledForUser(getCurrentUser())
+                && !isUnlockWithFingerprintPossible(getCurrentUser());
+    }
+
     private void maybeLogListenerModelData(KeyguardListenModel model) {
         mLogger.logKeyguardListenerModel(model);
 
diff --git a/packages/SystemUI/src/com/android/systemui/analytics/DataCollector.java b/packages/SystemUI/src/com/android/systemui/analytics/DataCollector.java
deleted file mode 100644
index 0db5bef..0000000
--- a/packages/SystemUI/src/com/android/systemui/analytics/DataCollector.java
+++ /dev/null
@@ -1,506 +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 com.android.systemui.analytics;
-
-import static com.android.systemui.statusbar.phone.nano.TouchAnalyticsProto.Session;
-import static com.android.systemui.statusbar.phone.nano.TouchAnalyticsProto.Session.PhoneEvent;
-
-import android.content.Context;
-import android.database.ContentObserver;
-import android.hardware.Sensor;
-import android.hardware.SensorEvent;
-import android.hardware.SensorEventListener;
-import android.net.Uri;
-import android.os.AsyncTask;
-import android.os.Build;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.UserHandle;
-import android.provider.Settings;
-import android.util.Log;
-import android.view.MotionEvent;
-import android.widget.Toast;
-
-import com.android.systemui.Dependency;
-import com.android.systemui.plugins.FalsingPlugin;
-import com.android.systemui.plugins.PluginListener;
-import com.android.systemui.shared.plugins.PluginManager;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-
-/**
- * Tracks touch, sensor and phone events when the lockscreen is on. If the phone is unlocked
- * the data containing these events is saved to a file. This data is collected
- * to analyze how a human interaction looks like.
- *
- * A session starts when the screen is turned on.
- * A session ends when the screen is turned off or user unlocks the phone.
- */
-public class DataCollector implements SensorEventListener {
-    private static final String TAG = "DataCollector";
-    private static final String COLLECTOR_ENABLE = "data_collector_enable";
-    private static final String COLLECT_BAD_TOUCHES = "data_collector_collect_bad_touches";
-    private static final String ALLOW_REJECTED_TOUCH_REPORTS =
-            "data_collector_allow_rejected_touch_reports";
-    private static final String DISABLE_UNLOCKING_FOR_FALSING_COLLECTION =
-            "data_collector_disable_unlocking";
-
-    private static final long TIMEOUT_MILLIS = 11000; // 11 seconds.
-    public static final boolean DEBUG = false;
-
-    private final Handler mHandler = new Handler(Looper.getMainLooper());
-    private final Context mContext;
-
-    // Err on the side of caution, so logging is not started after a crash even tough the screen
-    // is off.
-    private SensorLoggerSession mCurrentSession = null;
-
-    private boolean mEnableCollector = false;
-    private boolean mCollectBadTouches = false;
-    private boolean mCornerSwiping = false;
-    private boolean mTrackingStarted = false;
-    private boolean mAllowReportRejectedTouch = false;
-    private boolean mDisableUnlocking = false;
-
-    private static DataCollector sInstance = null;
-
-    private FalsingPlugin mFalsingPlugin = null;
-
-    protected final ContentObserver mSettingsObserver = new ContentObserver(mHandler) {
-        @Override
-        public void onChange(boolean selfChange) {
-            updateConfiguration();
-        }
-    };
-
-    private final PluginListener mPluginListener = new PluginListener<FalsingPlugin>() {
-        public void onPluginConnected(FalsingPlugin plugin, Context context) {
-            mFalsingPlugin = plugin;
-        }
-
-        public void onPluginDisconnected(FalsingPlugin plugin) {
-            mFalsingPlugin = null;
-        }
-    };
-
-    private DataCollector(Context context) {
-        mContext = context;
-
-        mContext.getContentResolver().registerContentObserver(
-                Settings.Secure.getUriFor(COLLECTOR_ENABLE), false,
-                mSettingsObserver,
-                UserHandle.USER_ALL);
-
-        mContext.getContentResolver().registerContentObserver(
-                Settings.Secure.getUriFor(COLLECT_BAD_TOUCHES), false,
-                mSettingsObserver,
-                UserHandle.USER_ALL);
-
-        mContext.getContentResolver().registerContentObserver(
-                Settings.Secure.getUriFor(ALLOW_REJECTED_TOUCH_REPORTS), false,
-                mSettingsObserver,
-                UserHandle.USER_ALL);
-
-        mContext.getContentResolver().registerContentObserver(
-                Settings.Secure.getUriFor(DISABLE_UNLOCKING_FOR_FALSING_COLLECTION), false,
-                mSettingsObserver,
-                UserHandle.USER_ALL);
-
-        updateConfiguration();
-
-        Dependency.get(PluginManager.class).addPluginListener(mPluginListener, FalsingPlugin.class);
-    }
-
-    public static DataCollector getInstance(Context context) {
-        if (sInstance == null) {
-            sInstance = new DataCollector(context);
-        }
-        return sInstance;
-    }
-
-    private void updateConfiguration() {
-        mEnableCollector = Build.IS_DEBUGGABLE && 0 != Settings.Secure.getInt(
-                mContext.getContentResolver(),
-                COLLECTOR_ENABLE, 0);
-        mCollectBadTouches = mEnableCollector && 0 != Settings.Secure.getInt(
-                mContext.getContentResolver(),
-                COLLECT_BAD_TOUCHES, 0);
-        mAllowReportRejectedTouch = Build.IS_DEBUGGABLE && 0 != Settings.Secure.getInt(
-                mContext.getContentResolver(),
-                ALLOW_REJECTED_TOUCH_REPORTS, 0);
-        mDisableUnlocking = mEnableCollector && Build.IS_DEBUGGABLE && 0 != Settings.Secure.getInt(
-                mContext.getContentResolver(),
-                DISABLE_UNLOCKING_FOR_FALSING_COLLECTION, 0);
-    }
-
-    private boolean sessionEntrypoint() {
-        if (isEnabled() && mCurrentSession == null) {
-            onSessionStart();
-            return true;
-        }
-        return false;
-    }
-
-    private void sessionExitpoint(int result) {
-        if (mCurrentSession != null) {
-            onSessionEnd(result);
-        }
-    }
-
-    private void onSessionStart() {
-        mCornerSwiping = false;
-        mTrackingStarted = false;
-        mCurrentSession = new SensorLoggerSession(System.currentTimeMillis(), System.nanoTime());
-    }
-
-    private void onSessionEnd(int result) {
-        SensorLoggerSession session = mCurrentSession;
-        mCurrentSession = null;
-
-        if (mEnableCollector || mDisableUnlocking) {
-            session.end(System.currentTimeMillis(), result);
-            queueSession(session);
-        }
-    }
-
-    public Uri reportRejectedTouch() {
-        if (mCurrentSession == null) {
-            Toast.makeText(mContext, "Generating rejected touch report failed: session timed out.",
-                    Toast.LENGTH_LONG).show();
-            return null;
-        }
-        SensorLoggerSession currentSession = mCurrentSession;
-
-        currentSession.setType(Session.REJECTED_TOUCH_REPORT);
-        currentSession.end(System.currentTimeMillis(), Session.SUCCESS);
-        Session proto = currentSession.toProto();
-
-        byte[] b = Session.toByteArray(proto);
-        File dir = new File(mContext.getExternalCacheDir(), "rejected_touch_reports");
-        dir.mkdir();
-        File touch = new File(dir, "rejected_touch_report_" + System.currentTimeMillis());
-
-        try {
-            new FileOutputStream(touch).write(b);
-        } catch (IOException e) {
-            throw new RuntimeException(e);
-        }
-
-        return Uri.fromFile(touch);
-    }
-
-    private void queueSession(final SensorLoggerSession currentSession) {
-        AsyncTask.execute(new Runnable() {
-            @Override
-            public void run() {
-                byte[] b = Session.toByteArray(currentSession.toProto());
-
-                if (mFalsingPlugin != null) {
-                    mFalsingPlugin.dataCollected(currentSession.getResult() == Session.SUCCESS, b);
-                } else {
-                    String dir = mContext.getFilesDir().getAbsolutePath();
-                    if (currentSession.getResult() != Session.SUCCESS) {
-                        if (!mDisableUnlocking && !mCollectBadTouches) {
-                            return;
-                        }
-                        dir += "/bad_touches";
-                    } else if (!mDisableUnlocking) {
-                        dir += "/good_touches";
-                    }
-
-                    File file = new File(dir);
-                    file.mkdir();
-                    File touch = new File(file, "trace_" + System.currentTimeMillis());
-                    try {
-                        new FileOutputStream(touch).write(b);
-                    } catch (IOException e) {
-                        throw new RuntimeException(e);
-                    }
-                }
-            }
-        });
-    }
-
-    @Override
-    public synchronized void onSensorChanged(SensorEvent event) {
-        if (isEnabled() && mCurrentSession != null) {
-            mCurrentSession.addSensorEvent(event, System.nanoTime());
-        }
-    }
-
-    @Override
-    public void onAccuracyChanged(Sensor sensor, int accuracy) {
-    }
-
-    /**
-     * @return true if data is being collected - either for data gathering or creating a
-     *         rejected touch report.
-     */
-    public boolean isEnabled() {
-        return mEnableCollector || mAllowReportRejectedTouch || mDisableUnlocking;
-    }
-
-    public boolean isUnlockingDisabled() {
-        return mDisableUnlocking;
-    }
-    /**
-     * @return true if the full data set for data gathering should be collected - including
-     *         extensive sensor data, which is is not normally included with rejected touch reports.
-     */
-    public boolean isEnabledFull() {
-        return mEnableCollector;
-    }
-
-    public void onScreenTurningOn() {
-        if (sessionEntrypoint()) {
-            if (DEBUG) {
-                Log.d(TAG, "onScreenTurningOn");
-            }
-            addEvent(PhoneEvent.ON_SCREEN_ON);
-        }
-    }
-
-    public void onScreenOnFromTouch() {
-        if (sessionEntrypoint()) {
-            if (DEBUG) {
-                Log.d(TAG, "onScreenOnFromTouch");
-            }
-            addEvent(PhoneEvent.ON_SCREEN_ON_FROM_TOUCH);
-        }
-    }
-
-    public void onScreenOff() {
-        if (DEBUG) {
-            Log.d(TAG, "onScreenOff");
-        }
-        addEvent(PhoneEvent.ON_SCREEN_OFF);
-        sessionExitpoint(Session.FAILURE);
-    }
-
-    public void onSucccessfulUnlock() {
-        if (DEBUG) {
-            Log.d(TAG, "onSuccessfulUnlock");
-        }
-        addEvent(PhoneEvent.ON_SUCCESSFUL_UNLOCK);
-        sessionExitpoint(Session.SUCCESS);
-    }
-
-    public void onBouncerShown() {
-        if (DEBUG) {
-            Log.d(TAG, "onBouncerShown");
-        }
-        addEvent(PhoneEvent.ON_BOUNCER_SHOWN);
-    }
-
-    public void onBouncerHidden() {
-        if (DEBUG) {
-            Log.d(TAG, "onBouncerHidden");
-        }
-        addEvent(PhoneEvent.ON_BOUNCER_HIDDEN);
-    }
-
-    public void onQsDown() {
-        if (DEBUG) {
-            Log.d(TAG, "onQsDown");
-        }
-        addEvent(PhoneEvent.ON_QS_DOWN);
-    }
-
-    public void setQsExpanded(boolean expanded) {
-        if (DEBUG) {
-            Log.d(TAG, "setQsExpanded = " + expanded);
-        }
-        if (expanded) {
-            addEvent(PhoneEvent.SET_QS_EXPANDED_TRUE);
-        } else {
-            addEvent(PhoneEvent.SET_QS_EXPANDED_FALSE);
-        }
-    }
-
-    public void onTrackingStarted() {
-        if (DEBUG) {
-            Log.d(TAG, "onTrackingStarted");
-        }
-        mTrackingStarted = true;
-        addEvent(PhoneEvent.ON_TRACKING_STARTED);
-    }
-
-    public void onTrackingStopped() {
-        if (mTrackingStarted) {
-            if (DEBUG) {
-                Log.d(TAG, "onTrackingStopped");
-            }
-            mTrackingStarted = false;
-            addEvent(PhoneEvent.ON_TRACKING_STOPPED);
-        }
-    }
-
-    public void onNotificationActive() {
-        if (DEBUG) {
-            Log.d(TAG, "onNotificationActive");
-        }
-        addEvent(PhoneEvent.ON_NOTIFICATION_ACTIVE);
-    }
-
-
-    public void onNotificationDoubleTap() {
-        if (DEBUG) {
-            Log.d(TAG, "onNotificationDoubleTap");
-        }
-        addEvent(PhoneEvent.ON_NOTIFICATION_DOUBLE_TAP);
-    }
-
-    public void setNotificationExpanded() {
-        if (DEBUG) {
-            Log.d(TAG, "setNotificationExpanded");
-        }
-        addEvent(PhoneEvent.SET_NOTIFICATION_EXPANDED);
-    }
-
-    public void onNotificatonStartDraggingDown() {
-        if (DEBUG) {
-            Log.d(TAG, "onNotificationStartDraggingDown");
-        }
-        addEvent(PhoneEvent.ON_NOTIFICATION_START_DRAGGING_DOWN);
-    }
-
-    public void onStartExpandingFromPulse() {
-        if (DEBUG) {
-            Log.d(TAG, "onStartExpandingFromPulse");
-        }
-        // TODO: maybe add event
-    }
-
-    public void onExpansionFromPulseStopped() {
-        if (DEBUG) {
-            Log.d(TAG, "onExpansionFromPulseStopped");
-        }
-        // TODO: maybe add event
-    }
-
-    public void onNotificatonStopDraggingDown() {
-        if (DEBUG) {
-            Log.d(TAG, "onNotificationStopDraggingDown");
-        }
-        addEvent(PhoneEvent.ON_NOTIFICATION_STOP_DRAGGING_DOWN);
-    }
-
-    public void onNotificationDismissed() {
-        if (DEBUG) {
-            Log.d(TAG, "onNotificationDismissed");
-        }
-        addEvent(PhoneEvent.ON_NOTIFICATION_DISMISSED);
-    }
-
-    public void onNotificatonStartDismissing() {
-        if (DEBUG) {
-            Log.d(TAG, "onNotificationStartDismissing");
-        }
-        addEvent(PhoneEvent.ON_NOTIFICATION_START_DISMISSING);
-    }
-
-    public void onNotificatonStopDismissing() {
-        if (DEBUG) {
-            Log.d(TAG, "onNotificationStopDismissing");
-        }
-        addEvent(PhoneEvent.ON_NOTIFICATION_STOP_DISMISSING);
-    }
-
-    public void onCameraOn() {
-        if (DEBUG) {
-            Log.d(TAG, "onCameraOn");
-        }
-        addEvent(PhoneEvent.ON_CAMERA_ON);
-    }
-
-    public void onLeftAffordanceOn() {
-        if (DEBUG) {
-            Log.d(TAG, "onLeftAffordanceOn");
-        }
-        addEvent(PhoneEvent.ON_LEFT_AFFORDANCE_ON);
-    }
-
-    public void onAffordanceSwipingStarted(boolean rightCorner) {
-        if (DEBUG) {
-            Log.d(TAG, "onAffordanceSwipingStarted");
-        }
-        mCornerSwiping = true;
-        if (rightCorner) {
-            addEvent(PhoneEvent.ON_RIGHT_AFFORDANCE_SWIPING_STARTED);
-        } else {
-            addEvent(PhoneEvent.ON_LEFT_AFFORDANCE_SWIPING_STARTED);
-        }
-    }
-
-    public void onAffordanceSwipingAborted() {
-        if (mCornerSwiping) {
-            if (DEBUG) {
-                Log.d(TAG, "onAffordanceSwipingAborted");
-            }
-            mCornerSwiping = false;
-            addEvent(PhoneEvent.ON_AFFORDANCE_SWIPING_ABORTED);
-        }
-    }
-
-    public void onUnlockHintStarted() {
-        if (DEBUG) {
-            Log.d(TAG, "onUnlockHintStarted");
-        }
-        addEvent(PhoneEvent.ON_UNLOCK_HINT_STARTED);
-    }
-
-    public void onCameraHintStarted() {
-        if (DEBUG) {
-            Log.d(TAG, "onCameraHintStarted");
-        }
-        addEvent(PhoneEvent.ON_CAMERA_HINT_STARTED);
-    }
-
-    public void onLeftAffordanceHintStarted() {
-        if (DEBUG) {
-            Log.d(TAG, "onLeftAffordanceHintStarted");
-        }
-        addEvent(PhoneEvent.ON_LEFT_AFFORDANCE_HINT_STARTED);
-    }
-
-    public void onTouchEvent(MotionEvent event, int width, int height) {
-        if (mCurrentSession != null) {
-            if (DEBUG) {
-                Log.v(TAG, "onTouchEvent(ev.action="
-                        + MotionEvent.actionToString(event.getAction()) + ")");
-            }
-            mCurrentSession.addMotionEvent(event);
-            mCurrentSession.setTouchArea(width, height);
-        }
-    }
-
-    private void addEvent(int eventType) {
-        if (isEnabled() && mCurrentSession != null) {
-            mCurrentSession.addPhoneEvent(eventType, System.nanoTime());
-        }
-    }
-
-    public boolean isReportingEnabled() {
-        return mAllowReportRejectedTouch;
-    }
-
-    public void onFalsingSessionStarted() {
-        sessionEntrypoint();
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/analytics/SensorLoggerSession.java b/packages/SystemUI/src/com/android/systemui/analytics/SensorLoggerSession.java
deleted file mode 100644
index d294012..0000000
--- a/packages/SystemUI/src/com/android/systemui/analytics/SensorLoggerSession.java
+++ /dev/null
@@ -1,165 +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 com.android.systemui.analytics;
-
-import static com.android.systemui.statusbar.phone.nano.TouchAnalyticsProto.Session;
-import static com.android.systemui.statusbar.phone.nano.TouchAnalyticsProto.Session.PhoneEvent;
-import static com.android.systemui.statusbar.phone.nano.TouchAnalyticsProto.Session.SensorEvent;
-import static com.android.systemui.statusbar.phone.nano.TouchAnalyticsProto.Session.TouchEvent;
-
-import android.os.Build;
-import android.util.Log;
-import android.view.MotionEvent;
-
-import java.util.ArrayList;
-
-/**
- * Collects touch, sensor and phone events and converts the data to
- * TouchAnalyticsProto.Session.
- */
-public class SensorLoggerSession {
-    private static final String TAG = "SensorLoggerSession";
-
-    private final long mStartTimestampMillis;
-    private final long mStartSystemTimeNanos;
-
-    private long mEndTimestampMillis;
-    private int mType;
-
-    private ArrayList<TouchEvent> mMotionEvents = new ArrayList<>();
-    private ArrayList<SensorEvent> mSensorEvents = new ArrayList<>();
-    private ArrayList<PhoneEvent> mPhoneEvents = new ArrayList<>();
-    private int mTouchAreaHeight;
-    private int mTouchAreaWidth;
-    private int mResult = Session.UNKNOWN;
-
-    public SensorLoggerSession(long startTimestampMillis, long startSystemTimeNanos) {
-        mStartTimestampMillis = startTimestampMillis;
-        mStartSystemTimeNanos = startSystemTimeNanos;
-        mType = Session.REAL;
-    }
-
-    public void setType(int type) {
-        mType = type;
-    }
-
-    public void end(long endTimestampMillis, int result) {
-        mResult = result;
-        mEndTimestampMillis = endTimestampMillis;
-
-        if (DataCollector.DEBUG) {
-            Log.d(TAG, "Ending session result=" + result + " it lasted for " +
-                    (float) (mEndTimestampMillis - mStartTimestampMillis) / 1000f + "s");
-        }
-    }
-
-    public void addMotionEvent(MotionEvent motionEvent) {
-        TouchEvent event = motionEventToProto(motionEvent);
-        mMotionEvents.add(event);
-    }
-
-    public void addSensorEvent(android.hardware.SensorEvent eventOrig, long systemTimeNanos) {
-        SensorEvent event = sensorEventToProto(eventOrig, systemTimeNanos);
-        mSensorEvents.add(event);
-    }
-
-    public void addPhoneEvent(int eventType, long systemTimeNanos) {
-        PhoneEvent event = phoneEventToProto(eventType, systemTimeNanos);
-        mPhoneEvents.add(event);
-    }
-
-
-    @Override
-    public String toString() {
-        final StringBuilder sb = new StringBuilder("Session{");
-        sb.append("mStartTimestampMillis=").append(mStartTimestampMillis);
-        sb.append(", mStartSystemTimeNanos=").append(mStartSystemTimeNanos);
-        sb.append(", mEndTimestampMillis=").append(mEndTimestampMillis);
-        sb.append(", mResult=").append(mResult);
-        sb.append(", mTouchAreaHeight=").append(mTouchAreaHeight);
-        sb.append(", mTouchAreaWidth=").append(mTouchAreaWidth);
-        sb.append(", mMotionEvents=[size=").append(mMotionEvents.size()).append("]");
-        sb.append(", mSensorEvents=[size=").append(mSensorEvents.size()).append("]");
-        sb.append(", mPhoneEvents=[size=").append(mPhoneEvents.size()).append("]");
-        sb.append('}');
-        return sb.toString();
-    }
-
-    public Session toProto() {
-        Session proto = new Session();
-        proto.startTimestampMillis = mStartTimestampMillis;
-        proto.durationMillis = mEndTimestampMillis - mStartTimestampMillis;
-        proto.build = Build.FINGERPRINT;
-        proto.deviceId = Build.DEVICE;
-        proto.result = mResult;
-        proto.type = mType;
-        proto.sensorEvents = mSensorEvents.toArray(proto.sensorEvents);
-        proto.touchEvents = mMotionEvents.toArray(proto.touchEvents);
-        proto.phoneEvents = mPhoneEvents.toArray(proto.phoneEvents);
-        proto.touchAreaWidth = mTouchAreaWidth;
-        proto.touchAreaHeight = mTouchAreaHeight;
-        return proto;
-    }
-
-    private PhoneEvent phoneEventToProto(int eventType, long sysTimeNanos) {
-        PhoneEvent proto = new PhoneEvent();
-        proto.type = eventType;
-        proto.timeOffsetNanos = sysTimeNanos - mStartSystemTimeNanos;
-        return proto;
-    }
-
-    private SensorEvent sensorEventToProto(android.hardware.SensorEvent ev, long sysTimeNanos) {
-        SensorEvent proto = new SensorEvent();
-        proto.type = ev.sensor.getType();
-        proto.timeOffsetNanos = sysTimeNanos - mStartSystemTimeNanos;
-        proto.timestamp = ev.timestamp;
-        proto.values = ev.values.clone();
-        return proto;
-    }
-
-    private TouchEvent motionEventToProto(MotionEvent ev) {
-        int count = ev.getPointerCount();
-        TouchEvent proto = new TouchEvent();
-        proto.timeOffsetNanos = ev.getEventTimeNano() - mStartSystemTimeNanos;
-        proto.action = ev.getActionMasked();
-        proto.actionIndex = ev.getActionIndex();
-        proto.pointers = new TouchEvent.Pointer[count];
-        for (int i = 0; i < count; i++) {
-            TouchEvent.Pointer p = new TouchEvent.Pointer();
-            p.x = ev.getX(i);
-            p.y = ev.getY(i);
-            p.size = ev.getSize(i);
-            p.pressure = ev.getPressure(i);
-            p.id = ev.getPointerId(i);
-            proto.pointers[i] = p;
-        }
-        return proto;
-    }
-
-    public void setTouchArea(int width, int height) {
-        mTouchAreaWidth = width;
-        mTouchAreaHeight = height;
-    }
-
-    public int getResult() {
-        return mResult;
-    }
-
-    public long getStartTimestampMillis() {
-        return mStartTimestampMillis;
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java
index 835025b..e82d0ea 100644
--- a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java
+++ b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java
@@ -16,8 +16,6 @@
 
 package com.android.systemui.charging;
 
-import static com.android.systemui.charging.WirelessChargingLayout.UNKNOWN_BATTERY_LEVEL;
-
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
@@ -32,13 +30,14 @@
 
 import com.android.internal.logging.UiEvent;
 import com.android.internal.logging.UiEventLogger;
+import com.android.systemui.ripple.RippleShader.RippleShape;
 
 /**
  * A WirelessChargingAnimation is a view containing view + animation for wireless charging.
  * @hide
  */
 public class WirelessChargingAnimation {
-
+    public static final int UNKNOWN_BATTERY_LEVEL = -1;
     public static final long DURATION = 1500;
     private static final String TAG = "WirelessChargingView";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
@@ -58,11 +57,12 @@
      * before calling {@link #show} - can be done through {@link #makeWirelessChargingAnimation}.
      * @hide
      */
-    public WirelessChargingAnimation(@NonNull Context context, @Nullable Looper looper,
+    private WirelessChargingAnimation(@NonNull Context context, @Nullable Looper looper,
             int transmittingBatteryLevel, int batteryLevel, Callback callback, boolean isDozing,
-            UiEventLogger uiEventLogger) {
+            RippleShape rippleShape, UiEventLogger uiEventLogger) {
         mCurrentWirelessChargingView = new WirelessChargingView(context, looper,
-                transmittingBatteryLevel, batteryLevel, callback, isDozing, uiEventLogger);
+                transmittingBatteryLevel, batteryLevel, callback, isDozing,
+                rippleShape, uiEventLogger);
     }
 
     /**
@@ -72,9 +72,10 @@
      */
     public static WirelessChargingAnimation makeWirelessChargingAnimation(@NonNull Context context,
             @Nullable Looper looper, int transmittingBatteryLevel, int batteryLevel,
-            Callback callback, boolean isDozing, UiEventLogger uiEventLogger) {
+            Callback callback, boolean isDozing, RippleShape rippleShape,
+            UiEventLogger uiEventLogger) {
         return new WirelessChargingAnimation(context, looper, transmittingBatteryLevel,
-                batteryLevel, callback, isDozing, uiEventLogger);
+                batteryLevel, callback, isDozing, rippleShape, uiEventLogger);
     }
 
     /**
@@ -82,9 +83,10 @@
      * battery level without charging number shown.
      */
     public static WirelessChargingAnimation makeChargingAnimationWithNoBatteryLevel(
-            @NonNull Context context, UiEventLogger uiEventLogger) {
+            @NonNull Context context, RippleShape rippleShape, UiEventLogger uiEventLogger) {
         return makeWirelessChargingAnimation(context, null,
-                UNKNOWN_BATTERY_LEVEL, UNKNOWN_BATTERY_LEVEL, null, false, uiEventLogger);
+                UNKNOWN_BATTERY_LEVEL, UNKNOWN_BATTERY_LEVEL, null, false,
+                rippleShape, uiEventLogger);
     }
 
     /**
@@ -121,10 +123,10 @@
 
         public WirelessChargingView(Context context, @Nullable Looper looper,
                 int transmittingBatteryLevel, int batteryLevel, Callback callback,
-                boolean isDozing, UiEventLogger uiEventLogger) {
+                boolean isDozing, RippleShape rippleShape, UiEventLogger uiEventLogger) {
             mCallback = callback;
             mNextView = new WirelessChargingLayout(context, transmittingBatteryLevel, batteryLevel,
-                    isDozing);
+                    isDozing, rippleShape);
             mGravity = Gravity.CENTER_HORIZONTAL | Gravity.CENTER;
             mUiEventLogger = uiEventLogger;
 
diff --git a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java
index 65400c2..47ea27f 100644
--- a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java
@@ -33,7 +33,7 @@
 import com.android.settingslib.Utils;
 import com.android.systemui.R;
 import com.android.systemui.animation.Interpolators;
-import com.android.systemui.ripple.RippleShader;
+import com.android.systemui.ripple.RippleShader.RippleShape;
 import com.android.systemui.ripple.RippleView;
 
 import java.text.NumberFormat;
@@ -41,37 +41,36 @@
 /**
  * @hide
  */
-public class WirelessChargingLayout extends FrameLayout {
-    public static final int UNKNOWN_BATTERY_LEVEL = -1;
+final class WirelessChargingLayout extends FrameLayout {
     private static final long RIPPLE_ANIMATION_DURATION = 1500;
     private static final int SCRIM_COLOR = 0x4C000000;
     private static final int SCRIM_FADE_DURATION = 300;
     private RippleView mRippleView;
 
-    public WirelessChargingLayout(Context context) {
+    WirelessChargingLayout(Context context, int transmittingBatteryLevel, int batteryLevel,
+            boolean isDozing, RippleShape rippleShape) {
         super(context);
-        init(context, null, false);
+        init(context, null, transmittingBatteryLevel, batteryLevel, isDozing, rippleShape);
     }
 
-    public WirelessChargingLayout(Context context, int transmittingBatteryLevel, int batteryLevel,
-            boolean isDozing) {
+    private WirelessChargingLayout(Context context) {
         super(context);
-        init(context, null, transmittingBatteryLevel, batteryLevel, isDozing);
+        init(context, null, /* isDozing= */ false, RippleShape.CIRCLE);
     }
 
-    public WirelessChargingLayout(Context context, AttributeSet attrs) {
+    private WirelessChargingLayout(Context context, AttributeSet attrs) {
         super(context, attrs);
-        init(context, attrs, false);
+        init(context, attrs, /* isDozing= */false, RippleShape.CIRCLE);
     }
 
-    private void init(Context c, AttributeSet attrs, boolean isDozing) {
-        init(c, attrs, -1, -1, false);
+    private void init(Context c, AttributeSet attrs, boolean isDozing, RippleShape rippleShape) {
+        init(c, attrs, -1, -1, isDozing, rippleShape);
     }
 
     private void init(Context context, AttributeSet attrs, int transmittingBatteryLevel,
-            int batteryLevel, boolean isDozing) {
+            int batteryLevel, boolean isDozing, RippleShape rippleShape) {
         final boolean showTransmittingBatteryLevel =
-                (transmittingBatteryLevel != UNKNOWN_BATTERY_LEVEL);
+                (transmittingBatteryLevel != WirelessChargingAnimation.UNKNOWN_BATTERY_LEVEL);
 
         // set style based on background
         int style = R.style.ChargingAnim_WallpaperBackground;
@@ -84,7 +83,7 @@
         // amount of battery:
         final TextView percentage = findViewById(R.id.wireless_charging_percentage);
 
-        if (batteryLevel != UNKNOWN_BATTERY_LEVEL) {
+        if (batteryLevel != WirelessChargingAnimation.UNKNOWN_BATTERY_LEVEL) {
             percentage.setText(NumberFormat.getPercentInstance().format(batteryLevel / 100f));
             percentage.setAlpha(0);
         }
@@ -138,8 +137,7 @@
         animatorSetScrim.start();
 
         mRippleView = findViewById(R.id.wireless_charging_ripple);
-        // TODO: Make rounded box shape if the device is tablet.
-        mRippleView.setupShader(RippleShader.RippleShape.CIRCLE);
+        mRippleView.setupShader(rippleShape);
         OnAttachStateChangeListener listener = new OnAttachStateChangeListener() {
             @Override
             public void onViewAttachedToWindow(View view) {
@@ -233,8 +231,12 @@
             int width = getMeasuredWidth();
             int height = getMeasuredHeight();
             mRippleView.setCenter(width * 0.5f, height * 0.5f);
-            float maxSize = Math.max(width, height);
-            mRippleView.setMaxSize(maxSize, maxSize);
+            if (mRippleView.getRippleShape() == RippleShape.ROUNDED_BOX) {
+                mRippleView.setMaxSize(width * 1.5f, height * 1.5f);
+            } else {
+                float maxSize = Math.max(width, height);
+                mRippleView.setMaxSize(maxSize, maxSize);
+            }
             mRippleView.setColor(Utils.getColorAttr(mRippleView.getContext(),
                     android.R.attr.colorAccent).getDefaultColor());
         }
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
index e16ac08..c21e36a 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
@@ -97,6 +97,7 @@
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
+import androidx.annotation.NonNull;
 import androidx.core.view.ViewCompat;
 import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
 
@@ -132,7 +133,7 @@
     private static final int FONT_SEARCH_STEP_PX = 4;
 
     private final Context mContext;
-    private final UiEventLogger mUiEventLogger;
+    private final ClipboardLogger mClipboardLogger;
     private final BroadcastDispatcher mBroadcastDispatcher;
     private final DisplayManager mDisplayManager;
     private final DisplayMetrics mDisplayMetrics;
@@ -181,7 +182,7 @@
         final Context displayContext = context.createDisplayContext(getDefaultDisplay());
         mContext = displayContext.createWindowContext(TYPE_SCREENSHOT, null);
 
-        mUiEventLogger = uiEventLogger;
+        mClipboardLogger = new ClipboardLogger(uiEventLogger);
 
         mAccessibilityManager = AccessibilityManager.getInstance(mContext);
         mTextClassifier = requireNonNull(context.getSystemService(TextClassificationManager.class))
@@ -231,7 +232,7 @@
 
             @Override
             public void onSwipeDismissInitiated(Animator animator) {
-                mUiEventLogger.log(CLIPBOARD_OVERLAY_SWIPE_DISMISSED);
+                mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_SWIPE_DISMISSED);
                 mExitAnimator = animator;
             }
 
@@ -249,7 +250,7 @@
         });
 
         mDismissButton.setOnClickListener(view -> {
-            mUiEventLogger.log(CLIPBOARD_OVERLAY_DISMISS_TAPPED);
+            mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_DISMISS_TAPPED);
             animateOut();
         });
 
@@ -285,7 +286,8 @@
                                 int newDisplayId) {
                             if (mContext.getResources().getConfiguration().orientation
                                     != mOrientation) {
-                                mUiEventLogger.log(CLIPBOARD_OVERLAY_DISMISSED_OTHER);
+                                mClipboardLogger.logSessionComplete(
+                                        CLIPBOARD_OVERLAY_DISMISSED_OTHER);
                                 hideImmediate();
                             }
                         }
@@ -300,7 +302,7 @@
         });
 
         mTimeoutHandler.setOnTimeoutRunnable(() -> {
-            mUiEventLogger.log(CLIPBOARD_OVERLAY_TIMED_OUT);
+            mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_TIMED_OUT);
             animateOut();
         });
 
@@ -308,7 +310,7 @@
             @Override
             public void onReceive(Context context, Intent intent) {
                 if (ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction())) {
-                    mUiEventLogger.log(CLIPBOARD_OVERLAY_DISMISSED_OTHER);
+                    mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_DISMISSED_OTHER);
                     animateOut();
                 }
             }
@@ -320,7 +322,7 @@
             @Override
             public void onReceive(Context context, Intent intent) {
                 if (SCREENSHOT_ACTION.equals(intent.getAction())) {
-                    mUiEventLogger.log(CLIPBOARD_OVERLAY_DISMISSED_OTHER);
+                    mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_DISMISSED_OTHER);
                     animateOut();
                 }
             }
@@ -390,7 +392,7 @@
                     mContext.getString(R.string.clipboard_send_nearby_description));
             mRemoteCopyChip.setVisibility(View.VISIBLE);
             mRemoteCopyChip.setOnClickListener((v) -> {
-                mUiEventLogger.log(CLIPBOARD_OVERLAY_REMOTE_COPY_TAPPED);
+                mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_REMOTE_COPY_TAPPED);
                 mContext.startActivity(remoteCopyIntent);
                 animateOut();
             });
@@ -450,7 +452,7 @@
         chip.setContentDescription(action.getTitle());
         chip.setIcon(action.getIcon(), false);
         chip.setPendingIntent(action.getActionIntent(), () -> {
-            mUiEventLogger.log(CLIPBOARD_OVERLAY_ACTION_TAPPED);
+            mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_ACTION_TAPPED);
             animateOut();
         });
         chip.setAlpha(1);
@@ -486,7 +488,7 @@
                         touchRegion.op(tmpRect, Region.Op.UNION);
                         if (!touchRegion.contains(
                                 (int) motionEvent.getRawX(), (int) motionEvent.getRawY())) {
-                            mUiEventLogger.log(CLIPBOARD_OVERLAY_TAP_OUTSIDE);
+                            mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_TAP_OUTSIDE);
                             animateOut();
                         }
                     }
@@ -497,7 +499,7 @@
     }
 
     private void editImage(Uri uri) {
-        mUiEventLogger.log(CLIPBOARD_OVERLAY_EDIT_TAPPED);
+        mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_EDIT_TAPPED);
         String editorPackage = mContext.getString(R.string.config_screenshotEditor);
         Intent editIntent = new Intent(Intent.ACTION_EDIT);
         if (!TextUtils.isEmpty(editorPackage)) {
@@ -512,7 +514,7 @@
     }
 
     private void editText() {
-        mUiEventLogger.log(CLIPBOARD_OVERLAY_EDIT_TAPPED);
+        mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_EDIT_TAPPED);
         Intent editIntent = new Intent(mContext, EditTextActivity.class);
         editIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
         mContext.startActivity(editIntent);
@@ -520,13 +522,15 @@
     }
 
     private void shareContent(ClipData clip) {
-        mUiEventLogger.log(CLIPBOARD_OVERLAY_SHARE_TAPPED);
+        mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_SHARE_TAPPED);
         Intent shareIntent = new Intent(Intent.ACTION_SEND);
-        shareIntent.putExtra(Intent.EXTRA_TEXT, clip.getItemAt(0).getText().toString());
         shareIntent.setDataAndType(
                 clip.getItemAt(0).getUri(), clip.getDescription().getMimeType(0));
-        shareIntent.putExtra(Intent.EXTRA_STREAM, clip.getItemAt(0).getUri());
-        shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+        shareIntent.putExtra(Intent.EXTRA_TEXT, clip.getItemAt(0).getText().toString());
+        if (clip.getItemAt(0).getUri() != null) {
+            shareIntent.putExtra(Intent.EXTRA_STREAM, clip.getItemAt(0).getUri());
+            shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+        }
         Intent chooserIntent = Intent.createChooser(shareIntent, null)
                 .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK)
                 .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
@@ -864,6 +868,7 @@
         mRemoteCopyChip.setVisibility(View.GONE);
         resetActionChips();
         mTimeoutHandler.cancelTimeout();
+        mClipboardLogger.reset();
     }
 
     @MainThread
@@ -969,4 +974,24 @@
             mWindowManager.updateViewLayout(decorView, mWindowLayoutParams);
         }
     }
+
+    static class ClipboardLogger {
+        private final UiEventLogger mUiEventLogger;
+        private boolean mGuarded = false;
+
+        ClipboardLogger(UiEventLogger uiEventLogger) {
+            mUiEventLogger = uiEventLogger;
+        }
+
+        void logSessionComplete(@NonNull UiEventLogger.UiEventEnum event) {
+            if (!mGuarded) {
+                mGuarded = true;
+                mUiEventLogger.log(event);
+            }
+        }
+
+        void reset() {
+            mGuarded = false;
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
index de7bf28..55c1806 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
@@ -215,16 +215,15 @@
         final boolean cameraBlocked = mSensorPrivacyController
                 .isSensorBlocked(SensorPrivacyManager.Sensors.CAMERA);
         @DreamOverlayStatusBarView.StatusIconType int iconType = Resources.ID_NULL;
-        if (micBlocked && cameraBlocked) {
-            iconType = DreamOverlayStatusBarView.STATUS_ICON_MIC_CAMERA_DISABLED;
-        } else if (!micBlocked && cameraBlocked) {
-            iconType = DreamOverlayStatusBarView.STATUS_ICON_CAMERA_DISABLED;
-        } else if (micBlocked && !cameraBlocked) {
-            iconType = DreamOverlayStatusBarView.STATUS_ICON_MIC_DISABLED;
-        }
-        if (iconType != Resources.ID_NULL) {
-            showIcon(iconType, true);
-        }
+        showIcon(
+                DreamOverlayStatusBarView.STATUS_ICON_CAMERA_DISABLED,
+                !micBlocked && cameraBlocked);
+        showIcon(
+                DreamOverlayStatusBarView.STATUS_ICON_MIC_DISABLED,
+                micBlocked && !cameraBlocked);
+        showIcon(
+                DreamOverlayStatusBarView.STATUS_ICON_MIC_CAMERA_DISABLED,
+                micBlocked && cameraBlocked);
     }
 
     private String buildNotificationsContentDescription(int notificationCount) {
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/Complication.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/Complication.java
index 5457144..29bb2f4 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/Complication.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/Complication.java
@@ -163,7 +163,8 @@
             COMPLICATION_TYPE_WEATHER,
             COMPLICATION_TYPE_AIR_QUALITY,
             COMPLICATION_TYPE_CAST_INFO,
-            COMPLICATION_TYPE_HOME_CONTROLS
+            COMPLICATION_TYPE_HOME_CONTROLS,
+            COMPLICATION_TYPE_SMARTSPACE
     })
     @Retention(RetentionPolicy.SOURCE)
     @interface ComplicationType {}
@@ -175,6 +176,7 @@
     int COMPLICATION_TYPE_AIR_QUALITY = 1 << 3;
     int COMPLICATION_TYPE_CAST_INFO = 1 << 4;
     int COMPLICATION_TYPE_HOME_CONTROLS = 1 << 5;
+    int COMPLICATION_TYPE_SMARTSPACE = 1 << 6;
 
     /**
      * The {@link Host} interface specifies a way a {@link Complication} to communicate with its
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationUtils.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationUtils.java
index dcab90f..d5db63d 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationUtils.java
@@ -21,6 +21,7 @@
 import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_DATE;
 import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_HOME_CONTROLS;
 import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_NONE;
+import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_SMARTSPACE;
 import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_TIME;
 import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_WEATHER;
 
@@ -51,6 +52,8 @@
                 return COMPLICATION_TYPE_CAST_INFO;
             case DreamBackend.COMPLICATION_TYPE_HOME_CONTROLS:
                 return COMPLICATION_TYPE_HOME_CONTROLS;
+            case DreamBackend.COMPLICATION_TYPE_SMARTSPACE:
+                return COMPLICATION_TYPE_SMARTSPACE;
             default:
                 return COMPLICATION_TYPE_NONE;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/SmartSpaceComplication.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/SmartSpaceComplication.java
index ac6edba..567bdbc 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/SmartSpaceComplication.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/SmartSpaceComplication.java
@@ -52,6 +52,11 @@
         return mViewHolderProvider.get();
     }
 
+    @Override
+    public int getRequiredTypeAvailability() {
+        return COMPLICATION_TYPE_SMARTSPACE;
+    }
+
     /**
      * {@link CoreStartable} responsbile for registering {@link SmartSpaceComplication} with
      * SystemUI.
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.kt b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.kt
index 2cee2520..dfa3bcd 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.kt
@@ -23,7 +23,10 @@
  */
 interface FeatureFlags : FlagListenable {
     /** Returns a boolean value for the given flag.  */
-    fun isEnabled(flag: BooleanFlag): Boolean
+    fun isEnabled(flag: UnreleasedFlag): Boolean
+
+    /** Returns a boolean value for the given flag.  */
+    fun isEnabled(flag: ReleasedFlag): Boolean
 
     /** Returns a boolean value for the given flag.  */
     fun isEnabled(flag: ResourceBooleanFlag): Boolean
@@ -39,4 +42,4 @@
 
     /** Returns a string value for the given flag.  */
     fun getString(flag: ResourceStringFlag): String
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java
index f804325..b4b8795 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java
@@ -48,6 +48,8 @@
 import com.android.systemui.util.DeviceConfigProxy;
 import com.android.systemui.util.settings.SecureSettings;
 
+import org.jetbrains.annotations.NotNull;
+
 import java.io.PrintWriter;
 import java.lang.reflect.Field;
 import java.util.ArrayList;
@@ -84,6 +86,7 @@
     private final Resources mResources;
     private final SystemPropertiesHelper mSystemProperties;
     private final DeviceConfigProxy mDeviceConfigProxy;
+    private final ServerFlagReader mServerFlagReader;
     private final Map<Integer, Flag<?>> mAllFlags;
     private final Map<Integer, Boolean> mBooleanFlagCache = new TreeMap<>();
     private final Map<Integer, String> mStringFlagCache = new TreeMap<>();
@@ -98,6 +101,7 @@
             @Main Resources resources,
             DumpManager dumpManager,
             DeviceConfigProxy deviceConfigProxy,
+            ServerFlagReader serverFlagReader,
             @Named(ALL_FLAGS) Map<Integer, Flag<?>> allFlags,
             CommandRegistry commandRegistry,
             IStatusBarService barService) {
@@ -106,6 +110,7 @@
         mResources = resources;
         mSystemProperties = systemProperties;
         mDeviceConfigProxy = deviceConfigProxy;
+        mServerFlagReader = serverFlagReader;
         mAllFlags = allFlags;
         mBarService = barService;
 
@@ -121,7 +126,16 @@
     }
 
     @Override
-    public boolean isEnabled(@NonNull BooleanFlag flag) {
+    public boolean isEnabled(@NotNull UnreleasedFlag flag) {
+        return isEnabledInternal(flag);
+    }
+
+    @Override
+    public boolean isEnabled(@NotNull ReleasedFlag flag) {
+        return isEnabledInternal(flag);
+    }
+
+    private boolean isEnabledInternal(@NotNull BooleanFlag flag) {
         int id = flag.getId();
         if (!mBooleanFlagCache.containsKey(id)) {
             mBooleanFlagCache.put(id,
@@ -198,14 +212,17 @@
     /** Specific override for Boolean flags that checks against the teamfood list.*/
     private boolean readFlagValue(int id, boolean defaultValue) {
         Boolean result = readBooleanFlagOverride(id);
-        // Only check for teamfood if the default is false.
-        if (!defaultValue && result == null && id != Flags.TEAMFOOD.getId()) {
+        boolean hasServerOverride = mServerFlagReader.hasOverride(id);
+
+        // Only check for teamfood if the default is false
+        // and there is no server override.
+        if (!hasServerOverride && !defaultValue && result == null && id != Flags.TEAMFOOD.getId()) {
             if (mAllFlags.containsKey(id) && mAllFlags.get(id).getTeamfood()) {
                 return isEnabled(Flags.TEAMFOOD);
             }
         }
 
-        return result == null ? defaultValue : result;
+        return result == null ? mServerFlagReader.readServerOverride(id, defaultValue) : result;
     }
 
     private Boolean readBooleanFlagOverride(int id) {
@@ -410,36 +427,38 @@
          */
         @Nullable
         private ParcelableFlag<?> toParcelableFlag(Flag<?> f) {
-            if (f instanceof BooleanFlag) {
-                return new BooleanFlag(
-                        f.getId(),
-                        isEnabled((BooleanFlag) f),
-                        f.getTeamfood(),
-                        readBooleanFlagOverride(f.getId()) != null);
-            }
-            if (f instanceof ResourceBooleanFlag) {
-                return new BooleanFlag(
-                        f.getId(),
-                        isEnabled((ResourceBooleanFlag) f),
-                        f.getTeamfood(),
-                        readBooleanFlagOverride(f.getId()) != null);
-            }
-            if (f instanceof DeviceConfigBooleanFlag) {
-                return new BooleanFlag(
-                        f.getId(), isEnabled((DeviceConfigBooleanFlag) f), f.getTeamfood());
-            }
-            if (f instanceof SysPropBooleanFlag) {
+            boolean enabled;
+            boolean teamfood = f.getTeamfood();
+            boolean overridden;
+
+            if (f instanceof ReleasedFlag) {
+                enabled = isEnabled((ReleasedFlag) f);
+                overridden = readBooleanFlagOverride(f.getId()) != null;
+            } else if (f instanceof UnreleasedFlag) {
+                enabled = isEnabled((UnreleasedFlag) f);
+                overridden = readBooleanFlagOverride(f.getId()) != null;
+            } else if (f instanceof ResourceBooleanFlag) {
+                enabled = isEnabled((ResourceBooleanFlag) f);
+                overridden = readBooleanFlagOverride(f.getId()) != null;
+            } else if (f instanceof DeviceConfigBooleanFlag) {
+                enabled = isEnabled((DeviceConfigBooleanFlag) f);
+                overridden = false;
+            } else if (f instanceof SysPropBooleanFlag) {
                 // TODO(b/223379190): Teamfood not supported for sysprop flags yet.
-                return new BooleanFlag(
-                        f.getId(),
-                        ((SysPropBooleanFlag) f).getDefault(),
-                        false,
-                        !mSystemProperties.get(((SysPropBooleanFlag) f).getName()).isEmpty());
+                enabled = isEnabled((SysPropBooleanFlag) f);
+                teamfood = false;
+                overridden = !mSystemProperties.get(((SysPropBooleanFlag) f).getName()).isEmpty();
+            } else {
+                // TODO: add support for other flag types.
+                Log.w(TAG, "Unsupported Flag Type. Please file a bug.");
+                return null;
             }
 
-            // TODO: add support for other flag types.
-            Log.w(TAG, "Unsupported Flag Type. Please file a bug.");
-            return null;
+            if (enabled) {
+                return new ReleasedFlag(f.getId(), teamfood, overridden);
+            } else {
+                return new UnreleasedFlag(f.getId(), teamfood, overridden);
+            }
         }
     };
 
@@ -540,8 +559,10 @@
         }
 
         private boolean isBooleanFlagEnabled(Flag<?> flag) {
-            if (flag instanceof BooleanFlag) {
-                return isEnabled((BooleanFlag) flag);
+            if (flag instanceof ReleasedFlag) {
+                return isEnabled((ReleasedFlag) flag);
+            } else if (flag instanceof UnreleasedFlag) {
+                return isEnabled((UnreleasedFlag) flag);
             } else if (flag instanceof ResourceBooleanFlag) {
                 return isEnabled((ResourceBooleanFlag) flag);
             } else if (flag instanceof SysPropFlag) {
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsRelease.java b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsRelease.java
index 1492a2b..049b17d 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsRelease.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsRelease.java
@@ -19,7 +19,6 @@
 import static java.util.Objects.requireNonNull;
 
 import android.content.res.Resources;
-import android.provider.DeviceConfig;
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
 
@@ -31,6 +30,8 @@
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.util.DeviceConfigProxy;
 
+import org.jetbrains.annotations.NotNull;
+
 import java.io.PrintWriter;
 import java.util.Map;
 
@@ -47,18 +48,22 @@
     private final Resources mResources;
     private final SystemPropertiesHelper mSystemProperties;
     private final DeviceConfigProxy mDeviceConfigProxy;
+    private final ServerFlagReader mServerFlagReader;
     SparseBooleanArray mBooleanCache = new SparseBooleanArray();
     SparseArray<String> mStringCache = new SparseArray<>();
+    private boolean mInited;
 
     @Inject
     public FeatureFlagsRelease(
             @Main Resources resources,
             SystemPropertiesHelper systemProperties,
             DeviceConfigProxy deviceConfigProxy,
+            ServerFlagReader serverFlagReader,
             DumpManager dumpManager) {
         mResources = resources;
         mSystemProperties = systemProperties;
         mDeviceConfigProxy = deviceConfigProxy;
+        mServerFlagReader = serverFlagReader;
         dumpManager.registerDumpable("SysUIFlags", this);
     }
 
@@ -69,8 +74,13 @@
     public void removeListener(@NonNull Listener listener) {}
 
     @Override
-    public boolean isEnabled(BooleanFlag flag) {
-        return flag.getDefault();
+    public boolean isEnabled(@NotNull UnreleasedFlag flag) {
+        return false;
+    }
+
+    @Override
+    public boolean isEnabled(@NotNull ReleasedFlag flag) {
+        return mServerFlagReader.readServerOverride(flag.getId(), true);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.java b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
index 3d40e67..6eb77bd 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
@@ -32,7 +32,7 @@
  *
  * Flag Ids are integers.
  * Ids must be unique. This is enforced in a unit test.
- * Ids need not be sequential. Flags can "claim" a chunk of ids for flags in related featurs with
+ * Ids need not be sequential. Flags can "claim" a chunk of ids for flags in related features with
  * a comment. This is purely for organizational purposes.
  *
  * On public release builds, flags will always return their default value. There is no way to
@@ -41,30 +41,30 @@
  * See {@link FeatureFlagsDebug} for instructions on flipping the flags via adb.
  */
 public class Flags {
-    public static final BooleanFlag TEAMFOOD = new BooleanFlag(1, false);
+    public static final UnreleasedFlag TEAMFOOD = new UnreleasedFlag(1);
 
     /***************************************/
     // 100 - notification
-    public static final BooleanFlag NOTIFICATION_PIPELINE_DEVELOPER_LOGGING =
-            new BooleanFlag(103, false);
+    public static final UnreleasedFlag NOTIFICATION_PIPELINE_DEVELOPER_LOGGING =
+            new UnreleasedFlag(103);
 
-    public static final BooleanFlag NSSL_DEBUG_LINES =
-            new BooleanFlag(105, false);
+    public static final UnreleasedFlag NSSL_DEBUG_LINES =
+            new UnreleasedFlag(105);
 
-    public static final BooleanFlag NSSL_DEBUG_REMOVE_ANIMATION =
-            new BooleanFlag(106, false);
+    public static final UnreleasedFlag NSSL_DEBUG_REMOVE_ANIMATION =
+            new UnreleasedFlag(106);
 
-    public static final BooleanFlag NEW_PIPELINE_CRASH_ON_CALL_TO_OLD_PIPELINE =
-            new BooleanFlag(107, false);
+    public static final UnreleasedFlag NEW_PIPELINE_CRASH_ON_CALL_TO_OLD_PIPELINE =
+            new UnreleasedFlag(107);
 
     public static final ResourceBooleanFlag NOTIFICATION_DRAG_TO_CONTENTS =
             new ResourceBooleanFlag(108, R.bool.config_notificationToContents);
 
-    public static final BooleanFlag REMOVE_UNRANKED_NOTIFICATIONS =
-            new BooleanFlag(109, false, true);
+    public static final UnreleasedFlag REMOVE_UNRANKED_NOTIFICATIONS =
+            new UnreleasedFlag(109, true);
 
-    public static final BooleanFlag FSI_REQUIRES_KEYGUARD =
-            new BooleanFlag(110, false, true);
+    public static final UnreleasedFlag FSI_REQUIRES_KEYGUARD =
+            new UnreleasedFlag(110, true);
 
     /***************************************/
     // 200 - keyguard/lockscreen
@@ -73,11 +73,11 @@
     // public static final BooleanFlag KEYGUARD_LAYOUT =
     //         new BooleanFlag(200, true);
 
-    public static final BooleanFlag LOCKSCREEN_ANIMATIONS =
-            new BooleanFlag(201, true);
+    public static final ReleasedFlag LOCKSCREEN_ANIMATIONS =
+            new ReleasedFlag(201);
 
-    public static final BooleanFlag NEW_UNLOCK_SWIPE_ANIMATION =
-            new BooleanFlag(202, true);
+    public static final ReleasedFlag NEW_UNLOCK_SWIPE_ANIMATION =
+            new ReleasedFlag(202);
 
     public static final ResourceBooleanFlag CHARGING_RIPPLE =
             new ResourceBooleanFlag(203, R.bool.flag_charging_ripple);
@@ -92,25 +92,28 @@
      * Whether the KeyguardBottomArea(View|Controller) should use the modern architecture or the old
      * one.
      */
-    public static final BooleanFlag MODERN_BOTTOM_AREA = new BooleanFlag(
-            206,
-            /* default= */ false,
-            /* teamfood= */ true);
+    public static final UnreleasedFlag MODERN_BOTTOM_AREA = new UnreleasedFlag(206, true);
 
-    public static final BooleanFlag LOCKSCREEN_CUSTOM_CLOCKS = new BooleanFlag(207, false);
+    public static final UnreleasedFlag LOCKSCREEN_CUSTOM_CLOCKS = new UnreleasedFlag(207);
+  
+    /**
+     * Flag to enable the usage of the new bouncer data source. This is a refactor of and
+     * eventual replacement of KeyguardBouncer.java.
+     */
+    public static final ReleasedFlag MODERN_BOUNCER = new ReleasedFlag(208);
 
     /***************************************/
     // 300 - power menu
-    public static final BooleanFlag POWER_MENU_LITE =
-            new BooleanFlag(300, true);
+    public static final ReleasedFlag POWER_MENU_LITE =
+            new ReleasedFlag(300);
 
     /***************************************/
     // 400 - smartspace
-    public static final BooleanFlag SMARTSPACE_DEDUPING =
-            new BooleanFlag(400, true);
+    public static final ReleasedFlag SMARTSPACE_DEDUPING =
+            new ReleasedFlag(400);
 
-    public static final BooleanFlag SMARTSPACE_SHARED_ELEMENT_TRANSITION_ENABLED =
-            new BooleanFlag(401, true);
+    public static final ReleasedFlag SMARTSPACE_SHARED_ELEMENT_TRANSITION_ENABLED =
+            new ReleasedFlag(401);
 
     public static final ResourceBooleanFlag SMARTSPACE =
             new ResourceBooleanFlag(402, R.bool.flag_smartspace);
@@ -121,11 +124,11 @@
      * @deprecated Not needed anymore
      */
     @Deprecated
-    public static final BooleanFlag NEW_USER_SWITCHER =
-            new BooleanFlag(500, true);
+    public static final ReleasedFlag NEW_USER_SWITCHER =
+            new ReleasedFlag(500);
 
-    public static final BooleanFlag COMBINED_QS_HEADERS =
-            new BooleanFlag(501, false, true);
+    public static final UnreleasedFlag COMBINED_QS_HEADERS =
+            new UnreleasedFlag(501, true);
 
     public static final ResourceBooleanFlag PEOPLE_TILE =
             new ResourceBooleanFlag(502, R.bool.flag_conversations);
@@ -137,35 +140,32 @@
      * @deprecated Not needed anymore
      */
     @Deprecated
-    public static final BooleanFlag NEW_FOOTER = new BooleanFlag(504, true);
+    public static final ReleasedFlag NEW_FOOTER = new ReleasedFlag(504);
 
-    public static final BooleanFlag NEW_HEADER = new BooleanFlag(505, false, true);
+    public static final UnreleasedFlag NEW_HEADER = new UnreleasedFlag(505, true);
     public static final ResourceBooleanFlag FULL_SCREEN_USER_SWITCHER =
             new ResourceBooleanFlag(506, R.bool.config_enableFullscreenUserSwitcher);
 
     /***************************************/
     // 600- status bar
-    public static final BooleanFlag COMBINED_STATUS_BAR_SIGNAL_ICONS =
-            new BooleanFlag(601, false);
-
     public static final ResourceBooleanFlag STATUS_BAR_USER_SWITCHER =
             new ResourceBooleanFlag(602, R.bool.flag_user_switcher_chip);
 
-    public static final BooleanFlag STATUS_BAR_LETTERBOX_APPEARANCE =
-            new BooleanFlag(603, false);
+    public static final UnreleasedFlag STATUS_BAR_LETTERBOX_APPEARANCE =
+            new UnreleasedFlag(603, false);
 
-    public static final BooleanFlag NEW_STATUS_BAR_PIPELINE = new BooleanFlag(604, false);
+    public static final UnreleasedFlag NEW_STATUS_BAR_PIPELINE = new UnreleasedFlag(604, true);
 
     /***************************************/
     // 700 - dialer/calls
-    public static final BooleanFlag ONGOING_CALL_STATUS_BAR_CHIP =
-            new BooleanFlag(700, true);
+    public static final ReleasedFlag ONGOING_CALL_STATUS_BAR_CHIP =
+            new ReleasedFlag(700);
 
-    public static final BooleanFlag ONGOING_CALL_IN_IMMERSIVE =
-            new BooleanFlag(701, true);
+    public static final ReleasedFlag ONGOING_CALL_IN_IMMERSIVE =
+            new ReleasedFlag(701);
 
-    public static final BooleanFlag ONGOING_CALL_IN_IMMERSIVE_CHIP_TAP =
-            new BooleanFlag(702, true);
+    public static final ReleasedFlag ONGOING_CALL_IN_IMMERSIVE_CHIP_TAP =
+            new ReleasedFlag(702);
 
     /***************************************/
     // 800 - general visual/theme
@@ -174,21 +174,21 @@
 
     /***************************************/
     // 801 - region sampling
-    public static final BooleanFlag REGION_SAMPLING =
-            new BooleanFlag(801, false);
+    public static final UnreleasedFlag REGION_SAMPLING = new UnreleasedFlag(801);
 
     /***************************************/
     // 900 - media
-    public static final BooleanFlag MEDIA_TAP_TO_TRANSFER = new BooleanFlag(900, true);
-    public static final BooleanFlag MEDIA_SESSION_ACTIONS = new BooleanFlag(901, false);
-    public static final BooleanFlag MEDIA_NEARBY_DEVICES = new BooleanFlag(903, true);
-    public static final BooleanFlag MEDIA_MUTE_AWAIT = new BooleanFlag(904, true);
+    public static final ReleasedFlag MEDIA_TAP_TO_TRANSFER = new ReleasedFlag(900);
+    public static final UnreleasedFlag MEDIA_SESSION_ACTIONS = new UnreleasedFlag(901);
+    public static final ReleasedFlag MEDIA_NEARBY_DEVICES = new ReleasedFlag(903);
+    public static final ReleasedFlag MEDIA_MUTE_AWAIT = new ReleasedFlag(904);
 
     // 1000 - dock
-    public static final BooleanFlag SIMULATE_DOCK_THROUGH_CHARGING =
-            new BooleanFlag(1000, true);
-    public static final BooleanFlag DOCK_SETUP_ENABLED = new BooleanFlag(1001, true);
+    public static final ReleasedFlag SIMULATE_DOCK_THROUGH_CHARGING =
+            new ReleasedFlag(1000);
+    public static final ReleasedFlag DOCK_SETUP_ENABLED = new ReleasedFlag(1001);
 
+    public static final UnreleasedFlag ROUNDED_BOX_RIPPLE = new UnreleasedFlag(1002, false);
 
     // 1100 - windowing
     @Keep
@@ -211,6 +211,14 @@
     public static final SysPropBooleanFlag HIDE_NAVBAR_WINDOW =
             new SysPropBooleanFlag(1103, "persist.wm.debug.hide_navbar_window", false);
 
+    @Keep
+    public static final SysPropBooleanFlag WM_DESKTOP_WINDOWING =
+            new SysPropBooleanFlag(1104, "persist.wm.debug.desktop_mode", false);
+
+    @Keep
+    public static final SysPropBooleanFlag WM_CAPTION_ON_SHELL =
+            new SysPropBooleanFlag(1105, "persist.wm.debug.caption_on_shell", false);
+
     // 1200 - predictive back
     @Keep
     public static final SysPropBooleanFlag WM_ENABLE_PREDICTIVE_BACK = new SysPropBooleanFlag(
@@ -222,8 +230,12 @@
     public static final SysPropBooleanFlag WM_ALWAYS_ENFORCE_PREDICTIVE_BACK =
             new SysPropBooleanFlag(1202, "persist.wm.debug.predictive_back_always_enforce", false);
 
-    public static final BooleanFlag NEW_BACK_AFFORDANCE =
-            new BooleanFlag(1203, false /* default */, true /* teamfood */);
+    public static final UnreleasedFlag NEW_BACK_AFFORDANCE =
+            new UnreleasedFlag(1203, false /* teamfood */);
+
+    // 1300 - screenshots
+
+    public static final UnreleasedFlag SCREENSHOT_REQUEST_PROCESSOR = new UnreleasedFlag(1300);
 
     // Pay no attention to the reflection behind the curtain.
     // ========================== Curtain ==========================
diff --git a/packages/SystemUI/src/com/android/systemui/flags/ServerFlagReader.kt b/packages/SystemUI/src/com/android/systemui/flags/ServerFlagReader.kt
new file mode 100644
index 0000000..fc5b9f4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/flags/ServerFlagReader.kt
@@ -0,0 +1,80 @@
+/*
+ * 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.flags
+
+import com.android.systemui.util.DeviceConfigProxy
+import dagger.Binds
+import dagger.Module
+import javax.inject.Inject
+
+interface ServerFlagReader {
+    /** Returns true if there is a server-side setting stored. */
+    fun hasOverride(flagId: Int): Boolean
+
+    /** Returns any stored server-side setting or the default if not set. */
+    fun readServerOverride(flagId: Int, default: Boolean): Boolean
+}
+
+class ServerFlagReaderImpl @Inject constructor(
+    private val deviceConfig: DeviceConfigProxy
+) : ServerFlagReader {
+    override fun hasOverride(flagId: Int): Boolean =
+        deviceConfig.getProperty(
+            SYSUI_NAMESPACE,
+            getServerOverrideName(flagId)
+        ) != null
+
+    override fun readServerOverride(flagId: Int, default: Boolean): Boolean {
+        return deviceConfig.getBoolean(
+            SYSUI_NAMESPACE,
+            getServerOverrideName(flagId),
+            default
+        )
+    }
+
+    private fun getServerOverrideName(flagId: Int): String {
+        return "flag_override_$flagId"
+    }
+}
+
+private val SYSUI_NAMESPACE = "systemui"
+
+@Module
+interface ServerFlagReaderModule {
+    @Binds
+    fun bindsReader(impl: ServerFlagReaderImpl): ServerFlagReader
+}
+
+class ServerFlagReaderFake : ServerFlagReader {
+    private val flagMap: MutableMap<Int, Boolean> = mutableMapOf()
+
+    override fun hasOverride(flagId: Int): Boolean {
+        return flagMap.containsKey(flagId)
+    }
+
+    override fun readServerOverride(flagId: Int, default: Boolean): Boolean {
+        return flagMap.getOrDefault(flagId, default)
+    }
+
+    fun setFlagValue(flagId: Int, value: Boolean) {
+        flagMap.put(flagId, value)
+    }
+
+    fun eraseFlag(flagId: Int) {
+        flagMap.remove(flagId)
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt
index 3202ecb..df44957 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt
@@ -36,6 +36,7 @@
 import javax.inject.Inject
 import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.flowOf
 
 /** Home controls quick affordance data source. */
@@ -50,7 +51,13 @@
     private val appContext = context.applicationContext
 
     override val state: Flow<KeyguardQuickAffordanceConfig.State> =
-        stateInternal(component.getControlsListingController().getOrNull())
+        component.canShowWhileLockedSetting.flatMapLatest { canShowWhileLocked ->
+            if (canShowWhileLocked) {
+                stateInternal(component.getControlsListingController().getOrNull())
+            } else {
+                flowOf(KeyguardQuickAffordanceConfig.State.Hidden)
+            }
+        }
 
     override fun onQuickAffordanceClicked(
         animationController: ActivityLaunchAnimator.Controller?,
diff --git a/packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt b/packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt
index dc23684d..6124e10 100644
--- a/packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt
@@ -22,17 +22,11 @@
 import com.android.systemui.util.collection.RingBuffer
 import com.google.errorprone.annotations.CompileTimeConstant
 import java.io.PrintWriter
-import java.text.SimpleDateFormat
-import java.util.Arrays.stream
-import java.util.Locale
 import java.util.concurrent.ArrayBlockingQueue
 import java.util.concurrent.BlockingQueue
 import kotlin.concurrent.thread
 import kotlin.math.max
 
-const val UNBOUNDED_STACK_TRACE = -1
-const val NESTED_TRACE_DEPTH = 10
-
 /**
  * A simple ring buffer of recyclable log messages
  *
@@ -74,18 +68,12 @@
  * @param maxSize The maximum number of messages to keep in memory at any one time. Buffers start
  * out empty and grow up to [maxSize] as new messages are logged. Once the buffer's size reaches
  * the maximum, it behaves like a ring buffer.
- * @param rootStackTraceDepth The number of stack trace elements to be logged for an exception when
- * the logBuffer is dumped. Defaulted to -1 [UNBOUNDED_STACK_TRACE] to print the entire stack trace.
- * @param nestedStackTraceDepth The number of stack trace elements to be logged for any nested
- * exceptions present in [Throwable.cause] or [Throwable.suppressedExceptions].
  */
 class LogBuffer @JvmOverloads constructor(
     private val name: String,
     private val maxSize: Int,
     private val logcatEchoTracker: LogcatEchoTracker,
     private val systrace: Boolean = true,
-    private val rootStackTraceDepth: Int = UNBOUNDED_STACK_TRACE,
-    private val nestedStackTraceDepth: Int = NESTED_TRACE_DEPTH,
 ) {
     private val buffer = RingBuffer(maxSize) { LogMessageImpl.create() }
 
@@ -236,7 +224,7 @@
         val iterationStart = if (tailLength <= 0) { 0 } else { max(0, buffer.size - tailLength) }
 
         for (i in iterationStart until buffer.size) {
-            dumpMessage(buffer[i], pw)
+            buffer[i].dump(pw)
         }
     }
 
@@ -264,76 +252,6 @@
         }
     }
 
-    private fun dumpMessage(
-        message: LogMessage,
-        pw: PrintWriter
-    ) {
-        val formattedTimestamp = DATE_FORMAT.format(message.timestamp)
-        val shortLevel = message.level.shortString
-        val messageToPrint = message.messagePrinter(message)
-        val tag = message.tag
-        printLikeLogcat(pw, formattedTimestamp, shortLevel, tag, messageToPrint)
-        message.exception?.let { ex ->
-            printException(
-                pw,
-                formattedTimestamp,
-                shortLevel,
-                ex,
-                tag,
-                stackTraceDepth = rootStackTraceDepth)
-        }
-    }
-
-    private fun printException(
-            pw: PrintWriter,
-            timestamp: String,
-            level: String,
-            exception: Throwable,
-            tag: String,
-            exceptionMessagePrefix: String = "",
-            stackTraceDepth: Int = UNBOUNDED_STACK_TRACE
-    ) {
-        val message = "$exceptionMessagePrefix$exception"
-        printLikeLogcat(pw, timestamp, level, tag, message)
-        var stacktraceStream = stream(exception.stackTrace)
-        if (stackTraceDepth != UNBOUNDED_STACK_TRACE) {
-            stacktraceStream = stacktraceStream.limit(stackTraceDepth.toLong())
-        }
-        stacktraceStream.forEach { line ->
-            printLikeLogcat(pw, timestamp, level, tag, "\tat $line")
-        }
-        exception.cause?.let { cause ->
-            printException(pw, timestamp, level, cause, tag, "Caused by: ", nestedStackTraceDepth)
-        }
-        exception.suppressedExceptions.forEach { suppressed ->
-            printException(
-                pw,
-                timestamp,
-                level,
-                suppressed,
-                tag,
-                "Suppressed: ",
-                nestedStackTraceDepth
-            )
-        }
-    }
-
-    private fun printLikeLogcat(
-        pw: PrintWriter,
-        formattedTimestamp: String,
-        shortLogLevel: String,
-        tag: String,
-        message: String
-    ) {
-        pw.print(formattedTimestamp)
-        pw.print(" ")
-        pw.print(shortLogLevel)
-        pw.print(" ")
-        pw.print(tag)
-        pw.print(": ")
-        pw.println(message)
-    }
-
     private fun echo(message: LogMessage, toLogcat: Boolean, toSystrace: Boolean) {
         if (toLogcat || toSystrace) {
             val strMessage = message.messagePrinter(message)
@@ -370,5 +288,4 @@
 typealias MessageInitializer = LogMessage.() -> Unit
 
 private const val TAG = "LogBuffer"
-private val DATE_FORMAT = SimpleDateFormat("MM-dd HH:mm:ss.SSS", Locale.US)
 private val FROZEN_MESSAGE = LogMessageImpl.create()
diff --git a/packages/SystemUI/src/com/android/systemui/log/LogMessage.kt b/packages/SystemUI/src/com/android/systemui/log/LogMessage.kt
index 987aea8..dae2592 100644
--- a/packages/SystemUI/src/com/android/systemui/log/LogMessage.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/LogMessage.kt
@@ -16,6 +16,10 @@
 
 package com.android.systemui.log
 
+import java.io.PrintWriter
+import java.text.SimpleDateFormat
+import java.util.Locale
+
 /**
  * Generic data class for storing messages logged to a [LogBuffer]
  *
@@ -50,6 +54,17 @@
     var bool2: Boolean
     var bool3: Boolean
     var bool4: Boolean
+
+    /**
+     * Function that dumps the [LogMessage] to the provided [writer].
+     */
+    fun dump(writer: PrintWriter) {
+        val formattedTimestamp = DATE_FORMAT.format(timestamp)
+        val shortLevel = level.shortString
+        val messageToPrint = messagePrinter(this)
+        printLikeLogcat(writer, formattedTimestamp, shortLevel, tag, messageToPrint)
+        exception?.printStackTrace(writer)
+    }
 }
 
 /**
@@ -61,3 +76,21 @@
  * of the printer for each call, thwarting our attempts at avoiding any sort of allocation.
  */
 typealias MessagePrinter = LogMessage.() -> String
+
+private fun printLikeLogcat(
+    pw: PrintWriter,
+    formattedTimestamp: String,
+    shortLogLevel: String,
+    tag: String,
+    message: String
+) {
+    pw.print(formattedTimestamp)
+    pw.print(" ")
+    pw.print(shortLogLevel)
+    pw.print(" ")
+    pw.print(tag)
+    pw.print(": ")
+    pw.println(message)
+}
+
+private val DATE_FORMAT = SimpleDateFormat("MM-dd HH:mm:ss.SSS", Locale.US)
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt
index 81efdf5..e0c8d66 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt
@@ -266,11 +266,11 @@
     }
 
     /**
-     * Are there any media notifications active, including the recommendation?
+     * Are there any active media entries, including the recommendation?
      */
-    fun hasActiveMediaOrRecommendation() =
-            userEntries.any { it.value.active } ||
-                    (smartspaceMediaData.isActive && smartspaceMediaData.isValid())
+    fun hasActiveMediaOrRecommendation() = userEntries.any { it.value.active } ||
+            (smartspaceMediaData.isActive &&
+                (smartspaceMediaData.isValid() || reactivatedKey != null))
 
     /**
      * Are there any media entries we should display?
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 e9b6af4..e360d10 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
@@ -263,6 +263,7 @@
         }
 
         private void onGroupActionTriggered(boolean isChecked, MediaDevice device) {
+            disableSeekBar();
             if (isChecked && isDeviceIncluded(mController.getSelectableMediaDevice(), device)) {
                 mController.addDeviceToPlayMedia(device);
             } else if (!isChecked && isDeviceIncluded(mController.getDeselectableMediaDevice(),
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 e6116c1..43b0287 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
@@ -273,6 +273,8 @@
         void initSeekbar(MediaDevice device, boolean isCurrentSeekbarInvisible) {
             if (!mController.isVolumeControlEnabled(device)) {
                 disableSeekBar();
+            } else {
+                enableSeekBar();
             }
             mSeekBar.setMaxVolume(device.getMaxVolume());
             final int currentVolume = device.getCurrentVolume();
@@ -417,11 +419,16 @@
             return drawable;
         }
 
-        private void disableSeekBar() {
+        protected void disableSeekBar() {
             mSeekBar.setEnabled(false);
             mSeekBar.setOnTouchListener((v, event) -> true);
         }
 
+        private void enableSeekBar() {
+            mSeekBar.setEnabled(true);
+            mSeekBar.setOnTouchListener((v, event) -> false);
+        }
+
         protected void setUpDeviceIcon(MediaDevice device) {
             ThreadUtils.postOnBackgroundThread(() -> {
                 Icon icon = mController.getDeviceIconCompat(device).toIcon(mContext);
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputMetricLogger.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputMetricLogger.java
index 5d7af52..6fe06e0 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputMetricLogger.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputMetricLogger.java
@@ -194,6 +194,11 @@
     }
 
     private int getLoggingDeviceType(MediaDevice device, boolean isSourceDevice) {
+        if (device == null) {
+            return isSourceDevice
+                    ? SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__SOURCE__UNKNOWN_TYPE
+                    : SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__TARGET__UNKNOWN_TYPE;
+        }
         switch (device.getDeviceType()) {
             case MediaDevice.MediaDeviceType.TYPE_PHONE_DEVICE:
                 return isSourceDevice
@@ -229,6 +234,9 @@
     }
 
     private int getInteractionDeviceType(MediaDevice device) {
+        if (device == null) {
+            return SysUiStatsLog.MEDIA_OUTPUT_OP_INTERACTION_REPORTED__TARGET__UNKNOWN_TYPE;
+        }
         switch (device.getDeviceType()) {
             case MediaDevice.MediaDeviceType.TYPE_PHONE_DEVICE:
                 return SysUiStatsLog.MEDIA_OUTPUT_OP_INTERACTION_REPORTED__TARGET__BUILTIN_SPEAKER;
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 5f478ce..9ab83b8 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
@@ -56,7 +56,7 @@
         internal val logger: MediaTttLogger,
         internal val windowManager: WindowManager,
         private val viewUtil: ViewUtil,
-        @Main private val mainExecutor: DelayableExecutor,
+        @Main internal val mainExecutor: DelayableExecutor,
         private val accessibilityManager: AccessibilityManager,
         private val configurationController: ConfigurationController,
         private val powerManager: PowerManager,
@@ -205,13 +205,15 @@
      *
      * @param appPackageName the package name of the app playing the media. Will be used to fetch
      *   the app icon and app name if overrides aren't provided.
+     *
+     * @return the content description of the icon.
      */
     internal fun setIcon(
         currentChipView: ViewGroup,
         appPackageName: String?,
         appIconDrawableOverride: Drawable? = null,
         appNameOverride: CharSequence? = null,
-    ) {
+    ): CharSequence {
         val appIconView = currentChipView.requireViewById<CachingIconView>(R.id.app_icon)
         val iconInfo = getIconInfo(appPackageName)
 
@@ -224,6 +226,7 @@
 
         appIconView.contentDescription = appNameOverride ?: iconInfo.iconName
         appIconView.setImageDrawable(appIconDrawableOverride ?: iconInfo.icon)
+        return appIconView.contentDescription.toString()
     }
 
     /**
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 3ea11b8..b94b8bf 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
@@ -122,13 +122,12 @@
         val chipState = newChipInfo.state
 
         // App icon
-        setIcon(currentChipView, newChipInfo.routeInfo.packageName)
+        val iconName = setIcon(currentChipView, newChipInfo.routeInfo.packageName)
 
         // Text
         val otherDeviceName = newChipInfo.routeInfo.name.toString()
-        currentChipView.requireViewById<TextView>(R.id.text).apply {
-            text = chipState.getChipTextString(context, otherDeviceName)
-        }
+        val chipText = chipState.getChipTextString(context, otherDeviceName)
+        currentChipView.requireViewById<TextView>(R.id.text).text = chipText
 
         // Loading
         currentChipView.requireViewById<View>(R.id.loading).visibility =
@@ -145,17 +144,29 @@
         // Failure
         currentChipView.requireViewById<View>(R.id.failure_icon).visibility =
             chipState.isTransferFailure.visibleIfTrue()
+
+        // For accessibility
+        currentChipView.requireViewById<ViewGroup>(
+                R.id.media_ttt_sender_chip_inner
+        ).contentDescription = "$iconName $chipText"
     }
 
     override fun animateChipIn(chipView: ViewGroup) {
+        val chipInnerView = chipView.requireViewById<ViewGroup>(R.id.media_ttt_sender_chip_inner)
         ViewHierarchyAnimator.animateAddition(
-            chipView.requireViewById<ViewGroup>(R.id.media_ttt_sender_chip_inner),
+            chipInnerView,
             ViewHierarchyAnimator.Hotspot.TOP,
             Interpolators.EMPHASIZED_DECELERATE,
-            duration = 500L,
+            duration = ANIMATION_DURATION,
             includeMargins = true,
             includeFadeIn = true,
         )
+
+        // We can only request focus once the animation finishes.
+        mainExecutor.executeDelayed(
+                { chipInnerView.requestAccessibilityFocus() },
+                ANIMATION_DURATION
+        )
     }
 
     override fun removeChip(removalReason: String) {
@@ -186,3 +197,4 @@
 }
 
 const val SENDER_TAG = "MediaTapToTransferSender"
+private const val ANIMATION_DURATION = 500L
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
index 6bc50a6..da9fefa 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
@@ -48,6 +48,7 @@
 
 import androidx.annotation.NonNull;
 
+import com.android.keyguard.KeyguardViewController;
 import com.android.systemui.Dumpable;
 import com.android.systemui.accessibility.AccessibilityButtonModeObserver;
 import com.android.systemui.accessibility.AccessibilityButtonTargetsObserver;
@@ -89,6 +90,7 @@
     private final AccessibilityManager mAccessibilityManager;
     private final Lazy<AssistManager> mAssistManagerLazy;
     private final Lazy<Optional<CentralSurfaces>> mCentralSurfacesOptionalLazy;
+    private final KeyguardViewController mKeyguardViewController;
     private final UserTracker mUserTracker;
     private final SystemActions mSystemActions;
     private final AccessibilityButtonModeObserver mAccessibilityButtonModeObserver;
@@ -123,6 +125,7 @@
             OverviewProxyService overviewProxyService,
             Lazy<AssistManager> assistManagerLazy,
             Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy,
+            KeyguardViewController keyguardViewController,
             NavigationModeController navigationModeController,
             UserTracker userTracker,
             DumpManager dumpManager) {
@@ -131,6 +134,7 @@
         mAccessibilityManager = accessibilityManager;
         mAssistManagerLazy = assistManagerLazy;
         mCentralSurfacesOptionalLazy = centralSurfacesOptionalLazy;
+        mKeyguardViewController = keyguardViewController;
         mUserTracker = userTracker;
         mSystemActions = systemActions;
         accessibilityManager.addAccessibilityServicesStateChangeListener(this);
@@ -317,8 +321,12 @@
      * {@link InputMethodService} and the keyguard states.
      */
     public boolean isImeShown(int vis) {
-        View shadeWindowView = mCentralSurfacesOptionalLazy.get().get().getNotificationShadeWindowView();
-        boolean isKeyguardShowing = mCentralSurfacesOptionalLazy.get().get().isKeyguardShowing();
+        View shadeWindowView = null;
+        if (mCentralSurfacesOptionalLazy.get().isPresent()) {
+            shadeWindowView =
+                    mCentralSurfacesOptionalLazy.get().get().getNotificationShadeWindowView();
+        }
+        boolean isKeyguardShowing = mKeyguardViewController.isShowing();
         boolean imeVisibleOnShade = shadeWindowView != null && shadeWindowView.isAttachedToWindow()
                 && shadeWindowView.getRootWindowInsets().isVisible(WindowInsets.Type.ime());
         return imeVisibleOnShade
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
index 2d7a809..3789cbb 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
@@ -302,10 +302,6 @@
      */
     @VisibleForTesting
     void createNavigationBar(Display display, Bundle savedState, RegisterStatusBarResult result) {
-        if (initializeTaskbarIfNecessary()) {
-            return;
-        }
-
         if (display == null) {
             return;
         }
@@ -315,7 +311,7 @@
 
         // We may show TaskBar on the default display for large screen device. Don't need to create
         // navigation bar for this case.
-        if (mIsTablet && isOnDefaultDisplay) {
+        if (isOnDefaultDisplay && initializeTaskbarIfNecessary()) {
             return;
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
index b05e75e..6e927b0 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
@@ -42,7 +42,6 @@
 import com.android.systemui.statusbar.VibratorHelper
 import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.util.ViewController
-import com.android.wm.shell.back.BackAnimation
 import java.io.PrintWriter
 import javax.inject.Inject
 import kotlin.math.abs
@@ -119,7 +118,7 @@
         private val latencyTracker: LatencyTracker
     ) {
         /** Construct a [BackPanelController].  */
-        fun create(context: Context, backAnimation: BackAnimation?): BackPanelController {
+        fun create(context: Context): BackPanelController {
             val backPanelController = BackPanelController(
                 context,
                 windowManager,
@@ -418,6 +417,7 @@
                 stretchEntryBackIndicator(preThresholdStretchProgress(xTranslation))
             GestureState.INACTIVE ->
                 mView.resetStretch()
+            else -> {}
         }
 
         // set y translation
@@ -510,7 +510,6 @@
     private fun playCommitBackAnimation() {
         // Check if we should vibrate again
         if (previousState != GestureState.FLUNG) {
-            backCallback.triggerBack()
             velocityTracker!!.computeCurrentVelocity(1000)
             val isSlow = abs(velocityTracker!!.xVelocity) < 500
             val hasNotVibratedRecently =
@@ -519,6 +518,10 @@
                 vibratorHelper.vibrate(VibrationEffect.EFFECT_CLICK)
             }
         }
+        // Dispatch the actual back trigger
+        if (DEBUG) Log.d(TAG, "playCommitBackAnimation() invoked triggerBack() on backCallback")
+        backCallback.triggerBack()
+
         playAnimation(setGoneEndListener)
     }
 
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 057ed24..6ac3ead 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
@@ -60,6 +60,7 @@
 import com.android.internal.util.LatencyTracker;
 import com.android.systemui.R;
 import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.flags.Flags;
@@ -82,6 +83,7 @@
 import com.android.systemui.tracing.ProtoTracer;
 import com.android.systemui.tracing.nano.EdgeBackGestureHandlerProto;
 import com.android.systemui.tracing.nano.SystemUiTraceProto;
+import com.android.systemui.util.Assert;
 import com.android.wm.shell.back.BackAnimation;
 import com.android.wm.shell.pip.Pip;
 
@@ -191,6 +193,7 @@
     private final int mDisplayId;
 
     private final Executor mMainExecutor;
+    private final Executor mBackgroundExecutor;
 
     private final Rect mPipExcludedBounds = new Rect();
     private final Rect mNavBarOverlayExcludedBounds = new Rect();
@@ -251,6 +254,7 @@
     private BackGestureTfClassifierProvider mBackGestureTfClassifierProvider;
     private Map<String, Integer> mVocab;
     private boolean mUseMLModel;
+    private boolean mMLModelIsLoading;
     // minimum width below which we do not run the model
     private int mMLEnableWidth;
     private float mMLModelThreshold;
@@ -318,6 +322,7 @@
             SysUiState sysUiState,
             PluginManager pluginManager,
             @Main Executor executor,
+            @Background Executor backgroundExecutor,
             BroadcastDispatcher broadcastDispatcher,
             ProtoTracer protoTracer,
             NavigationModeController navigationModeController,
@@ -334,6 +339,7 @@
         mContext = context;
         mDisplayId = context.getDisplayId();
         mMainExecutor = executor;
+        mBackgroundExecutor = backgroundExecutor;
         mOverviewProxyService = overviewProxyService;
         mSysUiState = sysUiState;
         mPluginManager = pluginManager;
@@ -568,10 +574,10 @@
     private void resetEdgeBackPlugin() {
         if (mIsNewBackAffordanceEnabled) {
             setEdgeBackPlugin(
-                    mBackPanelControllerFactory.create(mContext, mBackAnimation));
+                    mBackPanelControllerFactory.create(mContext));
         } else {
             setEdgeBackPlugin(
-                    new NavigationBarEdgePanel(mContext, mBackAnimation, mLatencyTracker));
+                    new NavigationBarEdgePanel(mContext, mLatencyTracker));
         }
     }
 
@@ -631,28 +637,63 @@
             return;
         }
 
-        if (newState) {
-            mBackGestureTfClassifierProvider = mBackGestureTfClassifierProviderProvider.get();
-            mMLModelThreshold = DeviceConfig.getFloat(DeviceConfig.NAMESPACE_SYSTEMUI,
-                    SystemUiDeviceConfigFlags.BACK_GESTURE_ML_MODEL_THRESHOLD, 0.9f);
-            if (mBackGestureTfClassifierProvider.isActive()) {
-                Trace.beginSection("EdgeBackGestureHandler#loadVocab");
-                mVocab = mBackGestureTfClassifierProvider.loadVocab(mContext.getAssets());
-                Trace.endSection();
-                mUseMLModel = true;
+        mUseMLModel = newState;
+
+        if (mUseMLModel) {
+            Assert.isMainThread();
+            if (mMLModelIsLoading) {
+                Log.d(TAG, "Model tried to load while already loading.");
                 return;
             }
-        }
-
-        mUseMLModel = false;
-        if (mBackGestureTfClassifierProvider != null) {
+            mMLModelIsLoading = true;
+            mBackgroundExecutor.execute(() -> loadMLModel());
+        } else if (mBackGestureTfClassifierProvider != null) {
             mBackGestureTfClassifierProvider.release();
             mBackGestureTfClassifierProvider = null;
+            mVocab = null;
         }
     }
 
+    private void loadMLModel() {
+        BackGestureTfClassifierProvider provider = mBackGestureTfClassifierProviderProvider.get();
+        float threshold = DeviceConfig.getFloat(DeviceConfig.NAMESPACE_SYSTEMUI,
+                SystemUiDeviceConfigFlags.BACK_GESTURE_ML_MODEL_THRESHOLD, 0.9f);
+        Map<String, Integer> vocab = null;
+        if (provider != null && !provider.isActive()) {
+            provider.release();
+            provider = null;
+            Log.w(TAG, "Cannot load model because it isn't active");
+        }
+        if (provider != null) {
+            Trace.beginSection("EdgeBackGestureHandler#loadVocab");
+            vocab = provider.loadVocab(mContext.getAssets());
+            Trace.endSection();
+        }
+        BackGestureTfClassifierProvider finalProvider = provider;
+        Map<String, Integer> finalVocab = vocab;
+        mMainExecutor.execute(() -> onMLModelLoadFinished(finalProvider, finalVocab, threshold));
+    }
+
+    private void onMLModelLoadFinished(BackGestureTfClassifierProvider provider,
+            Map<String, Integer> vocab, float threshold) {
+        Assert.isMainThread();
+        mMLModelIsLoading = false;
+        if (!mUseMLModel) {
+            // This can happen if the user disables Gesture Nav while the model is loading.
+            if (provider != null) {
+                provider.release();
+            }
+            Log.d(TAG, "Model finished loading but isn't needed.");
+            return;
+        }
+        mBackGestureTfClassifierProvider = provider;
+        mVocab = vocab;
+        mMLModelThreshold = threshold;
+    }
+
     private int getBackGesturePredictionsCategory(int x, int y, int app) {
-        if (app == -1) {
+        BackGestureTfClassifierProvider provider = mBackGestureTfClassifierProvider;
+        if (provider == null || app == -1) {
             return -1;
         }
         int distanceFromEdge;
@@ -673,7 +714,7 @@
             new long[]{(long) y},
         };
 
-        mMLResults = mBackGestureTfClassifierProvider.predict(featuresVector);
+        mMLResults = provider.predict(featuresVector);
         if (mMLResults == -1) {
             return -1;
         }
@@ -1031,6 +1072,7 @@
         private final SysUiState mSysUiState;
         private final PluginManager mPluginManager;
         private final Executor mExecutor;
+        private final Executor mBackgroundExecutor;
         private final BroadcastDispatcher mBroadcastDispatcher;
         private final ProtoTracer mProtoTracer;
         private final NavigationModeController mNavigationModeController;
@@ -1050,6 +1092,7 @@
                        SysUiState sysUiState,
                        PluginManager pluginManager,
                        @Main Executor executor,
+                       @Background Executor backgroundExecutor,
                        BroadcastDispatcher broadcastDispatcher,
                        ProtoTracer protoTracer,
                        NavigationModeController navigationModeController,
@@ -1067,6 +1110,7 @@
             mSysUiState = sysUiState;
             mPluginManager = pluginManager;
             mExecutor = executor;
+            mBackgroundExecutor = backgroundExecutor;
             mBroadcastDispatcher = broadcastDispatcher;
             mProtoTracer = protoTracer;
             mNavigationModeController = navigationModeController;
@@ -1089,6 +1133,7 @@
                     mSysUiState,
                     mPluginManager,
                     mExecutor,
+                    mBackgroundExecutor,
                     mBroadcastDispatcher,
                     mProtoTracer,
                     mNavigationModeController,
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java
index eba9d3f..122852f 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java
@@ -43,7 +43,6 @@
 import android.view.WindowManager;
 import android.view.animation.Interpolator;
 import android.view.animation.PathInterpolator;
-import android.window.BackEvent;
 
 import androidx.core.graphics.ColorUtils;
 import androidx.dynamicanimation.animation.DynamicAnimation;
@@ -59,7 +58,6 @@
 import com.android.systemui.plugins.NavigationEdgeBackPlugin;
 import com.android.systemui.shared.navigationbar.RegionSamplingHelper;
 import com.android.systemui.statusbar.VibratorHelper;
-import com.android.wm.shell.back.BackAnimation;
 
 import java.io.PrintWriter;
 import java.util.concurrent.Executor;
@@ -283,14 +281,11 @@
                 }
             };
     private BackCallback mBackCallback;
-    private BackAnimation mBackAnimation;
 
-    public NavigationBarEdgePanel(Context context,
-            BackAnimation backAnimation, LatencyTracker latencyTracker) {
+    public NavigationBarEdgePanel(Context context, LatencyTracker latencyTracker) {
         super(context);
 
         mWindowManager = context.getSystemService(WindowManager.class);
-        mBackAnimation = backAnimation;
         mVibratorHelper = Dependency.get(VibratorHelper.class);
 
         mDensity = context.getResources().getDisplayMetrics().density;
@@ -360,7 +355,6 @@
                 .getDimension(R.dimen.navigation_edge_action_drag_threshold);
         mSwipeProgressThreshold = context.getResources()
                 .getDimension(R.dimen.navigation_edge_action_progress_threshold);
-        initializeBackAnimation();
 
         setVisibility(GONE);
 
@@ -388,17 +382,6 @@
         mLatencyTracker = latencyTracker;
     }
 
-    public void setBackAnimation(BackAnimation backAnimation) {
-        mBackAnimation = backAnimation;
-        initializeBackAnimation();
-    }
-
-    private void initializeBackAnimation() {
-        if (mBackAnimation != null) {
-            mBackAnimation.setSwipeThresholds(mSwipeTriggerThreshold, mSwipeProgressThreshold);
-        }
-    }
-
     @Override
     public void onDestroy() {
         cancelFailsafe();
@@ -484,12 +467,6 @@
 
     @Override
     public void onMotionEvent(MotionEvent event) {
-        if (mBackAnimation != null) {
-            mBackAnimation.onBackMotion(
-                    event.getX(), event.getY(),
-                    event.getActionMasked(),
-                    mIsLeftPanel ? BackEvent.EDGE_LEFT : BackEvent.EDGE_RIGHT);
-        }
         if (mVelocityTracker == null) {
             mVelocityTracker = VelocityTracker.obtain();
         }
@@ -903,9 +880,6 @@
             // Whenever the trigger back state changes the existing translation animation should be
             // cancelled
             mTranslationAnimation.cancel();
-            if (mBackAnimation != null) {
-                mBackAnimation.setTriggerBack(triggerBack);
-            }
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
index 4552abd..77652c9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
@@ -110,6 +110,11 @@
     private Context mUserContext;
     private UserTracker mUserTracker;
     private SecureSettings mSecureSettings;
+    // Keep track of whether mTilesList contains the same information as the Settings value.
+    // This is a performance optimization to reduce the number of blocking calls to Settings from
+    // main thread.
+    // This is enforced by only cleaning the flag at the end of a successful run of #onTuningChanged
+    private boolean mTilesListDirty = true;
 
     private final TileServiceRequestController mTileServiceRequestController;
     private TileLifecycleManager.Factory mTileLifeCycleManagerFactory;
@@ -374,6 +379,7 @@
                 // the ones that are in the setting, update the Setting.
                 saveTilesToSettings(mTileSpecs);
             }
+            mTilesListDirty = false;
             for (int i = 0; i < mCallbacks.size(); i++) {
                 mCallbacks.get(i).onTilesChanged();
             }
@@ -436,6 +442,7 @@
         );
     }
 
+    // When calling this, you may want to modify mTilesListDirty accordingly.
     @MainThread
     private void saveTilesToSettings(List<String> tileSpecs) {
         mSecureSettings.putStringForUser(TILES_SETTING, TextUtils.join(",", tileSpecs),
@@ -445,9 +452,15 @@
 
     @MainThread
     private void changeTileSpecs(Predicate<List<String>> changeFunction) {
-        final String setting = mSecureSettings.getStringForUser(TILES_SETTING, mCurrentUser);
-        final List<String> tileSpecs = loadTileSpecs(mContext, setting);
+        final List<String> tileSpecs;
+        if (!mTilesListDirty) {
+            tileSpecs = new ArrayList<>(mTileSpecs);
+        } else {
+            tileSpecs = loadTileSpecs(mContext,
+                    mSecureSettings.getStringForUser(TILES_SETTING, mCurrentUser));
+        }
         if (changeFunction.test(tileSpecs)) {
+            mTilesListDirty = true;
             saveTilesToSettings(tileSpecs);
         }
     }
@@ -507,6 +520,7 @@
             }
         }
         if (DEBUG) Log.d(TAG, "saveCurrentTiles " + newTiles);
+        mTilesListDirty = true;
         saveTilesToSettings(newTiles);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
index ec0d081..eeb1010 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
@@ -134,18 +134,9 @@
         mQSCarrierGroupController
                 .setOnSingleCarrierChangedListener(mView::setIsSingleCarrier);
 
-        List<String> rssiIgnoredSlots;
-
-        if (mFeatureFlags.isEnabled(Flags.COMBINED_STATUS_BAR_SIGNAL_ICONS)) {
-            rssiIgnoredSlots = List.of(
-                    getResources().getString(com.android.internal.R.string.status_bar_no_calling),
-                    getResources().getString(com.android.internal.R.string.status_bar_call_strength)
-            );
-        } else {
-            rssiIgnoredSlots = List.of(
-                    getResources().getString(com.android.internal.R.string.status_bar_mobile)
-            );
-        }
+        List<String> rssiIgnoredSlots = List.of(
+                getResources().getString(com.android.internal.R.string.status_bar_mobile)
+        );
 
         mView.onAttach(mIconManager, mQSExpansionPathInterpolator, rssiIgnoredSlots,
                 mInsetsProvider, mFeatureFlags.isEnabled(Flags.COMBINED_QS_HEADERS));
diff --git a/packages/SystemUI/src/com/android/systemui/qs/carrier/CellSignalState.kt b/packages/SystemUI/src/com/android/systemui/qs/carrier/CellSignalState.kt
index 2dac639..e925b54 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/carrier/CellSignalState.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/carrier/CellSignalState.kt
@@ -27,7 +27,6 @@
     @JvmField val contentDescription: String? = null,
     @JvmField val typeContentDescription: String? = null,
     @JvmField val roaming: Boolean = false,
-    @JvmField val providerModelBehavior: Boolean = false
 ) {
     /**
      * Changes the visibility of this state by returning a copy with the visibility changed.
@@ -41,4 +40,4 @@
         if (this.visible == visible) return this
         else return copy(visible = visible)
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java
index 592da65..703b95a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java
@@ -45,7 +45,7 @@
     private View mSpacer;
     @Nullable
     private CellSignalState mLastSignalState;
-    private boolean mProviderModelInitialized = false;
+    private boolean mMobileSignalInitialized = false;
     private boolean mIsSingleCarrier;
 
     public QSCarrier(Context context) {
@@ -96,35 +96,25 @@
             mMobileRoaming.setImageTintList(colorStateList);
             mMobileSignal.setImageTintList(colorStateList);
 
-            if (state.providerModelBehavior) {
-                if (!mProviderModelInitialized) {
-                    mProviderModelInitialized = true;
-                    mMobileSignal.setImageDrawable(
-                            mContext.getDrawable(R.drawable.ic_qs_no_calling_sms));
-                }
-                mMobileSignal.setImageDrawable(mContext.getDrawable(state.mobileSignalIconId));
-                mMobileSignal.setContentDescription(state.contentDescription);
-            } else {
-                if (!mProviderModelInitialized) {
-                    mProviderModelInitialized = true;
-                    mMobileSignal.setImageDrawable(new SignalDrawable(mContext));
-                }
-                mMobileSignal.setImageLevel(state.mobileSignalIconId);
-                StringBuilder contentDescription = new StringBuilder();
-                if (state.contentDescription != null) {
-                    contentDescription.append(state.contentDescription).append(", ");
-                }
-                if (state.roaming) {
-                    contentDescription
-                            .append(mContext.getString(R.string.data_connection_roaming))
-                            .append(", ");
-                }
-                // TODO: show mobile data off/no internet text for 5 seconds before carrier text
-                if (hasValidTypeContentDescription(state.typeContentDescription)) {
-                    contentDescription.append(state.typeContentDescription);
-                }
-                mMobileSignal.setContentDescription(contentDescription);
+            if (!mMobileSignalInitialized) {
+                mMobileSignalInitialized = true;
+                mMobileSignal.setImageDrawable(new SignalDrawable(mContext));
             }
+            mMobileSignal.setImageLevel(state.mobileSignalIconId);
+            StringBuilder contentDescription = new StringBuilder();
+            if (state.contentDescription != null) {
+                contentDescription.append(state.contentDescription).append(", ");
+            }
+            if (state.roaming) {
+                contentDescription
+                        .append(mContext.getString(R.string.data_connection_roaming))
+                        .append(", ");
+            }
+            // TODO: show mobile data off/no internet text for 5 seconds before carrier text
+            if (hasValidTypeContentDescription(state.typeContentDescription)) {
+                contentDescription.append(state.typeContentDescription);
+            }
+            mMobileSignal.setContentDescription(contentDescription);
         }
         return true;
     }
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 209d09d..6a8bf75 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java
@@ -42,10 +42,7 @@
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
 import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.statusbar.connectivity.IconState;
 import com.android.systemui.statusbar.connectivity.MobileDataIndicators;
 import com.android.systemui.statusbar.connectivity.NetworkController;
 import com.android.systemui.statusbar.connectivity.SignalCallback;
@@ -78,7 +75,6 @@
     private QSCarrier[] mCarrierGroups = new QSCarrier[SIM_SLOTS];
     private int[] mLastSignalLevel = new int[SIM_SLOTS];
     private String[] mLastSignalLevelDescription = new String[SIM_SLOTS];
-    private final boolean mProviderModel;
     private final CarrierConfigTracker mCarrierConfigTracker;
 
     private boolean mIsSingleCarrier;
@@ -90,9 +86,6 @@
     private final SignalCallback mSignalCallback = new SignalCallback() {
                 @Override
                 public void setMobileDataIndicators(@NonNull MobileDataIndicators indicators) {
-                    if (mProviderModel) {
-                        return;
-                    }
                     int slotIndex = getSlotIndex(indicators.subId);
                     if (slotIndex >= SIM_SLOTS) {
                         Log.w(TAG, "setMobileDataIndicators - slot: " + slotIndex);
@@ -107,91 +100,12 @@
                             indicators.statusIcon.icon,
                             indicators.statusIcon.contentDescription,
                             indicators.typeContentDescription.toString(),
-                            indicators.roaming,
-                            mProviderModel
+                            indicators.roaming
                     );
                     mMainHandler.obtainMessage(H.MSG_UPDATE_STATE).sendToTarget();
                 }
 
                 @Override
-                public void setCallIndicator(@NonNull IconState statusIcon, int subId) {
-                    if (!mProviderModel) {
-                        return;
-                    }
-                    int slotIndex = getSlotIndex(subId);
-                    if (slotIndex >= SIM_SLOTS) {
-                        Log.w(TAG, "setMobileDataIndicators - slot: " + slotIndex);
-                        return;
-                    }
-                    if (slotIndex == SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
-                        Log.e(TAG, "Invalid SIM slot index for subscription: " + subId);
-                        return;
-                    }
-
-                    boolean displayCallStrengthIcon =
-                            mCarrierConfigTracker.getCallStrengthConfig(subId);
-
-                    if (statusIcon.icon == R.drawable.ic_qs_no_calling_sms) {
-                        if (statusIcon.visible) {
-                            mInfos[slotIndex] = new CellSignalState(
-                                    true,
-                                    statusIcon.icon,
-                                    statusIcon.contentDescription,
-                                    "",
-                                    false,
-                                    mProviderModel);
-                        } else {
-                            // Whenever the no Calling & SMS state is cleared, switched to the last
-                            // known call strength icon.
-                            if (displayCallStrengthIcon) {
-                                mInfos[slotIndex] = new CellSignalState(
-                                        true,
-                                        mLastSignalLevel[slotIndex],
-                                        mLastSignalLevelDescription[slotIndex],
-                                        "",
-                                        false,
-                                        mProviderModel);
-                            } else {
-                                mInfos[slotIndex] = new CellSignalState(
-                                        true,
-                                        R.drawable.ic_qs_sim_card,
-                                        "",
-                                        "",
-                                        false,
-                                        mProviderModel);
-                            }
-                        }
-                        mMainHandler.obtainMessage(H.MSG_UPDATE_STATE).sendToTarget();
-                    } else {
-                        mLastSignalLevel[slotIndex] = statusIcon.icon;
-                        mLastSignalLevelDescription[slotIndex] = statusIcon.contentDescription;
-                        // Only Shows the call strength icon when the no Calling & SMS icon is not
-                        // shown.
-                        if (mInfos[slotIndex].mobileSignalIconId
-                                != R.drawable.ic_qs_no_calling_sms) {
-                            if (displayCallStrengthIcon) {
-                                mInfos[slotIndex] = new CellSignalState(
-                                        true,
-                                        statusIcon.icon,
-                                        statusIcon.contentDescription,
-                                        "",
-                                        false,
-                                        mProviderModel);
-                            } else {
-                                mInfos[slotIndex] = new CellSignalState(
-                                        true,
-                                        R.drawable.ic_qs_sim_card,
-                                        "",
-                                        "",
-                                        false,
-                                        mProviderModel);
-                            }
-                            mMainHandler.obtainMessage(H.MSG_UPDATE_STATE).sendToTarget();
-                        }
-                    }
-                }
-
-                @Override
                 public void setNoSims(boolean hasNoSims, boolean simDetected) {
                     if (hasNoSims) {
                         for (int i = 0; i < SIM_SLOTS; i++) {
@@ -219,14 +133,8 @@
             @Background Handler bgHandler, @Main Looper mainLooper,
             NetworkController networkController,
             CarrierTextManager.Builder carrierTextManagerBuilder, Context context,
-            CarrierConfigTracker carrierConfigTracker, FeatureFlags featureFlags,
-            SlotIndexResolver slotIndexResolver) {
+            CarrierConfigTracker carrierConfigTracker, SlotIndexResolver slotIndexResolver) {
 
-        if (featureFlags.isEnabled(Flags.COMBINED_STATUS_BAR_SIGNAL_ICONS)) {
-            mProviderModel = true;
-        } else {
-            mProviderModel = false;
-        }
         mActivityStarter = activityStarter;
         mBgHandler = bgHandler;
         mNetworkController = networkController;
@@ -262,8 +170,7 @@
                     R.drawable.ic_qs_no_calling_sms,
                     context.getText(AccessibilityContentDescriptions.NO_CALLING).toString(),
                     "",
-                    false,
-                    mProviderModel);
+                    false);
             mLastSignalLevel[i] = TelephonyIcons.MOBILE_CALL_STRENGTH_ICONS[0];
             mLastSignalLevelDescription[i] =
                     context.getText(AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0])
@@ -351,8 +258,7 @@
             for (int i = 0; i < SIM_SLOTS; i++) {
                 if (mInfos[i].visible
                         && mInfos[i].mobileSignalIconId == R.drawable.ic_qs_sim_card) {
-                    mInfos[i] = new CellSignalState(true, R.drawable.ic_blank, "", "", false,
-                            mProviderModel);
+                    mInfos[i] = new CellSignalState(true, R.drawable.ic_blank, "", "", false);
                 }
             }
         }
@@ -470,15 +376,13 @@
         private final CarrierTextManager.Builder mCarrierTextControllerBuilder;
         private final Context mContext;
         private final CarrierConfigTracker mCarrierConfigTracker;
-        private final FeatureFlags mFeatureFlags;
         private final SlotIndexResolver mSlotIndexResolver;
 
         @Inject
         public Builder(ActivityStarter activityStarter, @Background Handler handler,
                 @Main Looper looper, NetworkController networkController,
                 CarrierTextManager.Builder carrierTextControllerBuilder, Context context,
-                CarrierConfigTracker carrierConfigTracker, FeatureFlags featureFlags,
-                SlotIndexResolver slotIndexResolver) {
+                CarrierConfigTracker carrierConfigTracker, SlotIndexResolver slotIndexResolver) {
             mActivityStarter = activityStarter;
             mHandler = handler;
             mLooper = looper;
@@ -486,7 +390,6 @@
             mCarrierTextControllerBuilder = carrierTextControllerBuilder;
             mContext = context;
             mCarrierConfigTracker = carrierConfigTracker;
-            mFeatureFlags = featureFlags;
             mSlotIndexResolver = slotIndexResolver;
         }
 
@@ -498,7 +401,7 @@
         public QSCarrierGroupController build() {
             return new QSCarrierGroupController(mView, mActivityStarter, mHandler, mLooper,
                     mNetworkController, mCarrierTextControllerBuilder, mContext,
-                    mCarrierConfigTracker, mFeatureFlags, mSlotIndexResolver);
+                    mCarrierConfigTracker, mSlotIndexResolver);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt b/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt
index 86ef858..ab795fa 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt
@@ -69,36 +69,63 @@
         })
     }
 
-    fun logTileClick(tileSpec: String, statusBarState: Int, state: Int) {
+    fun logTileClick(tileSpec: String, statusBarState: Int, state: Int, eventId: Int) {
         log(DEBUG, {
             str1 = tileSpec
-            int1 = statusBarState
+            int1 = eventId
             str2 = StatusBarState.toString(statusBarState)
             str3 = toStateString(state)
         }, {
-            "[$str1] Tile clicked. StatusBarState=$str2. TileState=$str3"
+            "[$str1][$int1] Tile clicked. StatusBarState=$str2. TileState=$str3"
         })
     }
 
-    fun logTileSecondaryClick(tileSpec: String, statusBarState: Int, state: Int) {
+    fun logHandleClick(tileSpec: String, eventId: Int) {
         log(DEBUG, {
             str1 = tileSpec
-            int1 = statusBarState
-            str2 = StatusBarState.toString(statusBarState)
-            str3 = toStateString(state)
+            int1 = eventId
         }, {
-            "[$str1] Tile long clicked. StatusBarState=$str2. TileState=$str3"
+            "[$str1][$int1] Tile handling click."
         })
     }
 
-    fun logTileLongClick(tileSpec: String, statusBarState: Int, state: Int) {
+    fun logTileSecondaryClick(tileSpec: String, statusBarState: Int, state: Int, eventId: Int) {
         log(DEBUG, {
             str1 = tileSpec
-            int1 = statusBarState
+            int1 = eventId
             str2 = StatusBarState.toString(statusBarState)
             str3 = toStateString(state)
         }, {
-            "[$str1] Tile long clicked. StatusBarState=$str2. TileState=$str3"
+            "[$str1][$int1] Tile secondary clicked. StatusBarState=$str2. TileState=$str3"
+        })
+    }
+
+    fun logHandleSecondaryClick(tileSpec: String, eventId: Int) {
+        log(DEBUG, {
+            str1 = tileSpec
+            int1 = eventId
+        }, {
+            "[$str1][$int1] Tile handling secondary click."
+        })
+    }
+
+    fun logTileLongClick(tileSpec: String, statusBarState: Int, state: Int, eventId: Int) {
+        log(DEBUG, {
+            str1 = tileSpec
+            int1 = eventId
+            str2 = StatusBarState.toString(statusBarState)
+            str3 = toStateString(state)
+        }, {
+            "[$str1][$int1] Tile long clicked. StatusBarState=$str2. TileState=$str3"
+        })
+    }
+
+    fun logHandleLongClick(tileSpec: String, eventId: Int) {
+        log(DEBUG, {
+            str1 = tileSpec
+            int1 = eventId
+        }, {
+            "[$str1][$int1] Tile handling long click."
         })
     }
 
@@ -144,4 +171,4 @@
     ) {
         buffer.log(TAG, logLevel, initializer, printer)
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
index 740e12a..2cffe89 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
@@ -105,6 +105,9 @@
     private final FalsingManager mFalsingManager;
     protected final QSLogger mQSLogger;
     private volatile int mReadyState;
+    // Keeps track of the click event, to match it with the handling in the background thread
+    // Only read and modified in main thread (where click events come through).
+    private int mClickEventId = 0;
 
     private final ArrayList<Callback> mCallbacks = new ArrayList<>();
     private final Object mStaleListener = new Object();
@@ -295,9 +298,11 @@
                         mStatusBarStateController.getState())));
         mUiEventLogger.logWithInstanceId(QSEvent.QS_ACTION_CLICK, 0, getMetricsSpec(),
                 getInstanceId());
-        mQSLogger.logTileClick(mTileSpec, mStatusBarStateController.getState(), mState.state);
+        final int eventId = mClickEventId++;
+        mQSLogger.logTileClick(mTileSpec, mStatusBarStateController.getState(), mState.state,
+                eventId);
         if (!mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
-            mHandler.obtainMessage(H.CLICK, view).sendToTarget();
+            mHandler.obtainMessage(H.CLICK, eventId, 0, view).sendToTarget();
         }
     }
 
@@ -307,9 +312,10 @@
                         mStatusBarStateController.getState())));
         mUiEventLogger.logWithInstanceId(QSEvent.QS_ACTION_SECONDARY_CLICK, 0, getMetricsSpec(),
                 getInstanceId());
+        final int eventId = mClickEventId++;
         mQSLogger.logTileSecondaryClick(mTileSpec, mStatusBarStateController.getState(),
-                mState.state);
-        mHandler.obtainMessage(H.SECONDARY_CLICK, view).sendToTarget();
+                mState.state, eventId);
+        mHandler.obtainMessage(H.SECONDARY_CLICK, eventId, 0, view).sendToTarget();
     }
 
     @Override
@@ -319,8 +325,10 @@
                         mStatusBarStateController.getState())));
         mUiEventLogger.logWithInstanceId(QSEvent.QS_ACTION_LONG_PRESS, 0, getMetricsSpec(),
                 getInstanceId());
-        mQSLogger.logTileLongClick(mTileSpec, mStatusBarStateController.getState(), mState.state);
-        mHandler.obtainMessage(H.LONG_CLICK, view).sendToTarget();
+        final int eventId = mClickEventId++;
+        mQSLogger.logTileLongClick(mTileSpec, mStatusBarStateController.getState(), mState.state,
+                eventId);
+        mHandler.obtainMessage(H.LONG_CLICK, eventId, 0, view).sendToTarget();
     }
 
     public LogMaker populate(LogMaker logMaker) {
@@ -590,13 +598,16 @@
                                 mContext, mEnforcedAdmin);
                         mActivityStarter.postStartActivityDismissingKeyguard(intent, 0);
                     } else {
+                        mQSLogger.logHandleClick(mTileSpec, msg.arg1);
                         handleClick((View) msg.obj);
                     }
                 } else if (msg.what == SECONDARY_CLICK) {
                     name = "handleSecondaryClick";
+                    mQSLogger.logHandleSecondaryClick(mTileSpec, msg.arg1);
                     handleSecondaryClick((View) msg.obj);
                 } else if (msg.what == LONG_CLICK) {
                     name = "handleLongClick";
+                    mQSLogger.logHandleLongClick(mTileSpec, msg.arg1);
                     handleLongClick((View) msg.obj);
                 } else if (msg.what == REFRESH_STATE) {
                     name = "handleRefreshState";
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 f629a036..d5efe36 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
@@ -806,7 +806,8 @@
             return;
         }
 
-        mTelephonyManager.setDataEnabled(enabled);
+        mTelephonyManager.setDataEnabledForReason(
+                TelephonyManager.DATA_ENABLED_REASON_USER, enabled);
         if (disableOtherSubscriptions) {
             final List<SubscriptionInfo> subInfoList =
                     mSubscriptionManager.getActiveSubscriptionInfoList();
diff --git a/packages/SystemUI/src/com/android/systemui/ripple/RippleShader.kt b/packages/SystemUI/src/com/android/systemui/ripple/RippleShader.kt
index 0a8e6e2..56a1874 100644
--- a/packages/SystemUI/src/com/android/systemui/ripple/RippleShader.kt
+++ b/packages/SystemUI/src/com/android/systemui/ripple/RippleShader.kt
@@ -39,7 +39,7 @@
         ROUNDED_BOX,
         ELLIPSE
     }
-
+    //language=AGSL
     companion object {
         private const val SHADER_UNIFORMS = """uniform vec2 in_center;
                 uniform vec2 in_size;
diff --git a/packages/SystemUI/src/com/android/systemui/ripple/RippleShaderUtilLibrary.kt b/packages/SystemUI/src/com/android/systemui/ripple/RippleShaderUtilLibrary.kt
index 0cacbc2..6de4648 100644
--- a/packages/SystemUI/src/com/android/systemui/ripple/RippleShaderUtilLibrary.kt
+++ b/packages/SystemUI/src/com/android/systemui/ripple/RippleShaderUtilLibrary.kt
@@ -17,6 +17,7 @@
 
 /** A common utility functions that are used for computing [RippleShader]. */
 class RippleShaderUtilLibrary {
+    //language=AGSL
     companion object {
         const val SHADER_LIB = """
             float triangleNoise(vec2 n) {
diff --git a/packages/SystemUI/src/com/android/systemui/ripple/RippleView.kt b/packages/SystemUI/src/com/android/systemui/ripple/RippleView.kt
index 83d9f2d..8b01201 100644
--- a/packages/SystemUI/src/com/android/systemui/ripple/RippleView.kt
+++ b/packages/SystemUI/src/com/android/systemui/ripple/RippleView.kt
@@ -39,7 +39,9 @@
 open class RippleView(context: Context?, attrs: AttributeSet?) : View(context, attrs) {
 
     private lateinit var rippleShader: RippleShader
-    private lateinit var rippleShape: RippleShape
+    lateinit var rippleShape: RippleShape
+        private set
+
     private val ripplePaint = Paint()
 
     var rippleInProgress: Boolean = false
diff --git a/packages/SystemUI/src/com/android/systemui/ripple/SdfShaderLibrary.kt b/packages/SystemUI/src/com/android/systemui/ripple/SdfShaderLibrary.kt
index 7f26146..5e256c6 100644
--- a/packages/SystemUI/src/com/android/systemui/ripple/SdfShaderLibrary.kt
+++ b/packages/SystemUI/src/com/android/systemui/ripple/SdfShaderLibrary.kt
@@ -17,6 +17,7 @@
 
 /** Library class that contains 2D signed distance functions. */
 class SdfShaderLibrary {
+    //language=AGSL
     companion object {
         const val CIRCLE_SDF = """
             float sdCircle(vec2 p, float r) {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ImageCapture.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ImageCapture.kt
new file mode 100644
index 0000000..39f35a5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ImageCapture.kt
@@ -0,0 +1,26 @@
+/*
+ * 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.screenshot
+
+import android.graphics.Bitmap
+import android.graphics.Rect
+
+interface ImageCapture {
+
+    fun captureDisplay(displayId: Int, crop: Rect? = null): Bitmap?
+
+    fun captureTask(taskId: Int): Bitmap?
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ImageCaptureImpl.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ImageCaptureImpl.kt
new file mode 100644
index 0000000..258c436
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ImageCaptureImpl.kt
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.screenshot
+
+import android.app.IActivityTaskManager
+import android.graphics.Bitmap
+import android.graphics.Rect
+import android.hardware.display.DisplayManager
+import android.os.IBinder
+import android.util.Log
+import android.view.DisplayAddress
+import android.view.SurfaceControl
+import android.view.SurfaceControl.DisplayCaptureArgs
+import android.view.SurfaceControl.ScreenshotHardwareBuffer
+import androidx.annotation.VisibleForTesting
+import javax.inject.Inject
+
+private const val TAG = "ImageCaptureImpl"
+
+open class ImageCaptureImpl @Inject constructor(
+    private val displayManager: DisplayManager,
+    private val atmService: IActivityTaskManager
+) : ImageCapture {
+
+    override fun captureDisplay(displayId: Int, crop: Rect?): Bitmap? {
+        val width = crop?.width() ?: 0
+        val height = crop?.height() ?: 0
+        val sourceCrop = crop ?: Rect()
+        val displayToken = physicalDisplayToken(displayId) ?: return null
+        val buffer = captureDisplay(displayToken, width, height, sourceCrop)
+
+        return buffer?.asBitmap()
+    }
+
+    override fun captureTask(taskId: Int): Bitmap? {
+        val snapshot = atmService.takeTaskSnapshot(taskId)
+        return Bitmap.wrapHardwareBuffer(snapshot.hardwareBuffer, snapshot.colorSpace)
+    }
+
+    @VisibleForTesting
+    open fun physicalDisplayToken(displayId: Int): IBinder? {
+        val display = displayManager.getDisplay(displayId)
+        if (display == null) {
+            Log.e(TAG, "No display with id: $displayId")
+            return null
+        }
+        val address = display.address
+        if (address !is DisplayAddress.Physical) {
+            Log.e(TAG, "Display does not have a physical address: $display")
+            return null
+        }
+        return SurfaceControl.getPhysicalDisplayToken(address.physicalDisplayId)
+    }
+
+    @VisibleForTesting
+    open fun captureDisplay(displayToken: IBinder, width: Int, height: Int, crop: Rect): ScreenshotHardwareBuffer? {
+        val captureArgs = DisplayCaptureArgs.Builder(displayToken)
+            .setSize(width, height)
+            .setSourceCrop(crop)
+            .build()
+        return SurfaceControl.captureDisplay(captureArgs)
+    }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/RequestProcessor.kt b/packages/SystemUI/src/com/android/systemui/screenshot/RequestProcessor.kt
new file mode 100644
index 0000000..beb54c8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/RequestProcessor.kt
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.screenshot
+
+import android.net.Uri
+import android.util.Log
+import android.view.WindowManager.ScreenshotType
+import android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN
+import android.view.WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE
+import android.view.WindowManager.TAKE_SCREENSHOT_SELECTED_REGION
+import com.android.internal.util.ScreenshotHelper.HardwareBitmapBundler
+import com.android.internal.util.ScreenshotHelper.ScreenshotRequest
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.screenshot.TakeScreenshotService.RequestCallback
+import java.util.function.Consumer
+import javax.inject.Inject
+
+/**
+ * Processes a screenshot request sent from {@link ScreenshotHelper}.
+ */
+@SysUISingleton
+internal class RequestProcessor @Inject constructor(
+    private val controller: ScreenshotController,
+) {
+    fun processRequest(
+        @ScreenshotType type: Int,
+        onSavedListener: Consumer<Uri>,
+        request: ScreenshotRequest,
+        callback: RequestCallback
+    ) {
+
+        if (type == TAKE_SCREENSHOT_PROVIDED_IMAGE) {
+            val image = HardwareBitmapBundler.bundleToHardwareBitmap(request.bitmapBundle)
+
+            controller.handleImageAsScreenshot(
+                image, request.boundsInScreen, request.insets,
+                request.taskId, request.userId, request.topComponent, onSavedListener, callback
+            )
+            return
+        }
+
+        when (type) {
+            TAKE_SCREENSHOT_FULLSCREEN ->
+                controller.takeScreenshotFullscreen(null, onSavedListener, callback)
+            TAKE_SCREENSHOT_SELECTED_REGION ->
+                controller.takeScreenshotPartial(null, onSavedListener, callback)
+            else -> Log.w(TAG, "Invalid screenshot option: $type")
+        }
+    }
+
+    companion object {
+        const val TAG: String = "RequestProcessor"
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index 82de389..69ee8e8 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -57,14 +57,12 @@
 import android.media.MediaPlayer;
 import android.net.Uri;
 import android.os.Bundle;
-import android.os.IBinder;
 import android.os.RemoteException;
 import android.provider.Settings;
 import android.util.DisplayMetrics;
 import android.util.Log;
 import android.util.Pair;
 import android.view.Display;
-import android.view.DisplayAddress;
 import android.view.IRemoteAnimationFinishedCallback;
 import android.view.IRemoteAnimationRunner;
 import android.view.KeyEvent;
@@ -72,7 +70,6 @@
 import android.view.RemoteAnimationAdapter;
 import android.view.RemoteAnimationTarget;
 import android.view.ScrollCaptureResponse;
-import android.view.SurfaceControl;
 import android.view.View;
 import android.view.ViewRootImpl;
 import android.view.ViewTreeObserver;
@@ -249,6 +246,7 @@
     private final ScreenshotSmartActions mScreenshotSmartActions;
     private final UiEventLogger mUiEventLogger;
     private final ImageExporter mImageExporter;
+    private final ImageCapture mImageCapture;
     private final Executor mMainExecutor;
     private final ExecutorService mBgExecutor;
     private final BroadcastSender mBroadcastSender;
@@ -295,6 +293,7 @@
             ScrollCaptureClient scrollCaptureClient,
             UiEventLogger uiEventLogger,
             ImageExporter imageExporter,
+            ImageCapture imageCapture,
             @Main Executor mainExecutor,
             ScrollCaptureController scrollCaptureController,
             LongScreenshotData longScreenshotHolder,
@@ -308,6 +307,7 @@
         mScrollCaptureClient = scrollCaptureClient;
         mUiEventLogger = uiEventLogger;
         mImageExporter = imageExporter;
+        mImageCapture = imageCapture;
         mMainExecutor = mainExecutor;
         mScrollCaptureController = scrollCaptureController;
         mLongScreenshotHolder = longScreenshotHolder;
@@ -531,7 +531,7 @@
 
         // copy the input Rect, since SurfaceControl.screenshot can mutate it
         Rect screenRect = new Rect(crop);
-        Bitmap screenshot = captureScreenshot(crop);
+        Bitmap screenshot = mImageCapture.captureDisplay(DEFAULT_DISPLAY, crop);
 
         if (screenshot == null) {
             Log.e(TAG, "takeScreenshotInternal: Screenshot bitmap was null");
@@ -549,32 +549,6 @@
                 ClipboardOverlayController.SELF_PERMISSION);
     }
 
-    private Bitmap captureScreenshot(Rect crop) {
-        int width = crop.width();
-        int height = crop.height();
-        Bitmap screenshot = null;
-        final Display display = getDefaultDisplay();
-        final DisplayAddress address = display.getAddress();
-        if (!(address instanceof DisplayAddress.Physical)) {
-            Log.e(TAG, "Skipping Screenshot - Default display does not have a physical address: "
-                    + display);
-        } else {
-            final DisplayAddress.Physical physicalAddress = (DisplayAddress.Physical) address;
-
-            final IBinder displayToken = SurfaceControl.getPhysicalDisplayToken(
-                    physicalAddress.getPhysicalDisplayId());
-            final SurfaceControl.DisplayCaptureArgs captureArgs =
-                    new SurfaceControl.DisplayCaptureArgs.Builder(displayToken)
-                            .setSourceCrop(crop)
-                            .setSize(width, height)
-                            .build();
-            final SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer =
-                    SurfaceControl.captureDisplay(captureArgs);
-            screenshot = screenshotBuffer == null ? null : screenshotBuffer.asBitmap();
-        }
-        return screenshot;
-    }
-
     private void saveScreenshot(Bitmap screenshot, Consumer<Uri> finisher, Rect screenRect,
             Insets screenInsets, ComponentName topComponent, boolean showFlash) {
         withWindowAttached(() ->
@@ -720,7 +694,7 @@
             mScreenshotView.showScrollChip(response.getPackageName(), /* onClick */ () -> {
                 DisplayMetrics displayMetrics = new DisplayMetrics();
                 getDefaultDisplay().getRealMetrics(displayMetrics);
-                Bitmap newScreenshot = captureScreenshot(
+                Bitmap newScreenshot = mImageCapture.captureDisplay(DEFAULT_DISPLAY,
                         new Rect(0, 0, displayMetrics.widthPixels, displayMetrics.heightPixels));
 
                 mScreenshotView.prepareScrollingTransition(response, mScreenBitmap, newScreenshot,
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
index 32d8203..f1f0223 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
@@ -21,6 +21,7 @@
 
 import static com.android.internal.util.ScreenshotHelper.SCREENSHOT_MSG_PROCESS_COMPLETE;
 import static com.android.internal.util.ScreenshotHelper.SCREENSHOT_MSG_URI;
+import static com.android.systemui.flags.Flags.SCREENSHOT_REQUEST_PROCESSOR;
 import static com.android.systemui.screenshot.LogConfig.DEBUG_CALLBACK;
 import static com.android.systemui.screenshot.LogConfig.DEBUG_DISMISS;
 import static com.android.systemui.screenshot.LogConfig.DEBUG_SERVICE;
@@ -57,6 +58,8 @@
 import com.android.internal.util.ScreenshotHelper;
 import com.android.systemui.R;
 import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.FlagListenable.FlagEvent;
 
 import java.util.concurrent.Executor;
 import java.util.function.Consumer;
@@ -75,6 +78,8 @@
     private final Handler mHandler;
     private final Context mContext;
     private final @Background Executor mBgExecutor;
+    private final RequestProcessor mProcessor;
+    private final FeatureFlags mFeatureFlags;
 
     private final BroadcastReceiver mCloseSystemDialogs = new BroadcastReceiver() {
         @Override
@@ -104,7 +109,8 @@
     public TakeScreenshotService(ScreenshotController screenshotController, UserManager userManager,
             DevicePolicyManager devicePolicyManager, UiEventLogger uiEventLogger,
             ScreenshotNotificationsController notificationsController, Context context,
-            @Background Executor bgExecutor) {
+            @Background Executor bgExecutor, FeatureFlags featureFlags,
+            RequestProcessor processor) {
         if (DEBUG_SERVICE) {
             Log.d(TAG, "new " + this);
         }
@@ -116,6 +122,9 @@
         mNotificationsController = notificationsController;
         mContext = context;
         mBgExecutor = bgExecutor;
+        mFeatureFlags = featureFlags;
+        mFeatureFlags.addListener(SCREENSHOT_REQUEST_PROCESSOR, FlagEvent::requestNoRestart);
+        mProcessor = processor;
     }
 
     @Override
@@ -218,6 +227,12 @@
         mUiEventLogger.log(ScreenshotEvent.getScreenshotSource(screenshotRequest.getSource()), 0,
                 topComponent == null ? "" : topComponent.getPackageName());
 
+        if (mFeatureFlags.isEnabled(SCREENSHOT_REQUEST_PROCESSOR)) {
+            Log.d(TAG, "handleMessage: Using request processor");
+            mProcessor.processRequest(msg.what, uriConsumer, screenshotRequest, requestCallback);
+            return true;
+        }
+
         switch (msg.what) {
             case WindowManager.TAKE_SCREENSHOT_FULLSCREEN:
                 if (DEBUG_SERVICE) {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java b/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java
index 91ef3c3..3e442587 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java
@@ -18,6 +18,8 @@
 
 import android.app.Service;
 
+import com.android.systemui.screenshot.ImageCapture;
+import com.android.systemui.screenshot.ImageCaptureImpl;
 import com.android.systemui.screenshot.TakeScreenshotService;
 
 import dagger.Binds;
@@ -37,4 +39,6 @@
     @ClassKey(TakeScreenshotService.class)
     public abstract Service bindTakeScreenshotService(TakeScreenshotService service);
 
+    @Binds
+    public abstract ImageCapture bindImageCapture(ImageCaptureImpl capture);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt b/packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt
index 5793105..0f9ac36 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt
@@ -264,14 +264,8 @@
             Utils.getColorAttrDefaultColor(header.context, android.R.attr.textColorPrimary)
         )
 
-        carrierIconSlots = if (featureFlags.isEnabled(Flags.COMBINED_STATUS_BAR_SIGNAL_ICONS)) {
-            listOf(
-                header.context.getString(com.android.internal.R.string.status_bar_no_calling),
-                header.context.getString(com.android.internal.R.string.status_bar_call_strength)
-            )
-        } else {
+        carrierIconSlots =
             listOf(header.context.getString(com.android.internal.R.string.status_bar_mobile))
-        }
         qsCarrierGroupController = qsCarrierGroupControllerBuilder
             .setQSCarrierGroup(qsCarrierGroup)
             .build()
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index e36b67e..7f0e76b 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -1499,10 +1499,7 @@
     }
 
     private void updateKeyguardStatusViewAlignment(boolean animate) {
-        boolean hasVisibleNotifications = mNotificationStackScrollLayoutController
-                .getVisibleNotificationCount() != 0
-                || mMediaDataManager.hasActiveMediaOrRecommendation();
-        boolean shouldBeCentered = !mSplitShadeEnabled || !hasVisibleNotifications || mDozing;
+        boolean shouldBeCentered = shouldKeyguardStatusViewBeCentered();
         if (mStatusViewCentered != shouldBeCentered) {
             mStatusViewCentered = shouldBeCentered;
             ConstraintSet constraintSet = new ConstraintSet();
@@ -1526,6 +1523,15 @@
         mKeyguardUnfoldTransition.ifPresent(t -> t.setStatusViewCentered(mStatusViewCentered));
     }
 
+    private boolean shouldKeyguardStatusViewBeCentered() {
+        boolean hasVisibleNotifications = mNotificationStackScrollLayoutController
+                .getVisibleNotificationCount() != 0
+                || mMediaDataManager.hasActiveMediaOrRecommendation();
+        boolean isOnAod = mDozing && mDozeParameters.getAlwaysOn();
+        return !mSplitShadeEnabled || !hasVisibleNotifications || isOnAod
+                || hasPulsingNotifications();
+    }
+
     /**
      * @return the padding of the stackscroller when unlocked
      */
diff --git a/packages/SystemUI/src/com/android/systemui/shade/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/PanelViewController.java
index 1a8a6d1..4aad245 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/PanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/PanelViewController.java
@@ -171,6 +171,7 @@
     private float mInitialTouchY;
     private float mInitialTouchX;
     private boolean mTouchDisabled;
+    private boolean mInitialTouchFromKeyguard;
 
     /**
      * Whether or not the PanelView can be expanded or collapsed with a drag.
@@ -395,6 +396,7 @@
         mInitialOffsetOnTouch = expandedHeight;
         mInitialTouchY = newY;
         mInitialTouchX = newX;
+        mInitialTouchFromKeyguard = mStatusBarStateController.getState() == StatusBarState.KEYGUARD;
         if (startTracking) {
             mTouchSlopExceeded = true;
             setExpandedHeight(mInitialOffsetOnTouch);
@@ -417,20 +419,14 @@
                     mStatusBarStateController.getState() == StatusBarState.KEYGUARD;
 
             final boolean expand;
-            if (event.getActionMasked() == MotionEvent.ACTION_CANCEL || forceCancel) {
-                // If the keyguard is fading away, don't expand it again. This can happen if you're
-                // swiping to unlock, the app below the keyguard is in landscape, and the screen
-                // rotates while your finger is still down after the swipe to unlock.
-                if (mKeyguardStateController.isKeyguardFadingAway()) {
-                    expand = false;
-                } else if (onKeyguard) {
+            if (mKeyguardStateController.isKeyguardFadingAway()
+                    || (mInitialTouchFromKeyguard && !onKeyguard)) {
+                // Don't expand for any touches that started from the keyguard and ended after the
+                // keyguard is gone.
+                expand = false;
+            } else if (event.getActionMasked() == MotionEvent.ACTION_CANCEL || forceCancel) {
+                if (onKeyguard) {
                     expand = true;
-                } else if (mKeyguardStateController.isKeyguardFadingAway()) {
-                    // If we're in the middle of dismissing the keyguard, don't expand due to the
-                    // cancelled gesture. Gesture cancellation during an unlock is expected in some
-                    // situations, such keeping your finger down while swiping to unlock to an app
-                    // that is locked in landscape (the rotation will cancel the touch event).
-                    expand = false;
                 } else if (mCentralSurfaces.isBouncerShowingOverDream()) {
                     expand = false;
                 } else {
diff --git a/packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceModule.kt b/packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceModule.kt
index 1b74ac3..b02a45a 100644
--- a/packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceModule.kt
@@ -18,7 +18,7 @@
 import com.android.systemui.plugins.BcSmartspaceDataPlugin
 import com.android.systemui.smartspace.SmartspacePrecondition
 import com.android.systemui.smartspace.SmartspaceTargetFilter
-import com.android.systemui.smartspace.filters.LockscreenTargetFilter
+import com.android.systemui.smartspace.filters.LockscreenAndDreamTargetFilter
 import com.android.systemui.smartspace.preconditions.LockscreenPrecondition
 import dagger.Binds
 import dagger.BindsOptionalOf
@@ -61,7 +61,7 @@
     @Binds
     @Named(LOCKSCREEN_SMARTSPACE_TARGET_FILTER)
     abstract fun provideLockscreenSmartspaceTargetFilter(
-        filter: LockscreenTargetFilter?
+        filter: LockscreenAndDreamTargetFilter?
     ): SmartspaceTargetFilter?
 
     @Binds
@@ -69,4 +69,4 @@
     abstract fun bindSmartspacePrecondition(
         lockscreenPrecondition: LockscreenPrecondition?
     ): SmartspacePrecondition?
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/smartspace/filters/LockscreenTargetFilter.kt b/packages/SystemUI/src/com/android/systemui/smartspace/filters/LockscreenAndDreamTargetFilter.kt
similarity index 97%
rename from packages/SystemUI/src/com/android/systemui/smartspace/filters/LockscreenTargetFilter.kt
rename to packages/SystemUI/src/com/android/systemui/smartspace/filters/LockscreenAndDreamTargetFilter.kt
index 6ad4901..aa4cf75 100644
--- a/packages/SystemUI/src/com/android/systemui/smartspace/filters/LockscreenTargetFilter.kt
+++ b/packages/SystemUI/src/com/android/systemui/smartspace/filters/LockscreenAndDreamTargetFilter.kt
@@ -32,9 +32,9 @@
 import javax.inject.Inject
 
 /**
- * {@link SmartspaceTargetFilter} for smartspace targets that show above the lockscreen.
+ * {@link SmartspaceTargetFilter} for smartspace targets that show above the lockscreen and dreams.
  */
-class LockscreenTargetFilter @Inject constructor(
+class LockscreenAndDreamTargetFilter @Inject constructor(
     private val secureSettings: SecureSettings,
     private val userTracker: UserTracker,
     private val execution: Execution,
@@ -149,4 +149,4 @@
 
         listeners.forEach { it.onCriteriaChanged() }
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java
index 3013ad0..a57d849b2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java
@@ -61,21 +61,19 @@
     private int mVisibleState = -1;
     private DualToneHandler mDualToneHandler;
     private boolean mForceHidden;
-    private boolean mProviderModel;
 
     /**
      * Designated constructor
      */
     public static StatusBarMobileView fromContext(
             Context context,
-            String slot,
-            boolean providerModel
+            String slot
     ) {
         LayoutInflater inflater = LayoutInflater.from(context);
         StatusBarMobileView v = (StatusBarMobileView)
                 inflater.inflate(R.layout.status_bar_mobile_signal_group, null);
         v.setSlot(slot);
-        v.init(providerModel);
+        v.init();
         v.setVisibleState(STATE_ICON);
         return v;
     }
@@ -108,17 +106,12 @@
         outRect.bottom += translationY;
     }
 
-    private void init(boolean providerModel) {
-        mProviderModel = providerModel;
+    private void init() {
         mDualToneHandler = new DualToneHandler(getContext());
         mMobileGroup = findViewById(R.id.mobile_group);
         mMobile = findViewById(R.id.mobile_signal);
         mMobileType = findViewById(R.id.mobile_type);
-        if (mProviderModel) {
-            mMobileRoaming = findViewById(R.id.mobile_roaming_large);
-        } else {
-            mMobileRoaming = findViewById(R.id.mobile_roaming);
-        }
+        mMobileRoaming = findViewById(R.id.mobile_roaming);
         mMobileRoamingSpace = findViewById(R.id.mobile_roaming_space);
         mIn = findViewById(R.id.mobile_in);
         mOut = findViewById(R.id.mobile_out);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/CallbackHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/CallbackHandler.java
index 6914ae6..1638780 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/CallbackHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/CallbackHandler.java
@@ -21,6 +21,7 @@
 import android.telephony.SubscriptionInfo;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.statusbar.connectivity.NetworkController.EmergencyListener;
 
@@ -36,6 +37,7 @@
  * Implements network listeners and forwards the calls along onto other listeners but on
  * the current or specified Looper.
  */
+@SysUISingleton
 public class CallbackHandler extends Handler implements EmergencyListener, SignalCallback {
     private static final String TAG = "CallbackHandler";
     private static final int MSG_EMERGENCE_CHANGED           = 0;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java
index f2014e9..ec221b7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java
@@ -26,25 +26,17 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.provider.Settings.Global;
-import android.telephony.AccessNetworkConstants;
 import android.telephony.CellSignalStrength;
 import android.telephony.CellSignalStrengthCdma;
-import android.telephony.ServiceState;
 import android.telephony.SignalStrength;
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
-import android.telephony.ims.ImsException;
-import android.telephony.ims.ImsMmTelManager;
-import android.telephony.ims.ImsReasonInfo;
-import android.telephony.ims.ImsRegistrationAttributes;
-import android.telephony.ims.RegistrationManager.RegistrationCallback;
 import android.text.Html;
 import android.text.TextUtils;
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.settingslib.AccessibilityContentDescriptions;
 import com.android.settingslib.SignalIcon.MobileIconGroup;
 import com.android.settingslib.graph.SignalDrawable;
 import com.android.settingslib.mobile.MobileMappings.Config;
@@ -54,8 +46,6 @@
 import com.android.settingslib.mobile.TelephonyIcons;
 import com.android.settingslib.net.SignalStrengthUtil;
 import com.android.systemui.R;
-import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
 import com.android.systemui.util.CarrierConfigTracker;
 
 import java.io.PrintWriter;
@@ -70,33 +60,22 @@
 public class MobileSignalController extends SignalController<MobileState, MobileIconGroup> {
     private static final SimpleDateFormat SSDF = new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
     private static final int STATUS_HISTORY_SIZE = 64;
-    private static final int IMS_TYPE_WWAN = 1;
-    private static final int IMS_TYPE_WLAN = 2;
-    private static final int IMS_TYPE_WLAN_CROSS_SIM = 3;
     private final TelephonyManager mPhone;
     private final CarrierConfigTracker mCarrierConfigTracker;
-    private final ImsMmTelManager mImsMmTelManager;
     private final SubscriptionDefaults mDefaults;
     private final String mNetworkNameDefault;
     private final String mNetworkNameSeparator;
     private final ContentObserver mObserver;
-    private final boolean mProviderModelBehavior;
-    private final Handler mReceiverHandler;
-    private int mImsType = IMS_TYPE_WWAN;
     // Save entire info for logging, we only use the id.
     final SubscriptionInfo mSubscriptionInfo;
     private Map<String, MobileIconGroup> mNetworkToIconLookup;
 
-    private int mLastLevel;
     private MobileIconGroup mDefaultIcons;
     private Config mConfig;
     @VisibleForTesting
     boolean mInflateSignalStrengths = false;
-    private int mLastWwanLevel;
-    private int mLastWlanLevel;
-    private int mLastWlanCrossSimLevel;
     @VisibleForTesting
-    MobileStatusTracker mMobileStatusTracker;
+    final MobileStatusTracker mMobileStatusTracker;
 
     // Save the previous STATUS_HISTORY_SIZE states for logging.
     private final String[] mMobileStatusHistory = new String[STATUS_HISTORY_SIZE];
@@ -133,52 +112,6 @@
                 }
             };
 
-    private final RegistrationCallback mRegistrationCallback = new RegistrationCallback() {
-        @Override
-        public void onRegistered(ImsRegistrationAttributes attributes) {
-            Log.d(mTag, "onRegistered: " + "attributes=" + attributes);
-            int imsTransportType = attributes.getTransportType();
-            int registrationAttributes = attributes.getAttributeFlags();
-            if (imsTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) {
-                mImsType = IMS_TYPE_WWAN;
-                IconState statusIcon = new IconState(
-                        true,
-                        getCallStrengthIcon(mLastWwanLevel, /* isWifi= */false),
-                        getCallStrengthDescription(mLastWwanLevel, /* isWifi= */false));
-                notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
-            } else if (imsTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WLAN) {
-                if (registrationAttributes == 0) {
-                    mImsType = IMS_TYPE_WLAN;
-                    IconState statusIcon = new IconState(
-                            true,
-                            getCallStrengthIcon(mLastWlanLevel, /* isWifi= */true),
-                            getCallStrengthDescription(mLastWlanLevel, /* isWifi= */true));
-                    notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
-                } else if (registrationAttributes
-                        == ImsRegistrationAttributes.ATTR_EPDG_OVER_CELL_INTERNET) {
-                    mImsType = IMS_TYPE_WLAN_CROSS_SIM;
-                    IconState statusIcon = new IconState(
-                            true,
-                            getCallStrengthIcon(mLastWlanCrossSimLevel, /* isWifi= */false),
-                            getCallStrengthDescription(
-                                    mLastWlanCrossSimLevel, /* isWifi= */false));
-                    notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
-                }
-            }
-        }
-
-        @Override
-        public void onUnregistered(ImsReasonInfo info) {
-            Log.d(mTag, "onDeregistered: " + "info=" + info);
-            mImsType = IMS_TYPE_WWAN;
-            IconState statusIcon = new IconState(
-                    true,
-                    getCallStrengthIcon(mLastWwanLevel, /* isWifi= */false),
-                    getCallStrengthDescription(mLastWwanLevel, /* isWifi= */false));
-            notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
-        }
-    };
-
     // TODO: Reduce number of vars passed in, if we have the NetworkController, probably don't
     // need listener lists anymore.
     public MobileSignalController(
@@ -192,7 +125,7 @@
             SubscriptionDefaults defaults,
             Looper receiverLooper,
             CarrierConfigTracker carrierConfigTracker,
-            FeatureFlags featureFlags
+            MobileStatusTrackerFactory mobileStatusTrackerFactory
     ) {
         super("MobileSignalController(" + info.getSubscriptionId() + ")", context,
                 NetworkCapabilities.TRANSPORT_CELLULAR, callbackHandler,
@@ -206,7 +139,6 @@
                 R.string.status_bar_network_name_separator).toString();
         mNetworkNameDefault = getTextIfExists(
                 com.android.internal.R.string.lockscreen_carrier_default).toString();
-        mReceiverHandler = new Handler(receiverLooper);
 
         mNetworkToIconLookup = mapIconSets(mConfig);
         mDefaultIcons = getDefaultIcons(mConfig);
@@ -223,10 +155,7 @@
                 updateTelephony();
             }
         };
-        mImsMmTelManager = ImsMmTelManager.createForSubscriptionId(info.getSubscriptionId());
-        mMobileStatusTracker = new MobileStatusTracker(mPhone, receiverLooper,
-                info, mDefaults, mMobileCallback);
-        mProviderModelBehavior = featureFlags.isEnabled(Flags.COMBINED_STATUS_BAR_SIGNAL_ICONS);
+        mMobileStatusTracker = mobileStatusTrackerFactory.createTracker(mMobileCallback);
     }
 
     void setConfiguration(Config config) {
@@ -271,41 +200,14 @@
         mContext.getContentResolver().registerContentObserver(Global.getUriFor(
                 Global.MOBILE_DATA + mSubscriptionInfo.getSubscriptionId()),
                 true, mObserver);
-        if (mProviderModelBehavior) {
-            mReceiverHandler.post(mTryRegisterIms);
-        }
     }
 
-    // There is no listener to monitor whether the IMS service is ready, so we have to retry the
-    // IMS registration.
-    private final Runnable mTryRegisterIms = new Runnable() {
-        private static final int MAX_RETRY = 12;
-        private int mRetryCount;
-
-        @Override
-        public void run() {
-            try {
-                mRetryCount++;
-                mImsMmTelManager.registerImsRegistrationCallback(
-                        mReceiverHandler::post, mRegistrationCallback);
-                Log.d(mTag, "registerImsRegistrationCallback succeeded");
-            } catch (RuntimeException | ImsException e) {
-                if (mRetryCount < MAX_RETRY) {
-                    Log.e(mTag, mRetryCount + " registerImsRegistrationCallback failed", e);
-                    // Wait for 5 seconds to retry
-                    mReceiverHandler.postDelayed(mTryRegisterIms, 5000);
-                }
-            }
-        }
-    };
-
     /**
      * Stop listening for phone state changes.
      */
     public void unregisterListener() {
         mMobileStatusTracker.setListening(false);
         mContext.getContentResolver().unregisterContentObserver(mObserver);
-        mImsMmTelManager.unregisterImsRegistrationCallback(mRegistrationCallback);
     }
 
     private void updateInflateSignalStrength() {
@@ -394,7 +296,7 @@
         CharSequence qsDescription = null;
 
         if (mCurrentState.dataSim) {
-            // If using provider model behavior, only show QS icons if the state is also default
+            // only show QS icons if the state is also default
             if (!mCurrentState.isDefault) {
                 return new QsInfo(qsTypeIcon, qsIcon, qsDescription);
             }
@@ -416,32 +318,15 @@
 
     private SbInfo getSbInfo(String contentDescription, int dataTypeIcon) {
         final boolean dataDisabled = mCurrentState.isDataDisabledOrNotDefault();
-        boolean showTriangle = false;
-        int typeIcon = 0;
-        IconState statusIcon = null;
+        IconState statusIcon = new IconState(
+                mCurrentState.enabled && !mCurrentState.airplaneMode,
+                getCurrentIconId(), contentDescription);
 
-        if (mProviderModelBehavior) {
-            boolean showDataIconStatusBar = (mCurrentState.dataConnected || dataDisabled)
-                    && (mCurrentState.dataSim && mCurrentState.isDefault);
-            typeIcon =
-                    (showDataIconStatusBar || mConfig.alwaysShowDataRatIcon) ? dataTypeIcon : 0;
-            showDataIconStatusBar |= mCurrentState.roaming;
-            statusIcon = new IconState(
-                    showDataIconStatusBar && !mCurrentState.airplaneMode,
-                    getCurrentIconId(), contentDescription);
-
-            showTriangle = showDataIconStatusBar && !mCurrentState.airplaneMode;
-        } else {
-            statusIcon = new IconState(
-                    mCurrentState.enabled && !mCurrentState.airplaneMode,
-                    getCurrentIconId(), contentDescription);
-
-            boolean showDataIconInStatusBar =
-                    (mCurrentState.dataConnected && mCurrentState.isDefault) || dataDisabled;
-            typeIcon =
-                    (showDataIconInStatusBar || mConfig.alwaysShowDataRatIcon) ? dataTypeIcon : 0;
-            showTriangle = mCurrentState.enabled && !mCurrentState.airplaneMode;
-        }
+        boolean showDataIconInStatusBar =
+                (mCurrentState.dataConnected && mCurrentState.isDefault) || dataDisabled;
+        int typeIcon =
+                (showDataIconInStatusBar || mConfig.alwaysShowDataRatIcon) ? dataTypeIcon : 0;
+        boolean showTriangle = mCurrentState.enabled && !mCurrentState.airplaneMode;
 
         return new SbInfo(showTriangle, typeIcon, statusIcon);
     }
@@ -560,144 +445,7 @@
     }
 
     private void updateMobileStatus(MobileStatus mobileStatus) {
-        int lastVoiceState = mCurrentState.getVoiceServiceState();
         mCurrentState.setFromMobileStatus(mobileStatus);
-
-        notifyMobileLevelChangeIfNecessary(mobileStatus.signalStrength);
-        if (mProviderModelBehavior) {
-            maybeNotifyCallStateChanged(lastVoiceState);
-        }
-    }
-
-    /** Call state changed is only applicable when provider model behavior is true */
-    private void maybeNotifyCallStateChanged(int lastVoiceState) {
-        int currentVoiceState = mCurrentState.getVoiceServiceState();
-        if (lastVoiceState == currentVoiceState) {
-            return;
-        }
-        // Only update the no calling Status in the below scenarios
-        // 1. The first valid voice state has been received
-        // 2. The voice state has been changed and either the last or current state is
-        //    ServiceState.STATE_IN_SERVICE
-        if (lastVoiceState == -1
-                || (lastVoiceState == ServiceState.STATE_IN_SERVICE
-                        || currentVoiceState == ServiceState.STATE_IN_SERVICE)) {
-            boolean isNoCalling = mCurrentState.isNoCalling();
-            isNoCalling &= !hideNoCalling();
-            IconState statusIcon = new IconState(isNoCalling,
-                    R.drawable.ic_qs_no_calling_sms,
-                    getTextIfExists(AccessibilityContentDescriptions.NO_CALLING).toString());
-            notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
-        }
-    }
-
-    void updateNoCallingState() {
-        int currentVoiceState = mCurrentState.getVoiceServiceState();
-        boolean isNoCalling = currentVoiceState != ServiceState.STATE_IN_SERVICE;
-        isNoCalling &= !hideNoCalling();
-        IconState statusIcon = new IconState(isNoCalling,
-                R.drawable.ic_qs_no_calling_sms,
-                getTextIfExists(AccessibilityContentDescriptions.NO_CALLING).toString());
-        notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
-    }
-
-    private boolean hideNoCalling() {
-        return mNetworkController.hasDefaultNetwork()
-                && mCarrierConfigTracker.getNoCallingConfig(mSubscriptionInfo.getSubscriptionId());
-    }
-
-    private int getCallStrengthIcon(int level, boolean isWifi) {
-        return isWifi ? TelephonyIcons.WIFI_CALL_STRENGTH_ICONS[level]
-                : TelephonyIcons.MOBILE_CALL_STRENGTH_ICONS[level];
-    }
-
-    private String getCallStrengthDescription(int level, boolean isWifi) {
-        return isWifi
-                ? getTextIfExists(AccessibilityContentDescriptions.WIFI_CONNECTION_STRENGTH[level])
-                        .toString()
-                : getTextIfExists(AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[level])
-                        .toString();
-    }
-
-    void refreshCallIndicator(SignalCallback callback) {
-        boolean isNoCalling = mCurrentState.isNoCalling();
-        isNoCalling &= !hideNoCalling();
-        IconState statusIcon = new IconState(isNoCalling,
-                R.drawable.ic_qs_no_calling_sms,
-                getTextIfExists(AccessibilityContentDescriptions.NO_CALLING).toString());
-        callback.setCallIndicator(statusIcon, mSubscriptionInfo.getSubscriptionId());
-
-        switch (mImsType) {
-            case IMS_TYPE_WWAN:
-                statusIcon = new IconState(
-                        true,
-                        getCallStrengthIcon(mLastWwanLevel, /* isWifi= */false),
-                        getCallStrengthDescription(mLastWwanLevel, /* isWifi= */false));
-                break;
-            case IMS_TYPE_WLAN:
-                statusIcon = new IconState(
-                        true,
-                        getCallStrengthIcon(mLastWlanLevel, /* isWifi= */true),
-                        getCallStrengthDescription(mLastWlanLevel, /* isWifi= */true));
-                break;
-            case IMS_TYPE_WLAN_CROSS_SIM:
-                statusIcon = new IconState(
-                        true,
-                        getCallStrengthIcon(mLastWlanCrossSimLevel, /* isWifi= */false),
-                        getCallStrengthDescription(mLastWlanCrossSimLevel, /* isWifi= */false));
-        }
-        callback.setCallIndicator(statusIcon, mSubscriptionInfo.getSubscriptionId());
-    }
-
-    void notifyWifiLevelChange(int level) {
-        if (!mProviderModelBehavior) {
-            return;
-        }
-        mLastWlanLevel = level;
-        if (mImsType != IMS_TYPE_WLAN) {
-            return;
-        }
-        IconState statusIcon = new IconState(
-                true,
-                getCallStrengthIcon(level, /* isWifi= */true),
-                getCallStrengthDescription(level, /* isWifi= */true));
-        notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
-    }
-
-    void notifyDefaultMobileLevelChange(int level) {
-        if (!mProviderModelBehavior) {
-            return;
-        }
-        mLastWlanCrossSimLevel = level;
-        if (mImsType != IMS_TYPE_WLAN_CROSS_SIM) {
-            return;
-        }
-        IconState statusIcon = new IconState(
-                true,
-                getCallStrengthIcon(level, /* isWifi= */false),
-                getCallStrengthDescription(level, /* isWifi= */false));
-        notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
-    }
-
-    void notifyMobileLevelChangeIfNecessary(SignalStrength signalStrength) {
-        if (!mProviderModelBehavior) {
-            return;
-        }
-        int newLevel = getSignalLevel(signalStrength);
-        if (newLevel != mLastLevel) {
-            mLastLevel = newLevel;
-            mLastWwanLevel = newLevel;
-            if (mImsType == IMS_TYPE_WWAN) {
-                IconState statusIcon = new IconState(
-                        true,
-                        getCallStrengthIcon(newLevel, /* isWifi= */false),
-                        getCallStrengthDescription(newLevel, /* isWifi= */false));
-                notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
-            }
-            if (mCurrentState.dataSim) {
-                mNetworkController.notifyDefaultMobileLevelChange(newLevel);
-            }
-        }
     }
 
     int getSignalLevel(SignalStrength signalStrength) {
@@ -801,19 +549,14 @@
         mMobileStatusHistoryIndex = (mMobileStatusHistoryIndex + 1) % STATUS_HISTORY_SIZE;
     }
 
-    @VisibleForTesting
-    void setImsType(int imsType) {
-        mImsType = imsType;
-    }
-
     @Override
     public void dump(PrintWriter pw) {
         super.dump(pw);
         pw.println("  mSubscription=" + mSubscriptionInfo + ",");
-        pw.println("  mProviderModelBehavior=" + mProviderModelBehavior + ",");
         pw.println("  mInflateSignalStrengths=" + mInflateSignalStrengths + ",");
         pw.println("  isDataDisabled=" + isDataDisabled() + ",");
         pw.println("  mNetworkToIconLookup=" + mNetworkToIconLookup + ",");
+        pw.println("  mMobileStatusTracker.isListening=" + mMobileStatusTracker.isListening());
         pw.println("  MobileStatusHistory");
         int size = 0;
         for (int i = 0; i < STATUS_HISTORY_SIZE; i++) {
@@ -843,6 +586,11 @@
             icon = iconState;
             description = desc;
         }
+
+        @Override
+        public String toString() {
+            return "QsInfo: ratTypeIcon=" + ratTypeIcon + " icon=" + icon;
+        }
     }
 
     /** Box for status bar icon info */
@@ -856,5 +604,11 @@
             ratTypeIcon = typeIcon;
             icon = iconState;
         }
+
+        @Override
+        public String toString() {
+            return "SbInfo: showTriangle=" + showTriangle + " ratTypeIcon=" + ratTypeIcon
+                    + " icon=" + icon;
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalControllerFactory.kt b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalControllerFactory.kt
new file mode 100644
index 0000000..7938179
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalControllerFactory.kt
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.statusbar.connectivity
+
+import android.content.Context
+import android.os.Looper
+import android.telephony.SubscriptionInfo
+import android.telephony.TelephonyManager
+import com.android.settingslib.mobile.MobileMappings
+import com.android.settingslib.mobile.MobileStatusTracker
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.util.CarrierConfigTracker
+import javax.inject.Inject
+
+/**
+ * Factory to make MobileSignalController injectable
+ */
+@SysUISingleton
+internal class MobileSignalControllerFactory @Inject constructor(
+    val context: Context,
+    val callbackHandler: CallbackHandler,
+    val carrierConfigTracker: CarrierConfigTracker,
+) {
+    fun createMobileSignalController(
+        config: MobileMappings.Config,
+        hasMobileData: Boolean,
+        phone: TelephonyManager,
+        networkController: NetworkControllerImpl,
+        subscriptionInfo: SubscriptionInfo,
+        subscriptionDefaults: MobileStatusTracker.SubscriptionDefaults,
+        receiverLooper: Looper,
+    ): MobileSignalController {
+        val mobileTrackerFactory = MobileStatusTrackerFactory(
+            phone,
+            receiverLooper,
+            subscriptionInfo,
+            subscriptionDefaults)
+
+        return MobileSignalController(
+            context,
+            config,
+            hasMobileData,
+            phone,
+            callbackHandler,
+            networkController,
+            subscriptionInfo,
+            subscriptionDefaults,
+            receiverLooper,
+            carrierConfigTracker,
+            mobileTrackerFactory,
+        )
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileStatusTrackerFactory.kt b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileStatusTrackerFactory.kt
new file mode 100644
index 0000000..a4c1a198
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileStatusTrackerFactory.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.statusbar.connectivity
+
+import android.os.Looper
+import android.telephony.SubscriptionInfo
+import android.telephony.TelephonyManager
+import com.android.settingslib.mobile.MobileStatusTracker
+
+/**
+ * Factory for [MobileStatusTracker], which lives in SettingsLib
+ */
+class MobileStatusTrackerFactory (
+    val phone: TelephonyManager,
+    val receiverLooper: Looper,
+    val info: SubscriptionInfo,
+    val defaults: MobileStatusTracker.SubscriptionDefaults,
+) {
+    fun createTracker(
+        callback: MobileStatusTracker.Callback
+    ): MobileStatusTracker {
+        return MobileStatusTracker(
+            phone,
+            receiverLooper,
+            info,
+            defaults,
+            callback)
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java
index 268531d..ea7ec4f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java
@@ -71,8 +71,6 @@
 import com.android.systemui.demomode.DemoMode;
 import com.android.systemui.demomode.DemoModeController;
 import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
 import com.android.systemui.log.LogBuffer;
 import com.android.systemui.log.LogLevel;
 import com.android.systemui.log.dagger.StatusBarNetworkControllerLog;
@@ -133,12 +131,11 @@
     private final BroadcastDispatcher mBroadcastDispatcher;
     private final DemoModeController mDemoModeController;
     private final Object mLock = new Object();
-    private final boolean mProviderModelBehavior;
     private Config mConfig;
     private final CarrierConfigTracker mCarrierConfigTracker;
-    private final FeatureFlags mFeatureFlags;
     private final DumpManager mDumpManager;
     private final LogBuffer mLogBuffer;
+    private final MobileSignalControllerFactory mMobileFactory;
 
     private TelephonyCallback.ActiveDataSubscriptionIdListener mPhoneStateListener;
     private int mActiveMobileDataSubscription = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
@@ -234,9 +231,9 @@
             DemoModeController demoModeController,
             CarrierConfigTracker carrierConfigTracker,
             WifiStatusTrackerFactory trackerFactory,
+            MobileSignalControllerFactory mobileFactory,
             @Main Handler handler,
             InternetDialogFactory internetDialogFactory,
-            FeatureFlags featureFlags,
             DumpManager dumpManager,
             @StatusBarNetworkControllerLog LogBuffer logBuffer) {
         this(context, connectivityManager,
@@ -256,8 +253,8 @@
                 demoModeController,
                 carrierConfigTracker,
                 trackerFactory,
+                mobileFactory,
                 handler,
-                featureFlags,
                 dumpManager,
                 logBuffer);
         mReceiverHandler.post(mRegisterListeners);
@@ -282,8 +279,8 @@
             DemoModeController demoModeController,
             CarrierConfigTracker carrierConfigTracker,
             WifiStatusTrackerFactory trackerFactory,
+            MobileSignalControllerFactory mobileFactory,
             @Main Handler handler,
-            FeatureFlags featureFlags,
             DumpManager dumpManager,
             LogBuffer logBuffer
     ) {
@@ -297,6 +294,7 @@
         mCallbackHandler = callbackHandler;
         mDataSaverController = new DataSaverControllerImpl(context);
         mBroadcastDispatcher = broadcastDispatcher;
+        mMobileFactory = mobileFactory;
 
         mSubscriptionManager = subManager;
         mSubDefaults = defaultsHandler;
@@ -304,7 +302,6 @@
         mHasMobileDataFeature = telephonyManager.isDataCapable();
         mDemoModeController = demoModeController;
         mCarrierConfigTracker = carrierConfigTracker;
-        mFeatureFlags = featureFlags;
         mDumpManager = dumpManager;
         mLogBuffer = logBuffer;
 
@@ -456,7 +453,6 @@
         };
 
         mDemoModeController.addCallback(this);
-        mProviderModelBehavior = mFeatureFlags.isEnabled(Flags.COMBINED_STATUS_BAR_SIGNAL_ICONS);
 
         mDumpManager.registerDumpable(TAG, this);
     }
@@ -497,16 +493,16 @@
 
         // broadcasts
         IntentFilter filter = new IntentFilter();
-        filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
-        filter.addAction(Intent.ACTION_SIM_STATE_CHANGED);
-        filter.addAction(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED);
-        filter.addAction(TelephonyManager.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED);
-        filter.addAction(Intent.ACTION_SERVICE_STATE);
-        filter.addAction(TelephonyManager.ACTION_SERVICE_PROVIDERS_UPDATED);
+        filter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
         filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
         filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
-        filter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
+        filter.addAction(Intent.ACTION_SERVICE_STATE);
+        filter.addAction(Intent.ACTION_SIM_STATE_CHANGED);
         filter.addAction(Settings.Panel.ACTION_INTERNET_CONNECTIVITY);
+        filter.addAction(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED);
+        filter.addAction(TelephonyManager.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED);
+        filter.addAction(TelephonyManager.ACTION_SERVICE_PROVIDERS_UPDATED);
+        filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
         mBroadcastDispatcher.registerReceiverWithHandler(this, filter, mReceiverHandler);
         mListening = true;
 
@@ -659,20 +655,6 @@
         return controller != null ? controller.getNetworkNameForCarrierWiFi() : "";
     }
 
-    void notifyWifiLevelChange(int level) {
-        for (int i = 0; i < mMobileSignalControllers.size(); i++) {
-            MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i);
-            mobileSignalController.notifyWifiLevelChange(level);
-        }
-    }
-
-    void notifyDefaultMobileLevelChange(int level) {
-        for (int i = 0; i < mMobileSignalControllers.size(); i++) {
-            MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i);
-            mobileSignalController.notifyDefaultMobileLevelChange(level);
-        }
-    }
-
     private void notifyControllersMobileDataChanged() {
         for (int i = 0; i < mMobileSignalControllers.size(); i++) {
             MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i);
@@ -745,9 +727,6 @@
         for (int i = 0; i < mMobileSignalControllers.size(); i++) {
             MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i);
             mobileSignalController.notifyListeners(cb);
-            if (mProviderModelBehavior) {
-                mobileSignalController.refreshCallIndicator(cb);
-            }
         }
         mCallbackHandler.setListening(cb, true);
     }
@@ -862,9 +841,6 @@
         for (int i = 0; i < mMobileSignalControllers.size(); i++) {
             MobileSignalController controller = mMobileSignalControllers.valueAt(i);
             controller.setConfiguration(mConfig);
-            if (mProviderModelBehavior) {
-                controller.refreshCallIndicator(mCallbackHandler);
-            }
         }
         refreshLocale();
     }
@@ -981,11 +957,15 @@
                 mMobileSignalControllers.put(subId, cachedControllers.get(subId));
                 cachedControllers.remove(subId);
             } else {
-                MobileSignalController controller = new MobileSignalController(mContext, mConfig,
-                        mHasMobileDataFeature, mPhone.createForSubscriptionId(subId),
-                        mCallbackHandler, this, subscriptions.get(i),
-                        mSubDefaults, mReceiverHandler.getLooper(), mCarrierConfigTracker,
-                        mFeatureFlags);
+                MobileSignalController controller = mMobileFactory.createMobileSignalController(
+                        mConfig,
+                        mHasMobileDataFeature,
+                        mPhone.createForSubscriptionId(subId),
+                        this,
+                        subscriptions.get(i),
+                        mSubDefaults,
+                        mReceiverHandler.getLooper()
+                );
                 controller.setUserSetupComplete(mUserSetup);
                 mMobileSignalControllers.put(subId, controller);
                 if (subscriptions.get(i).getSimSlotIndex() == 0) {
@@ -1139,24 +1119,11 @@
                 || mValidatedTransports.get(NetworkCapabilities.TRANSPORT_ETHERNET);
 
         pushConnectivityToSignals();
-        if (mProviderModelBehavior) {
-            mNoDefaultNetwork = !mConnectedTransports.get(NetworkCapabilities.TRANSPORT_CELLULAR)
-                    && !mConnectedTransports.get(NetworkCapabilities.TRANSPORT_WIFI)
-                    && !mConnectedTransports.get(NetworkCapabilities.TRANSPORT_ETHERNET);
-            mCallbackHandler.setConnectivityStatus(mNoDefaultNetwork, !mInetCondition,
-                    mNoNetworksAvailable);
-            for (int i = 0; i < mMobileSignalControllers.size(); i++) {
-                MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i);
-                mobileSignalController.updateNoCallingState();
-            }
-            notifyAllListeners();
-        } else {
-            mNoDefaultNetwork = !mConnectedTransports.get(NetworkCapabilities.TRANSPORT_CELLULAR)
-                    && !mConnectedTransports.get(NetworkCapabilities.TRANSPORT_WIFI)
-                    && !mConnectedTransports.get(NetworkCapabilities.TRANSPORT_ETHERNET);
-            mCallbackHandler.setConnectivityStatus(mNoDefaultNetwork, !mInetCondition,
-                    mNoNetworksAvailable);
-        }
+        mNoDefaultNetwork = !mConnectedTransports.get(NetworkCapabilities.TRANSPORT_CELLULAR)
+                && !mConnectedTransports.get(NetworkCapabilities.TRANSPORT_WIFI)
+                && !mConnectedTransports.get(NetworkCapabilities.TRANSPORT_ETHERNET);
+        mCallbackHandler.setConnectivityStatus(mNoDefaultNetwork, !mInetCondition,
+                mNoNetworksAvailable);
     }
 
     /**
@@ -1346,7 +1313,7 @@
                 mMobileSignalControllers.clear();
                 int start = mSubscriptionManager.getActiveSubscriptionInfoCountMax();
                 for (int i = start /* get out of normal index range */; i < start + num; i++) {
-                    subs.add(addSignalController(i, i));
+                    subs.add(addDemoModeSignalController(i, i));
                 }
                 mCallbackHandler.setSubs(subs);
                 for (int i = 0; i < mMobileSignalControllers.size(); i++) {
@@ -1372,7 +1339,7 @@
             List<SubscriptionInfo> subs = new ArrayList<>();
             while (mMobileSignalControllers.size() <= slot) {
                 int nextSlot = mMobileSignalControllers.size();
-                subs.add(addSignalController(nextSlot, nextSlot));
+                subs.add(addDemoModeSignalController(nextSlot, nextSlot));
             }
             if (!subs.isEmpty()) {
                 mCallbackHandler.setSubs(subs);
@@ -1462,14 +1429,20 @@
         mHistoryIndex = (mHistoryIndex + 1) % HISTORY_SIZE;
     }
 
-    private SubscriptionInfo addSignalController(int id, int simSlotIndex) {
+    private SubscriptionInfo addDemoModeSignalController(int id, int simSlotIndex) {
         SubscriptionInfo info = new SubscriptionInfo(id, "", simSlotIndex, "", "", 0, 0, "", 0,
                 null, null, null, "", false, null, null);
-        MobileSignalController controller = new MobileSignalController(mContext,
-                mConfig, mHasMobileDataFeature,
-                mPhone.createForSubscriptionId(info.getSubscriptionId()), mCallbackHandler, this,
-                info, mSubDefaults, mReceiverHandler.getLooper(), mCarrierConfigTracker,
-                mFeatureFlags);
+
+        MobileSignalController controller = mMobileFactory.createMobileSignalController(
+                mConfig,
+                mHasMobileDataFeature,
+                mPhone.createForSubscriptionId(info.getSubscriptionId()),
+                this,
+                info,
+                mSubDefaults,
+                mReceiverHandler.getLooper()
+        );
+
         mMobileSignalControllers.put(id, controller);
         controller.getState().userSetup = true;
         return info;
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 87cdb17..12f2c22 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiSignalController.java
@@ -222,7 +222,6 @@
         mCurrentState.connected = mWifiTracker.connected;
         mCurrentState.ssid = mWifiTracker.ssid;
         mCurrentState.rssi = mWifiTracker.rssi;
-        boolean levelChanged = mCurrentState.level != mWifiTracker.level;
         mCurrentState.level = mWifiTracker.level;
         mCurrentState.statusLabel = mWifiTracker.statusLabel;
         mCurrentState.isCarrierMerged = mWifiTracker.isCarrierMerged;
@@ -230,10 +229,6 @@
         mCurrentState.iconGroup =
                 mCurrentState.isCarrierMerged ? mCarrierMergedWifiIconGroup
                         : mUnmergedWifiIconGroup;
-
-        if (levelChanged) {
-            mNetworkController.notifyWifiLevelChange(mCurrentState.level);
-        }
     }
 
     boolean isCarrierMergedWifi(int subId) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
index 48e3450..0951e82 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
@@ -19,7 +19,9 @@
 import android.app.IActivityManager;
 import android.content.Context;
 import android.os.Handler;
+import android.os.RemoteException;
 import android.service.dreams.IDreamManager;
+import android.util.Log;
 
 import com.android.internal.jank.InteractionJankMonitor;
 import com.android.internal.statusbar.IStatusBarService;
@@ -60,10 +62,12 @@
 import com.android.systemui.statusbar.phone.StatusBarIconController;
 import com.android.systemui.statusbar.phone.StatusBarIconControllerImpl;
 import com.android.systemui.statusbar.phone.StatusBarIconList;
+import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
 import com.android.systemui.statusbar.phone.StatusBarRemoteInputCallback;
 import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
 import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallFlags;
 import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallLogger;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.statusbar.policy.RemoteInputUriController;
 import com.android.systemui.statusbar.window.StatusBarWindowController;
 import com.android.systemui.tracing.ProtoTracer;
@@ -274,7 +278,30 @@
     @Provides
     @SysUISingleton
     static DialogLaunchAnimator provideDialogLaunchAnimator(IDreamManager dreamManager,
+            KeyguardStateController keyguardStateController,
+            Lazy<StatusBarKeyguardViewManager> statusBarKeyguardViewManager,
             InteractionJankMonitor interactionJankMonitor) {
-        return new DialogLaunchAnimator(dreamManager, interactionJankMonitor);
+        DialogLaunchAnimator.Callback callback = new DialogLaunchAnimator.Callback() {
+            @Override
+            public boolean isDreaming() {
+                try {
+                    return dreamManager.isDreaming();
+                } catch (RemoteException e) {
+                    Log.e("DialogLaunchAnimator.Callback", "dreamManager.isDreaming failed", e);
+                    return false;
+                }
+            }
+
+            @Override
+            public boolean isUnlocked() {
+                return keyguardStateController.isUnlocked();
+            }
+
+            @Override
+            public boolean isShowingAlternateAuthOnUnlock() {
+                return statusBarKeyguardViewManager.get().shouldShowAltAuth();
+            }
+        };
+        return new DialogLaunchAnimator(callback, interactionJankMonitor);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
index 126a986..dbf4810 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
@@ -18,8 +18,10 @@
 
 import android.animation.ObjectAnimator
 import android.util.FloatProperty
+import com.android.systemui.Dumpable
 import com.android.systemui.animation.Interpolators
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dump.DumpManager
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.statusbar.StatusBarState
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
@@ -32,17 +34,20 @@
 import com.android.systemui.statusbar.phone.panelstate.PanelExpansionListener
 import com.android.systemui.statusbar.policy.HeadsUpManager
 import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener
+import java.io.PrintWriter
 import javax.inject.Inject
 import kotlin.math.min
 
 @SysUISingleton
 class NotificationWakeUpCoordinator @Inject constructor(
+    dumpManager: DumpManager,
     private val mHeadsUpManager: HeadsUpManager,
     private val statusBarStateController: StatusBarStateController,
     private val bypassController: KeyguardBypassController,
     private val dozeParameters: DozeParameters,
     private val screenOffAnimationController: ScreenOffAnimationController
-) : OnHeadsUpChangedListener, StatusBarStateController.StateListener, PanelExpansionListener {
+) : OnHeadsUpChangedListener, StatusBarStateController.StateListener, PanelExpansionListener,
+    Dumpable {
 
     private val mNotificationVisibility = object : FloatProperty<NotificationWakeUpCoordinator>(
         "notificationVisibility") {
@@ -60,6 +65,7 @@
 
     private var mLinearDozeAmount: Float = 0.0f
     private var mDozeAmount: Float = 0.0f
+    private var mDozeAmountSource: String = "init"
     private var mNotificationVisibleAmount = 0.0f
     private var mNotificationsVisible = false
     private var mNotificationsVisibleForExpansion = false
@@ -142,6 +148,7 @@
         }
 
     init {
+        dumpManager.registerDumpable(this)
         mHeadsUpManager.addListener(this)
         statusBarStateController.addCallback(this)
         addListener(object : WakeUpListener {
@@ -248,13 +255,14 @@
             // Let's notify the scroller that an animation started
             notifyAnimationStart(mLinearDozeAmount == 1.0f)
         }
-        setDozeAmount(linear, eased)
+        setDozeAmount(linear, eased, source = "StatusBar")
     }
 
-    fun setDozeAmount(linear: Float, eased: Float) {
+    fun setDozeAmount(linear: Float, eased: Float, source: String) {
         val changed = linear != mLinearDozeAmount
         mLinearDozeAmount = linear
         mDozeAmount = eased
+        mDozeAmountSource = source
         mStackScrollerController.setDozeAmount(mDozeAmount)
         updateHideAmount()
         if (changed && linear == 0.0f) {
@@ -271,7 +279,7 @@
             // undefined state, so it's an indication that we should do state cleanup. We override
             // the doze amount to 0f (not dozing) so that the notifications are no longer hidden.
             // See: UnlockedScreenOffAnimationController.onFinishedWakingUp()
-            setDozeAmount(0f, 0f)
+            setDozeAmount(0f, 0f, source = "Override: Shade->Shade (lock cancelled by unlock)")
         }
 
         if (overrideDozeAmountIfAnimatingScreenOff(mLinearDozeAmount)) {
@@ -311,12 +319,11 @@
      */
     private fun overrideDozeAmountIfBypass(): Boolean {
         if (bypassController.bypassEnabled) {
-            var amount = 1.0f
-            if (statusBarStateController.state == StatusBarState.SHADE ||
-                statusBarStateController.state == StatusBarState.SHADE_LOCKED) {
-                amount = 0.0f
+            if (statusBarStateController.state == StatusBarState.KEYGUARD) {
+                setDozeAmount(1f, 1f, source = "Override: bypass (keyguard)")
+            } else {
+                setDozeAmount(0f, 0f, source = "Override: bypass (shade)")
             }
-            setDozeAmount(amount, amount)
             return true
         }
         return false
@@ -332,7 +339,7 @@
      */
     private fun overrideDozeAmountIfAnimatingScreenOff(linearDozeAmount: Float): Boolean {
         if (screenOffAnimationController.overrideNotificationsFullyDozingOnKeyguard()) {
-            setDozeAmount(1f, 1f)
+            setDozeAmount(1f, 1f, source = "Override: animating screen off")
             return true
         }
 
@@ -426,4 +433,24 @@
          */
         @JvmDefault fun onPulseExpansionChanged(expandingChanged: Boolean) {}
     }
-}
\ No newline at end of file
+
+    override fun dump(pw: PrintWriter, args: Array<out String>) {
+        pw.println("mLinearDozeAmount: $mLinearDozeAmount")
+        pw.println("mDozeAmount: $mDozeAmount")
+        pw.println("mDozeAmountSource: $mDozeAmountSource")
+        pw.println("mNotificationVisibleAmount: $mNotificationVisibleAmount")
+        pw.println("mNotificationsVisible: $mNotificationsVisible")
+        pw.println("mNotificationsVisibleForExpansion: $mNotificationsVisibleForExpansion")
+        pw.println("mVisibilityAmount: $mVisibilityAmount")
+        pw.println("mLinearVisibilityAmount: $mLinearVisibilityAmount")
+        pw.println("pulseExpanding: $pulseExpanding")
+        pw.println("state: ${StatusBarState.toString(state)}")
+        pw.println("fullyAwake: $fullyAwake")
+        pw.println("wakingUp: $wakingUp")
+        pw.println("willWakeUp: $willWakeUp")
+        pw.println("collapsedEnoughToHide: $collapsedEnoughToHide")
+        pw.println("pulsing: $pulsing")
+        pw.println("notificationsFullyHidden: $notificationsFullyHidden")
+        pw.println("canShowPulsingHuns: $canShowPulsingHuns")
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index a2ea0ba..a5bf88e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -31,7 +31,7 @@
 import static androidx.lifecycle.Lifecycle.State.RESUMED;
 
 import static com.android.systemui.Dependency.TIME_TICK_HANDLER_NAME;
-import static com.android.systemui.charging.WirelessChargingLayout.UNKNOWN_BATTERY_LEVEL;
+import static com.android.systemui.charging.WirelessChargingAnimation.UNKNOWN_BATTERY_LEVEL;
 import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_ASLEEP;
 import static com.android.systemui.statusbar.NotificationLockscreenUserManager.PERMISSION_SELF;
 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT;
@@ -171,6 +171,7 @@
 import com.android.systemui.qs.QSFragment;
 import com.android.systemui.qs.QSPanelController;
 import com.android.systemui.recents.ScreenPinningRequest;
+import com.android.systemui.ripple.RippleShader.RippleShape;
 import com.android.systemui.scrim.ScrimView;
 import com.android.systemui.settings.brightness.BrightnessSliderController;
 import com.android.systemui.shade.NotificationPanelViewController;
@@ -1089,7 +1090,6 @@
                     isFolded, willGoToSleep, isShadeOpen, leaveOpen));
         }
         if (leaveOpen) {
-            mStatusBarStateController.setLeaveOpenOnKeyguardHide(true);
             if (mKeyguardStateController.isShowing()) {
                 // When device state changes on keyguard we don't want to keep the state of
                 // the shade and instead we open clean state of keyguard with shade closed.
@@ -1098,6 +1098,9 @@
                 // expanded. To prevent that we can close QS which resets QS and some parts of
                 // the shade to its default state. Read more in b/201537421
                 mCloseQsBeforeScreenOff = true;
+            } else {
+                // below makes shade stay open when going from folded to unfolded
+                mStatusBarStateController.setLeaveOpenOnKeyguardHide(true);
             }
         }
     }
@@ -2192,7 +2195,8 @@
                     public void onAnimationEnded() {
                         mNotificationShadeWindowController.setRequestTopUi(false, TAG);
                     }
-                }, false, sUiEventLogger).show(animationDelay);
+                }, /* isDozing= */ false, RippleShape.CIRCLE,
+                sUiEventLogger).show(animationDelay);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java
index 6dbbf0d..fc8e7d5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java
@@ -30,7 +30,6 @@
 import com.android.systemui.R;
 import com.android.systemui.demomode.DemoMode;
 import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
 import com.android.systemui.plugins.DarkIconDispatcher;
 import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
 import com.android.systemui.statusbar.StatusBarIconView;
@@ -255,9 +254,7 @@
 
     public void addMobileView(MobileIconState state) {
         Log.d(TAG, "addMobileView: ");
-        StatusBarMobileView view = StatusBarMobileView.fromContext(
-                mContext, state.slot,
-                mFeatureFlags.isEnabled(Flags.COMBINED_STATUS_BAR_SIGNAL_ICONS));
+        StatusBarMobileView view = StatusBarMobileView.fromContext(mContext, state.slot);
 
         view.applyMobileState(state);
         view.setStaticDrawableColor(mColor);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
index 31d9266..30b640b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
@@ -38,7 +38,6 @@
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.demomode.DemoModeCommandReceiver;
 import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
 import com.android.systemui.plugins.DarkIconDispatcher;
 import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
 import com.android.systemui.statusbar.StatusBarIconView;
@@ -361,9 +360,7 @@
         }
 
         private StatusBarMobileView onCreateStatusBarMobileView(String slot) {
-            StatusBarMobileView view = StatusBarMobileView.fromContext(
-                            mContext, slot,
-                    mFeatureFlags.isEnabled(Flags.COMBINED_STATUS_BAR_SIGNAL_ICONS));
+            StatusBarMobileView view = StatusBarMobileView.fromContext(mContext, slot);
             return view;
         }
 
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 4d9c811..e9771d2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -19,6 +19,7 @@
 import static android.view.WindowInsets.Type.navigationBars;
 
 import static com.android.systemui.plugins.ActivityStarter.OnDismissAction;
+import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_DISMISS_BOUNCER;
 import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_UNLOCK_COLLAPSING;
 import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK;
 import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING;
@@ -387,6 +388,7 @@
             mBouncer.setExpansion(KeyguardBouncer.EXPANSION_VISIBLE);
         } else if (mShowing && !hideBouncerOverDream) {
             if (!isWakeAndUnlocking()
+                    && !(mBiometricUnlockController.getMode() == MODE_DISMISS_BOUNCER)
                     && !mCentralSurfaces.isInLaunchTransition()
                     && !isUnlockCollapsing()) {
                 mBouncer.setExpansion(fraction);
@@ -398,9 +400,8 @@
             }
         } else if (!mShowing && mBouncer.inTransit()) {
             // Keyguard is not visible anymore, but expansion animation was still running.
-            // We need to keep propagating the expansion state to the bouncer, otherwise it will be
-            // stuck in transit.
-            mBouncer.setExpansion(fraction);
+            // We need to hide the bouncer, otherwise it will be stuck in transit.
+            mBouncer.setExpansion(KeyguardBouncer.EXPANSION_HIDDEN);
         } else if (mPulsing && fraction == KeyguardBouncer.EXPANSION_VISIBLE) {
             // Panel expanded while pulsing but didn't translate the bouncer (because we are
             // unlocked.) Let's simply wake-up to dismiss the lock screen.
@@ -470,7 +471,8 @@
         showBouncer(scrimmed);
     }
 
-    private boolean shouldShowAltAuth() {
+    /** Whether we should show the alternate authentication instead of the traditional bouncer. */
+    public boolean shouldShowAltAuth() {
         return mAlternateAuthInterceptor != null
                 && mKeyguardUpdateManager.isUnlockingWithBiometricAllowed(true);
     }
@@ -739,8 +741,10 @@
         }
         mNotificationShadeWindowController.setKeyguardOccluded(mOccluded);
 
-        // setDozing(false) will call reset once we stop dozing.
-        if (!mDozing) {
+        // setDozing(false) will call reset once we stop dozing. Also, if we're going away, there's
+        // no need to reset the keyguard views as we'll be gone shortly. Resetting now could cause
+        // unexpected visible behavior if the keyguard is still visible as we're animating unlocked.
+        if (!mDozing && !mKeyguardStateController.isKeyguardGoingAway()) {
             // If Keyguard is reshown, don't hide the bouncer as it might just have been requested
             // by a FLAG_DISMISS_KEYGUARD_ACTIVITY.
             reset(isOccluding /* hideBouncerWhenShowing*/);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
index ee242a4..492734e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
@@ -26,8 +26,6 @@
 import com.android.settingslib.mobile.TelephonyIcons;
 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.connectivity.IconState;
 import com.android.systemui.statusbar.connectivity.MobileDataIndicators;
 import com.android.systemui.statusbar.connectivity.NetworkController;
@@ -66,7 +64,6 @@
     private final Handler mHandler = Handler.getMain();
     private final CarrierConfigTracker mCarrierConfigTracker;
     private final TunerService mTunerService;
-    private final FeatureFlags mFeatureFlags;
 
     private boolean mHideAirplane;
     private boolean mHideMobile;
@@ -90,8 +87,7 @@
             CarrierConfigTracker carrierConfigTracker,
             NetworkController networkController,
             SecurityController securityController,
-            TunerService tunerService,
-            FeatureFlags featureFlags
+            TunerService tunerService
     ) {
         mContext = context;
 
@@ -100,7 +96,6 @@
         mNetworkController = networkController;
         mSecurityController = securityController;
         mTunerService = tunerService;
-        mFeatureFlags = featureFlags;
 
         mSlotAirplane = mContext.getString(com.android.internal.R.string.status_bar_airplane);
         mSlotMobile   = mContext.getString(com.android.internal.R.string.status_bar_mobile);
@@ -378,40 +373,6 @@
     }
 
     @Override
-    public void setConnectivityStatus(boolean noDefaultNetwork, boolean noValidatedNetwork,
-            boolean noNetworksAvailable) {
-        if (!mFeatureFlags.isEnabled(Flags.COMBINED_STATUS_BAR_SIGNAL_ICONS)) {
-            return;
-        }
-        if (DEBUG) {
-            Log.d(TAG, "setConnectivityStatus: "
-                    + "noDefaultNetwork = " + noDefaultNetwork + ","
-                    + "noValidatedNetwork = " + noValidatedNetwork + ","
-                    + "noNetworksAvailable = " + noNetworksAvailable);
-        }
-        WifiIconState newState = mWifiIconState.copy();
-        newState.noDefaultNetwork = noDefaultNetwork;
-        newState.noValidatedNetwork = noValidatedNetwork;
-        newState.noNetworksAvailable = noNetworksAvailable;
-        newState.slot = mSlotWifi;
-        newState.airplaneSpacerVisible = mIsAirplaneMode;
-        if (noDefaultNetwork && noNetworksAvailable && !mIsAirplaneMode) {
-            newState.visible = true;
-            newState.resId = R.drawable.ic_qs_no_internet_unavailable;
-        } else if (noDefaultNetwork && !noNetworksAvailable
-                && (!mIsAirplaneMode || (mIsAirplaneMode && mIsWifiEnabled))) {
-            newState.visible = true;
-            newState.resId = R.drawable.ic_qs_no_internet_available;
-        } else {
-            newState.visible = false;
-            newState.resId = 0;
-        }
-        updateWifiIconWithState(newState);
-        mWifiIconState = newState;
-    }
-
-
-    @Override
     public void setEthernetIndicators(IconState state) {
         boolean visible = state.visible && !mHideEthernet;
         int resId = state.icon;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoCollector.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoCollector.kt
new file mode 100644
index 0000000..6c02b0d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoCollector.kt
@@ -0,0 +1,49 @@
+/*
+ * 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.pipeline
+
+import com.android.systemui.statusbar.pipeline.repository.NetworkCapabilityInfo
+import kotlinx.coroutines.flow.StateFlow
+
+/**
+ * Interface exposing a flow for raw connectivity information. Clients should collect on
+ * [rawConnectivityInfoFlow] to get updates on connectivity information.
+ *
+ * Note: [rawConnectivityInfoFlow] should be a *hot* flow, so that we only create one instance of it
+ * and all clients get references to the same flow.
+ *
+ * This will be used for the new status bar pipeline to compile information we need to display some
+ * of the icons in the RHS of the status bar.
+ */
+interface ConnectivityInfoCollector {
+    val rawConnectivityInfoFlow: StateFlow<RawConnectivityInfo>
+}
+
+/**
+ * An object containing all of the raw connectivity information.
+ *
+ * Importantly, all the information in this object should not be processed at all (i.e., the data
+ * that we receive from callbacks should be piped straight into this object and not be filtered,
+ * manipulated, or processed in any way). Instead, any listeners on
+ * [ConnectivityInfoCollector.rawConnectivityInfoFlow] can do the processing.
+ *
+ * This allows us to keep all the processing in one place which is beneficial for logging and
+ * debugging purposes.
+ */
+data class RawConnectivityInfo(
+        val networkCapabilityInfo: Map<Int, NetworkCapabilityInfo> = emptyMap(),
+)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoCollectorImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoCollectorImpl.kt
new file mode 100644
index 0000000..8d69422
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoCollectorImpl.kt
@@ -0,0 +1,46 @@
+/*
+ * 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.pipeline
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.statusbar.pipeline.repository.NetworkCapabilitiesRepo
+import kotlinx.coroutines.CoroutineScope
+import javax.inject.Inject
+import kotlinx.coroutines.flow.SharingStarted.Companion.Lazily
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
+
+/**
+ * The real implementation of [ConnectivityInfoCollector] that will collect information from all the
+ * relevant connectivity callbacks and compile it into [rawConnectivityInfoFlow].
+ */
+@SysUISingleton
+class ConnectivityInfoCollectorImpl @Inject constructor(
+        networkCapabilitiesRepo: NetworkCapabilitiesRepo,
+        @Application scope: CoroutineScope,
+) : ConnectivityInfoCollector {
+    override val rawConnectivityInfoFlow: StateFlow<RawConnectivityInfo> =
+            // TODO(b/238425913): Collect all the separate flows for individual raw information into
+            //   this final flow.
+            networkCapabilitiesRepo.dataStream
+                    .map {
+                        RawConnectivityInfo(networkCapabilityInfo = it)
+                    }
+                    .stateIn(scope, started = Lazily, initialValue = RawConnectivityInfo())
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoProcessor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoProcessor.kt
index a841914..1aae250 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoProcessor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoProcessor.kt
@@ -19,7 +19,15 @@
 import android.content.Context
 import com.android.systemui.CoreStartable
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.statusbar.pipeline.repository.NetworkCapabilityInfo
 import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted.Companion.Lazily
+import kotlinx.coroutines.flow.emptyFlow
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
 
 /**
  * A processor that transforms raw connectivity information that we get from callbacks and turns it
@@ -27,20 +35,43 @@
  *
  * This will be used for the new status bar pipeline to calculate the list of icons that should be
  * displayed in the RHS of the status bar.
+ *
+ * Anyone can listen to [processedInfoFlow] to get updates to the processed data.
  */
 @SysUISingleton
 class ConnectivityInfoProcessor @Inject constructor(
+        connectivityInfoCollector: ConnectivityInfoCollector,
         context: Context,
-        private val statusBarPipelineFlags: StatusBarPipelineFlags,
+        @Application private val scope: CoroutineScope,
+        statusBarPipelineFlags: StatusBarPipelineFlags,
 ) : CoreStartable(context) {
+    // Note: This flow will not start running until a client calls `collect` on it, which means that
+    // [connectivityInfoCollector]'s flow will also not start anything until that `collect` call
+    // happens.
+    val processedInfoFlow: Flow<ProcessedConnectivityInfo> =
+            if (!statusBarPipelineFlags.isNewPipelineEnabled())
+                emptyFlow()
+            else connectivityInfoCollector.rawConnectivityInfoFlow
+                    .map { it.process() }
+                    .stateIn(
+                            scope,
+                            started = Lazily,
+                            initialValue = ProcessedConnectivityInfo()
+                    )
+
     override fun start() {
-        if (statusBarPipelineFlags.isNewPipelineEnabled()) {
-            init()
-        }
     }
 
-    /** Initializes this processor and everything it depends on. */
-    private fun init() {
-        // TODO(b/238425913): Register all the connectivity callbacks here.
+    private fun RawConnectivityInfo.process(): ProcessedConnectivityInfo {
+        // TODO(b/238425913): Actually process the raw info into meaningful data.
+        return ProcessedConnectivityInfo(this.networkCapabilityInfo)
     }
 }
+
+/**
+ * An object containing connectivity info that has been processed into data that can be directly
+ * used by the status bar (and potentially other SysUI areas) to display icons.
+ */
+data class ProcessedConnectivityInfo(
+        val networkCapabilityInfo: Map<Int, NetworkCapabilityInfo> = emptyMap(),
+)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
index 771bb0c..c4e2b73 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
@@ -17,6 +17,8 @@
 package com.android.systemui.statusbar.pipeline.dagger
 
 import com.android.systemui.CoreStartable
+import com.android.systemui.statusbar.pipeline.ConnectivityInfoCollector
+import com.android.systemui.statusbar.pipeline.ConnectivityInfoCollectorImpl
 import com.android.systemui.statusbar.pipeline.ConnectivityInfoProcessor
 import dagger.Binds
 import dagger.Module
@@ -30,4 +32,9 @@
     @IntoMap
     @ClassKey(ConnectivityInfoProcessor::class)
     abstract fun bindConnectivityInfoProcessor(cip: ConnectivityInfoProcessor): CoreStartable
+
+    @Binds
+    abstract fun provideConnectivityInfoCollector(
+            impl: ConnectivityInfoCollectorImpl
+    ): ConnectivityInfoCollector
 }
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
index e22a896..4c76270 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
@@ -405,8 +405,8 @@
             }
 
             @Override
-            public void onEntryUpdated(NotificationEntry entry) {
-                BubblesManager.this.onEntryUpdated(entry);
+            public void onEntryUpdated(NotificationEntry entry, boolean fromSystem) {
+                BubblesManager.this.onEntryUpdated(entry, fromSystem);
             }
 
             @Override
@@ -444,9 +444,10 @@
         }
     }
 
-    void onEntryUpdated(NotificationEntry entry) {
+    void onEntryUpdated(NotificationEntry entry, boolean fromSystem) {
+        boolean shouldBubble = mNotificationInterruptStateProvider.shouldBubbleUp(entry);
         mBubbles.onEntryUpdated(notifToBubbleEntry(entry),
-                mNotificationInterruptStateProvider.shouldBubbleUp(entry));
+                shouldBubble, fromSystem);
     }
 
     void onEntryRemoved(NotificationEntry entry) {
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
index 12597e0..eba2795 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
@@ -118,6 +118,33 @@
     private final UserInfoController mUserInfoController;
     private final Executor mSysUiMainExecutor;
 
+    // Listeners and callbacks. Note that we prefer member variable over anonymous class here to
+    // avoid the situation that some implementations, like KeyguardUpdateMonitor, use WeakReference
+    // internally and anonymous class could be released after registration.
+    private final ConfigurationController.ConfigurationListener mConfigurationListener =
+            new ConfigurationController.ConfigurationListener() {
+                @Override
+                public void onConfigChanged(Configuration newConfig) {
+                    mShell.onConfigurationChanged(newConfig);
+                }
+            };
+    private final KeyguardStateController.Callback mKeyguardStateCallback =
+            new KeyguardStateController.Callback() {
+                @Override
+                public void onKeyguardShowingChanged() {
+                    mShell.onKeyguardVisibilityChanged(mKeyguardStateController.isShowing(),
+                            mKeyguardStateController.isOccluded(),
+                            mKeyguardStateController.isAnimatingBetweenKeyguardAndSurfaceBehind());
+                }
+            };
+    private final KeyguardUpdateMonitorCallback mKeyguardUpdateMonitorCallback =
+            new KeyguardUpdateMonitorCallback() {
+                @Override
+                public void onKeyguardDismissAnimationFinished() {
+                    mShell.onKeyguardDismissAnimationFinished();
+                }
+            };
+
     private boolean mIsSysUiStateValid;
     private KeyguardUpdateMonitorCallback mOneHandedKeyguardCallback;
     private WakefulnessLifecycle.Observer mWakefulnessObserver;
@@ -159,28 +186,11 @@
     public void start() {
         // Notify with the initial configuration and subscribe for new config changes
         mShell.onConfigurationChanged(mContext.getResources().getConfiguration());
-        mConfigurationController.addCallback(new ConfigurationController.ConfigurationListener() {
-            @Override
-            public void onConfigChanged(Configuration newConfig) {
-                mShell.onConfigurationChanged(newConfig);
-            }
-        });
+        mConfigurationController.addCallback(mConfigurationListener);
 
         // Subscribe to keyguard changes
-        mKeyguardStateController.addCallback(new KeyguardStateController.Callback() {
-            @Override
-            public void onKeyguardShowingChanged() {
-                mShell.onKeyguardVisibilityChanged(mKeyguardStateController.isShowing(),
-                        mKeyguardStateController.isOccluded(),
-                        mKeyguardStateController.isAnimatingBetweenKeyguardAndSurfaceBehind());
-            }
-        });
-        mKeyguardUpdateMonitor.registerCallback(new KeyguardUpdateMonitorCallback() {
-            @Override
-            public void onKeyguardDismissAnimationFinished() {
-                mShell.onKeyguardDismissAnimationFinished();
-            }
-        });
+        mKeyguardStateController.addCallback(mKeyguardStateCallback);
+        mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateMonitorCallback);
 
         // TODO: Consider piping config change and other common calls to a shell component to
         //  delegate internally
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java
index dffad6c..80385e6 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java
@@ -44,6 +44,7 @@
 
 import androidx.test.filters.SmallTest;
 
+import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
 import com.android.systemui.SysuiTestCase;
 
 import org.junit.After;
@@ -190,7 +191,7 @@
 
     private void verifyViewDismissed(SurfaceView v) throws Exception {
         verify(mKeyguardSecurityContainer).removeView(v);
-        verify(mKeyguardCallback).dismiss(true, TARGET_USER_ID, true);
+        verify(mKeyguardCallback).dismiss(true, TARGET_USER_ID, true, SecurityMode.Invalid);
         assertThat(mContext.isBound(mComponentName)).isFalse();
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardListenQueueTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardListenQueueTest.kt
index ad6d146..bb455da 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardListenQueueTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardListenQueueTest.kt
@@ -86,10 +86,11 @@
     becauseCannotSkipBouncer = false,
     biometricSettingEnabledForUser = false,
     bouncerFullyShown = false,
+    onlyFaceEnrolled = false,
     faceAuthenticated = false,
     faceDisabled = false,
     goingToSleep = false,
-    keyguardAwake = false,
+    keyguardAwakeExcludingBouncerShowing = false,
     keyguardGoingAway = false,
     listeningForFaceAssistant = false,
     occludingAppRequestingFaceAuth = false,
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
index dc87a6a..aecec9d 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
@@ -21,7 +21,10 @@
 import static com.android.keyguard.KeyguardSecurityContainer.MODE_DEFAULT;
 import static com.android.keyguard.KeyguardSecurityContainer.MODE_ONE_HANDED;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.eq;
@@ -76,6 +79,8 @@
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper()
 public class KeyguardSecurityContainerControllerTest extends SysuiTestCase {
+    private static final int TARGET_USER_ID = 100;
+
     @Rule
     public MockitoRule mRule = MockitoJUnit.rule();
 
@@ -381,6 +386,44 @@
         verify(mSidefpsController, never()).show();
     }
 
+    @Test
+    public void showNextSecurityScreenOrFinish_setsSecurityScreenToPinAfterSimPinUnlock() {
+        // GIVEN the current security method is SimPin
+        when(mKeyguardUpdateMonitor.getUserHasTrust(anyInt())).thenReturn(false);
+        when(mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(TARGET_USER_ID)).thenReturn(false);
+        mKeyguardSecurityContainerController.showSecurityScreen(SecurityMode.SimPin);
+
+        // WHEN a request is made from the SimPin screens to show the next security method
+        when(mKeyguardSecurityModel.getSecurityMode(TARGET_USER_ID)).thenReturn(SecurityMode.PIN);
+        mKeyguardSecurityContainerController.showNextSecurityScreenOrFinish(
+                /* authenticated= */true,
+                TARGET_USER_ID,
+                /* bypassSecondaryLockScreen= */true,
+                SecurityMode.SimPin);
+
+        // THEN the next security method of PIN is set, and the keyguard is not marked as done
+        verify(mSecurityCallback, never()).finish(anyBoolean(), anyInt());
+        assertThat(mKeyguardSecurityContainerController.getCurrentSecurityMode())
+                .isEqualTo(SecurityMode.PIN);
+    }
+
+    @Test
+    public void showNextSecurityScreenOrFinish_ignoresCallWhenSecurityMethodHasChanged() {
+        //GIVEN current security mode has been set to PIN
+        mKeyguardSecurityContainerController.showSecurityScreen(SecurityMode.PIN);
+
+        //WHEN a request comes from SimPin to dismiss the security screens
+        boolean keyguardDone = mKeyguardSecurityContainerController.showNextSecurityScreenOrFinish(
+                /* authenticated= */true,
+                TARGET_USER_ID,
+                /* bypassSecondaryLockScreen= */true,
+                SecurityMode.SimPin);
+
+        //THEN no action has happened, which will not dismiss the security screens
+        assertThat(keyguardDone).isEqualTo(false);
+        verify(mKeyguardUpdateMonitor, never()).getUserHasTrust(anyInt());
+    }
+
     private void setupConditionsToEnableSideFpsHint() {
         attachView();
         setSideFpsHintEnabledFromResources(true);
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 85ecfd3..c677371 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -16,11 +16,13 @@
 
 package com.android.keyguard;
 
+import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_LOCKOUT;
 import static android.telephony.SubscriptionManager.DATA_ROAMING_DISABLE;
 import static android.telephony.SubscriptionManager.NAME_SOURCE_CARRIER_ID;
 
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST;
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT;
+import static com.android.keyguard.KeyguardUpdateMonitor.DEFAULT_CANCEL_SIGNAL_TIMEOUT;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -31,7 +33,6 @@
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.atLeastOnce;
 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;
@@ -41,6 +42,7 @@
 
 import android.app.Activity;
 import android.app.ActivityManager;
+import android.app.IActivityManager;
 import android.app.admin.DevicePolicyManager;
 import android.app.trust.IStrongAuthTracker;
 import android.app.trust.TrustManager;
@@ -52,6 +54,7 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
+import android.content.pm.UserInfo;
 import android.hardware.biometrics.BiometricConstants;
 import android.hardware.biometrics.BiometricManager;
 import android.hardware.biometrics.BiometricSourceType;
@@ -68,6 +71,7 @@
 import android.os.CancellationSignal;
 import android.os.Handler;
 import android.os.IRemoteCallback;
+import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.telephony.ServiceState;
@@ -106,6 +110,7 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.mockito.MockitoSession;
+import org.mockito.internal.util.reflection.FieldSetter;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -182,26 +187,39 @@
     private ActiveUnlockConfig mActiveUnlockConfig;
     @Mock
     private KeyguardUpdateMonitorLogger mKeyguardUpdateMonitorLogger;
+    @Mock
+    private IActivityManager mActivityService;
+
+    private final int mCurrentUserId = 100;
+    private final UserInfo mCurrentUserInfo = new UserInfo(mCurrentUserId, "Test user", 0);
+
+    @Captor
+    private ArgumentCaptor<IBiometricEnabledOnKeyguardCallback>
+            mBiometricEnabledCallbackArgCaptor;
+    @Captor
+    private ArgumentCaptor<FaceManager.AuthenticationCallback> mAuthenticationCallbackCaptor;
+    @Captor
+    private ArgumentCaptor<CancellationSignal> mCancellationSignalCaptor;
+
     // Direct executor
-    private Executor mBackgroundExecutor = Runnable::run;
-    private Executor mMainExecutor = Runnable::run;
+    private final Executor mBackgroundExecutor = Runnable::run;
+    private final Executor mMainExecutor = Runnable::run;
     private TestableLooper mTestableLooper;
+    private Handler mHandler;
     private TestableKeyguardUpdateMonitor mKeyguardUpdateMonitor;
     private TestableContext mSpiedContext;
     private MockitoSession mMockitoSession;
     private StatusBarStateController.StateListener mStatusBarStateListener;
+    private IBiometricEnabledOnKeyguardCallback mBiometricEnabledOnKeyguardCallback;
 
     @Before
-    public void setup() {
+    public void setup() throws RemoteException {
         MockitoAnnotations.initMocks(this);
         mSpiedContext = spy(mContext);
         when(mPackageManager.hasSystemFeature(anyString())).thenReturn(true);
         when(mSpiedContext.getPackageManager()).thenReturn(mPackageManager);
-        doAnswer(invocation -> {
-            IBiometricEnabledOnKeyguardCallback callback = invocation.getArgument(0);
-            callback.onChanged(true /* enabled */, KeyguardUpdateMonitor.getCurrentUser());
-            return null;
-        }).when(mBiometricManager).registerEnabledOnKeyguardCallback(any());
+        when(mActivityService.getCurrentUser()).thenReturn(mCurrentUserInfo);
+        when(mActivityService.getCurrentUserId()).thenReturn(mCurrentUserId);
         when(mFaceManager.isHardwareDetected()).thenReturn(true);
         when(mFaceManager.hasEnrolledTemplates()).thenReturn(true);
         when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(true);
@@ -262,13 +280,27 @@
                 .startMocking();
         ExtendedMockito.doReturn(SubscriptionManager.INVALID_SUBSCRIPTION_ID)
                 .when(SubscriptionManager::getDefaultSubscriptionId);
+        KeyguardUpdateMonitor.setCurrentUser(mCurrentUserId);
         ExtendedMockito.doReturn(KeyguardUpdateMonitor.getCurrentUser())
                 .when(ActivityManager::getCurrentUser);
+        ExtendedMockito.doReturn(mActivityService).when(ActivityManager::getService);
 
         mTestableLooper = TestableLooper.get(this);
         allowTestableLooperAsMainThread();
         mKeyguardUpdateMonitor = new TestableKeyguardUpdateMonitor(mSpiedContext);
 
+        verify(mBiometricManager)
+                .registerEnabledOnKeyguardCallback(mBiometricEnabledCallbackArgCaptor.capture());
+        mBiometricEnabledOnKeyguardCallback = mBiometricEnabledCallbackArgCaptor.getValue();
+        biometricsEnabledForCurrentUser();
+
+        mHandler = spy(mKeyguardUpdateMonitor.getHandler());
+        try {
+            FieldSetter.setField(mKeyguardUpdateMonitor,
+                    KeyguardUpdateMonitor.class.getDeclaredField("mHandler"), mHandler);
+        } catch (NoSuchFieldException e) {
+
+        }
         verify(mStatusBarStateController).addCallback(mStatusBarStateListenerCaptor.capture());
         mStatusBarStateListener = mStatusBarStateListenerCaptor.getValue();
         mKeyguardUpdateMonitor.registerCallback(mTestCallback);
@@ -308,7 +340,7 @@
 
         when(mTelephonyManager.getActiveModemCount()).thenReturn(1);
         when(mTelephonyManager.getSimState(anyInt())).thenReturn(state);
-        when(mSubscriptionManager.getSubscriptionIds(anyInt())).thenReturn(new int[] { subId });
+        when(mSubscriptionManager.getSubscriptionIds(anyInt())).thenReturn(new int[]{subId});
 
         KeyguardUpdateMonitor testKUM = new TestableKeyguardUpdateMonitor(mSpiedContext);
 
@@ -483,7 +515,7 @@
         // Even SimState Loaded, still need ACTION_SERVICE_STATE turn on mTelephonyCapable
         assertThat(mKeyguardUpdateMonitor.mTelephonyCapable).isFalse();
 
-        Intent intentServiceState =  new Intent(Intent.ACTION_SERVICE_STATE);
+        Intent intentServiceState = new Intent(Intent.ACTION_SERVICE_STATE);
         intentSimState.putExtra(Intent.EXTRA_SIM_STATE
                 , Intent.SIM_STATE_LOADED);
         mKeyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext()
@@ -498,7 +530,7 @@
         mTestableLooper.processAllMessages();
 
         verify(mFingerprintManager).authenticate(any(), any(), any(), any(), anyInt(), anyInt(),
-                        anyInt());
+                anyInt());
         verify(mFingerprintManager, never()).detectFingerprint(any(), any(), anyInt());
     }
 
@@ -548,11 +580,12 @@
 
     @Test
     public void testTriesToAuthenticate_whenBouncer() {
+        fingerprintIsNotEnrolled();
         setKeyguardBouncerVisibility(true);
 
         verify(mFaceManager).authenticate(any(), any(), any(), any(), anyInt(), anyBoolean());
-        verify(mFaceManager).isHardwareDetected();
-        verify(mFaceManager).hasEnrolledTemplates(anyInt());
+        verify(mFaceManager, atLeastOnce()).isHardwareDetected();
+        verify(mFaceManager, atLeastOnce()).hasEnrolledTemplates(anyInt());
     }
 
     @Test
@@ -718,7 +751,6 @@
         verify(mLockPatternUtils).requireStrongAuth(anyInt(), anyInt());
     }
 
-
     @Test
     public void testFaceAndFingerprintLockout() {
         mKeyguardUpdateMonitor.dispatchStartedWakingUp();
@@ -769,7 +801,8 @@
     public void testBiometricsCleared_whenUserSwitches() throws Exception {
         final IRemoteCallback reply = new IRemoteCallback.Stub() {
             @Override
-            public void sendResult(Bundle data) {} // do nothing
+            public void sendResult(Bundle data) {
+            } // do nothing
         };
         final BiometricAuthenticated dummyAuthentication =
                 new BiometricAuthenticated(true /* authenticated */, true /* strong */);
@@ -787,7 +820,8 @@
     public void testMultiUserJankMonitor_whenUserSwitches() throws Exception {
         final IRemoteCallback reply = new IRemoteCallback.Stub() {
             @Override
-            public void sendResult(Bundle data) {} // do nothing
+            public void sendResult(Bundle data) {
+            } // do nothing
         };
         mKeyguardUpdateMonitor.handleUserSwitchComplete(10 /* user */);
         verify(mInteractionJankMonitor).end(InteractionJankMonitor.CUJ_USER_SWITCH);
@@ -924,6 +958,7 @@
 
     @Test
     public void testSecondaryLockscreenRequirement() {
+        KeyguardUpdateMonitor.setCurrentUser(UserHandle.myUserId());
         int user = KeyguardUpdateMonitor.getCurrentUser();
         String packageName = "fake.test.package";
         String cls = "FakeService";
@@ -1097,8 +1132,7 @@
     @Test
     public void testShouldNotUpdateBiometricListeningStateOnStatusBarStateChange() {
         // GIVEN state for face auth should run aside from StatusBarState
-        when(mDevicePolicyManager.getKeyguardDisabledFeatures(null,
-                KeyguardUpdateMonitor.getCurrentUser())).thenReturn(0);
+        biometricsNotDisabledThroughDevicePolicyManager();
         mStatusBarStateListener.onStateChanged(StatusBarState.SHADE_LOCKED);
         setKeyguardBouncerVisibility(false /* isVisible */);
         mKeyguardUpdateMonitor.dispatchStartedWakingUp();
@@ -1153,6 +1187,471 @@
         verify(mTestCallback).showTrustGrantedMessage("Unlocked by wearable");
     }
 
+    @Test
+    public void testShouldListenForFace_whenFaceManagerNotAvailable_returnsFalse() {
+        mFaceManager = null;
+        mKeyguardUpdateMonitor = new TestableKeyguardUpdateMonitor(mSpiedContext);
+
+        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
+    }
+
+    @Test
+    public void testShouldListenForFace_whenFpIsLockedOut_returnsFalse() throws RemoteException {
+        // Face auth should run when the following is true.
+        keyguardNotGoingAway();
+        bouncerFullyVisibleAndNotGoingToSleep();
+        currentUserIsPrimary();
+        strongAuthNotRequired();
+        biometricsEnabledForCurrentUser();
+        currentUserDoesNotHaveTrust();
+        biometricsNotDisabledThroughDevicePolicyManager();
+        userNotCurrentlySwitching();
+        mTestableLooper.processAllMessages();
+
+        // Fingerprint is locked out.
+        fingerprintErrorLockedOut();
+
+        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
+    }
+
+    @Test
+    public void testShouldListenForFace_whenFaceIsAlreadyAuthenticated_returnsFalse()
+            throws RemoteException {
+        // Face auth should run when the following is true.
+        bouncerFullyVisibleAndNotGoingToSleep();
+        fingerprintIsNotEnrolled();
+        keyguardNotGoingAway();
+        currentUserIsPrimary();
+        strongAuthNotRequired();
+        biometricsEnabledForCurrentUser();
+        currentUserDoesNotHaveTrust();
+        biometricsNotDisabledThroughDevicePolicyManager();
+        userNotCurrentlySwitching();
+
+        mTestableLooper.processAllMessages();
+
+        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
+
+        triggerSuccessfulFaceAuth();
+        mTestableLooper.processAllMessages();
+
+        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
+    }
+
+    @Test
+    public void testShouldListenForFace_whenUserIsNotPrimary_returnsFalse() throws RemoteException {
+        // This disables face auth
+        when(mUserManager.isPrimaryUser()).thenReturn(false);
+        mKeyguardUpdateMonitor =
+                new TestableKeyguardUpdateMonitor(mSpiedContext);
+
+        // Preconditions for face auth to run
+        keyguardNotGoingAway();
+        bouncerFullyVisibleAndNotGoingToSleep();
+        strongAuthNotRequired();
+        biometricsEnabledForCurrentUser();
+        currentUserDoesNotHaveTrust();
+        biometricsNotDisabledThroughDevicePolicyManager();
+        userNotCurrentlySwitching();
+        mTestableLooper.processAllMessages();
+
+
+        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
+    }
+
+    @Test
+    public void testShouldListenForFace_whenStrongAuthDoesNotAllowScanning_returnsFalse()
+            throws RemoteException {
+        // Preconditions for face auth to run
+        keyguardNotGoingAway();
+        bouncerFullyVisibleAndNotGoingToSleep();
+        currentUserIsPrimary();
+        biometricsEnabledForCurrentUser();
+        currentUserDoesNotHaveTrust();
+        biometricsNotDisabledThroughDevicePolicyManager();
+        userNotCurrentlySwitching();
+
+        // This disables face auth
+        when(mStrongAuthTracker.getStrongAuthForUser(KeyguardUpdateMonitor.getCurrentUser()))
+                .thenReturn(STRONG_AUTH_REQUIRED_AFTER_BOOT);
+        mTestableLooper.processAllMessages();
+
+
+        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
+    }
+
+    @Test
+    public void testShouldListenForFace_whenBiometricsDisabledForUser_returnsFalse()
+            throws RemoteException {
+        // Preconditions for face auth to run
+        keyguardNotGoingAway();
+        bouncerFullyVisibleAndNotGoingToSleep();
+        fingerprintIsNotEnrolled();
+        currentUserIsPrimary();
+        currentUserDoesNotHaveTrust();
+        biometricsNotDisabledThroughDevicePolicyManager();
+        biometricsEnabledForCurrentUser();
+        userNotCurrentlySwitching();
+        mTestableLooper.processAllMessages();
+
+        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
+
+        // This disables face auth
+        biometricsDisabledForCurrentUser();
+        mTestableLooper.processAllMessages();
+
+        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
+    }
+
+    @Test
+    public void testShouldListenForFace_whenUserCurrentlySwitching_returnsFalse()
+            throws RemoteException {
+        // Preconditions for face auth to run
+        keyguardNotGoingAway();
+        bouncerFullyVisibleAndNotGoingToSleep();
+        fingerprintIsNotEnrolled();
+        currentUserIsPrimary();
+        currentUserDoesNotHaveTrust();
+        biometricsNotDisabledThroughDevicePolicyManager();
+        biometricsEnabledForCurrentUser();
+        userNotCurrentlySwitching();
+        mTestableLooper.processAllMessages();
+
+        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
+
+        userCurrentlySwitching();
+        mTestableLooper.processAllMessages();
+
+        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
+    }
+
+    @Test
+    public void testShouldListenForFace_whenSecureCameraLaunched_returnsFalse()
+            throws RemoteException {
+        // Preconditions for face auth to run
+        keyguardNotGoingAway();
+        bouncerFullyVisibleAndNotGoingToSleep();
+        fingerprintIsNotEnrolled();
+        currentUserIsPrimary();
+        currentUserDoesNotHaveTrust();
+        biometricsNotDisabledThroughDevicePolicyManager();
+        biometricsEnabledForCurrentUser();
+        userNotCurrentlySwitching();
+        mTestableLooper.processAllMessages();
+
+        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
+
+        secureCameraLaunched();
+        mTestableLooper.processAllMessages();
+
+        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
+    }
+
+    @Test
+    public void testShouldListenForFace_whenOccludingAppRequestsFaceAuth_returnsTrue()
+            throws RemoteException {
+        // Preconditions for face auth to run
+        keyguardNotGoingAway();
+        bouncerFullyVisibleAndNotGoingToSleep();
+        currentUserIsPrimary();
+        currentUserDoesNotHaveTrust();
+        biometricsNotDisabledThroughDevicePolicyManager();
+        biometricsEnabledForCurrentUser();
+        userNotCurrentlySwitching();
+        mTestableLooper.processAllMessages();
+
+        secureCameraLaunched();
+
+        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
+
+        occludingAppRequestsFaceAuth();
+        mTestableLooper.processAllMessages();
+
+        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
+    }
+
+    @Test
+    public void testShouldListenForFace_whenBouncerShowingAndDeviceIsAwake_returnsTrue()
+            throws RemoteException {
+        // Preconditions for face auth to run
+        keyguardNotGoingAway();
+        currentUserIsPrimary();
+        currentUserDoesNotHaveTrust();
+        biometricsNotDisabledThroughDevicePolicyManager();
+        biometricsEnabledForCurrentUser();
+        userNotCurrentlySwitching();
+        mTestableLooper.processAllMessages();
+
+        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
+
+        bouncerFullyVisibleAndNotGoingToSleep();
+        fingerprintIsNotEnrolled();
+        mTestableLooper.processAllMessages();
+
+        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
+    }
+
+    @Test
+    public void testShouldListenForFace_whenAuthInterruptIsActive_returnsTrue()
+            throws RemoteException {
+        // Preconditions for face auth to run
+        keyguardNotGoingAway();
+        currentUserIsPrimary();
+        currentUserDoesNotHaveTrust();
+        biometricsNotDisabledThroughDevicePolicyManager();
+        biometricsEnabledForCurrentUser();
+        userNotCurrentlySwitching();
+        mTestableLooper.processAllMessages();
+
+        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
+
+        triggerAuthInterrupt();
+        mTestableLooper.processAllMessages();
+
+        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
+    }
+
+    @Test
+    public void testShouldListenForFace_whenKeyguardIsAwake_returnsTrue() throws RemoteException {
+        // Preconditions for face auth to run
+        keyguardNotGoingAway();
+        currentUserIsPrimary();
+        currentUserDoesNotHaveTrust();
+        biometricsNotDisabledThroughDevicePolicyManager();
+        biometricsEnabledForCurrentUser();
+        userNotCurrentlySwitching();
+        bouncerFullyVisible();
+
+        statusBarShadeIsLocked();
+        mTestableLooper.processAllMessages();
+
+        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
+
+        deviceNotGoingToSleep();
+        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
+        deviceIsInteractive();
+        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
+        keyguardIsVisible();
+        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
+        statusBarShadeIsNotLocked();
+        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
+        bouncerNotFullyVisible();
+
+        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
+    }
+
+    @Test
+    public void testShouldListenForFace_whenUdfpsFingerDown_returnsTrue() throws RemoteException {
+        // Preconditions for face auth to run
+        keyguardNotGoingAway();
+        currentUserIsPrimary();
+        currentUserDoesNotHaveTrust();
+        biometricsNotDisabledThroughDevicePolicyManager();
+        biometricsEnabledForCurrentUser();
+        userNotCurrentlySwitching();
+        when(mAuthController.isUdfpsFingerDown()).thenReturn(false);
+        mTestableLooper.processAllMessages();
+
+        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
+
+        when(mAuthController.isUdfpsFingerDown()).thenReturn(true);
+        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
+    }
+
+    @Test
+    public void testShouldListenForFace_whenUdfpsBouncerIsShowing_returnsTrue()
+            throws RemoteException {
+        // Preconditions for face auth to run
+        keyguardNotGoingAway();
+        currentUserIsPrimary();
+        currentUserDoesNotHaveTrust();
+        biometricsNotDisabledThroughDevicePolicyManager();
+        biometricsEnabledForCurrentUser();
+        userNotCurrentlySwitching();
+        mTestableLooper.processAllMessages();
+        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
+
+        mKeyguardUpdateMonitor.setUdfpsBouncerShowing(true);
+
+        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
+    }
+
+    @Test
+    public void testBouncerVisibility_whenBothFingerprintAndFaceIsEnrolled_stopsFaceAuth()
+            throws RemoteException {
+        // Both fingerprint and face are enrolled by default
+        // Preconditions for face auth to run
+        keyguardNotGoingAway();
+        currentUserIsPrimary();
+        currentUserDoesNotHaveTrust();
+        biometricsNotDisabledThroughDevicePolicyManager();
+        biometricsEnabledForCurrentUser();
+        userNotCurrentlySwitching();
+        deviceNotGoingToSleep();
+        deviceIsInteractive();
+        statusBarShadeIsNotLocked();
+        keyguardIsVisible();
+
+        mTestableLooper.processAllMessages();
+
+        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
+
+        mKeyguardUpdateMonitor.requestFaceAuth(true);
+        verify(mFaceManager).authenticate(any(),
+                mCancellationSignalCaptor.capture(),
+                mAuthenticationCallbackCaptor.capture(),
+                any(),
+                anyInt(),
+                anyBoolean());
+        CancellationSignal cancelSignal = mCancellationSignalCaptor.getValue();
+
+        bouncerFullyVisible();
+        mTestableLooper.processAllMessages();
+
+        assertThat(cancelSignal.isCanceled()).isTrue();
+    }
+
+    @Test
+    public void testFingerprintCanAuth_whenCancellationNotReceivedAndAuthFailed() {
+        mKeyguardUpdateMonitor.dispatchStartedWakingUp();
+        mTestableLooper.processAllMessages();
+        mKeyguardUpdateMonitor.onKeyguardVisibilityChanged(true);
+
+        verify(mFaceManager).authenticate(any(), any(), any(), any(), anyInt(), anyBoolean());
+        verify(mFingerprintManager).authenticate(any(), any(), any(), any(), anyInt(), anyInt(),
+                anyInt());
+
+        mKeyguardUpdateMonitor.onFaceAuthenticated(0, false);
+        // Make sure keyguard is going away after face auth attempt, and that it calls
+        // updateBiometricStateListeningState.
+        mKeyguardUpdateMonitor.onKeyguardVisibilityChanged(false);
+        mTestableLooper.processAllMessages();
+
+        verify(mHandler).postDelayed(mKeyguardUpdateMonitor.mFpCancelNotReceived,
+                DEFAULT_CANCEL_SIGNAL_TIMEOUT);
+
+        mKeyguardUpdateMonitor.onFingerprintAuthenticated(0, true);
+        mTestableLooper.processAllMessages();
+
+        verify(mHandler, times(1)).removeCallbacks(mKeyguardUpdateMonitor.mFpCancelNotReceived);
+        mKeyguardUpdateMonitor.dispatchStartedGoingToSleep(0 /* why */);
+        mTestableLooper.processAllMessages();
+        assertThat(mKeyguardUpdateMonitor.shouldListenForFingerprint(anyBoolean())).isEqualTo(true);
+    }
+
+    private void fingerprintIsNotEnrolled() {
+        when(mFingerprintManager.hasEnrolledTemplates(mCurrentUserId)).thenReturn(false);
+    }
+
+    private void statusBarShadeIsNotLocked() {
+        mStatusBarStateListener.onStateChanged(StatusBarState.KEYGUARD);
+    }
+
+    private void statusBarShadeIsLocked() {
+        mStatusBarStateListener.onStateChanged(StatusBarState.SHADE_LOCKED);
+    }
+
+    private void keyguardIsVisible() {
+        mKeyguardUpdateMonitor.onKeyguardVisibilityChanged(true);
+    }
+
+    private void triggerAuthInterrupt() {
+        mKeyguardUpdateMonitor.onAuthInterruptDetected(true);
+    }
+
+    private void occludingAppRequestsFaceAuth() {
+        mKeyguardUpdateMonitor.requestFaceAuthOnOccludingApp(true);
+    }
+
+    private void secureCameraLaunched() {
+        mKeyguardUpdateMonitor.onCameraLaunched();
+    }
+
+    private void userCurrentlySwitching() {
+        mKeyguardUpdateMonitor.setSwitchingUser(true);
+    }
+
+    private void fingerprintErrorLockedOut() {
+        mKeyguardUpdateMonitor.mFingerprintAuthenticationCallback
+                .onAuthenticationError(FINGERPRINT_ERROR_LOCKOUT, "Fingerprint locked out");
+    }
+
+    private void triggerSuccessfulFaceAuth() {
+        mKeyguardUpdateMonitor.requestFaceAuth(true);
+        verify(mFaceManager).authenticate(any(),
+                any(),
+                mAuthenticationCallbackCaptor.capture(),
+                any(),
+                anyInt(),
+                anyBoolean());
+        mAuthenticationCallbackCaptor.getValue()
+                .onAuthenticationSucceeded(
+                        new FaceManager.AuthenticationResult(null, null, mCurrentUserId, false));
+    }
+
+    private void currentUserIsPrimary() {
+        when(mUserManager.isPrimaryUser()).thenReturn(true);
+    }
+
+    private void biometricsNotDisabledThroughDevicePolicyManager() {
+        when(mDevicePolicyManager.getKeyguardDisabledFeatures(null,
+                KeyguardUpdateMonitor.getCurrentUser())).thenReturn(0);
+    }
+
+    private void biometricsEnabledForCurrentUser() throws RemoteException {
+        mBiometricEnabledOnKeyguardCallback.onChanged(true, KeyguardUpdateMonitor.getCurrentUser());
+    }
+
+    private void biometricsDisabledForCurrentUser() throws RemoteException {
+        mBiometricEnabledOnKeyguardCallback.onChanged(
+                false,
+                KeyguardUpdateMonitor.getCurrentUser()
+        );
+    }
+
+    private void strongAuthNotRequired() {
+        when(mStrongAuthTracker.getStrongAuthForUser(KeyguardUpdateMonitor.getCurrentUser()))
+                .thenReturn(0);
+    }
+
+    private void currentUserDoesNotHaveTrust() {
+        mKeyguardUpdateMonitor.onTrustChanged(
+                false,
+                KeyguardUpdateMonitor.getCurrentUser(),
+                -1,
+                new ArrayList<>()
+        );
+    }
+
+    private void userNotCurrentlySwitching() {
+        mKeyguardUpdateMonitor.setSwitchingUser(false);
+    }
+
+    private void keyguardNotGoingAway() {
+        mKeyguardUpdateMonitor.setKeyguardGoingAway(false);
+    }
+
+    private void bouncerFullyVisibleAndNotGoingToSleep() {
+        bouncerFullyVisible();
+        deviceNotGoingToSleep();
+    }
+
+    private void deviceNotGoingToSleep() {
+        mKeyguardUpdateMonitor.dispatchFinishedGoingToSleep(/* value doesn't matter */1);
+    }
+
+    private void deviceIsInteractive() {
+        mKeyguardUpdateMonitor.dispatchStartedWakingUp();
+    }
+
+    private void bouncerNotFullyVisible() {
+        setKeyguardBouncerVisibility(false);
+    }
+
+    private void bouncerFullyVisible() {
+        setKeyguardBouncerVisibility(true);
+    }
+
     private void setKeyguardBouncerVisibility(boolean isVisible) {
         mKeyguardUpdateMonitor.sendKeyguardBouncerChanged(isVisible, isVisible);
         mTestableLooper.processAllMessages();
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 c48cbb1..0f11241 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt
@@ -26,6 +26,7 @@
 import junit.framework.Assert.assertNull
 import junit.framework.Assert.assertTrue
 import junit.framework.AssertionFailedError
+import kotlin.concurrent.thread
 import org.junit.After
 import org.junit.Before
 import org.junit.Rule
@@ -34,19 +35,18 @@
 import org.mockito.ArgumentCaptor
 import org.mockito.ArgumentMatchers.anyBoolean
 import org.mockito.Mock
-import org.mockito.Mockito.`when`
 import org.mockito.Mockito.never
 import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
 import org.mockito.Spy
 import org.mockito.junit.MockitoJUnit
-import kotlin.concurrent.thread
 
 @SmallTest
 @RunWith(AndroidTestingRunner::class)
 @RunWithLooper
 class ActivityLaunchAnimatorTest : SysuiTestCase() {
     private val launchContainer = LinearLayout(mContext)
-    private val testLaunchAnimator = LaunchAnimator(TEST_TIMINGS, TEST_INTERPOLATORS)
+    private val testLaunchAnimator = fakeLaunchAnimator()
     @Mock lateinit var callback: ActivityLaunchAnimator.Callback
     @Mock lateinit var listener: ActivityLaunchAnimator.Listener
     @Spy private val controller = TestLaunchAnimatorController(launchContainer)
@@ -77,12 +77,13 @@
         // We start in a new thread so that we can ensure that the callbacks are called in the main
         // thread.
         thread {
-            animator.startIntentWithAnimation(
+                animator.startIntentWithAnimation(
                     controller = controller,
                     animate = animate,
                     intentStarter = intentStarter
-            )
-        }.join()
+                )
+            }
+            .join()
     }
 
     @Test
@@ -197,14 +198,25 @@
         val bounds = Rect(10 /* left */, 20 /* top */, 30 /* right */, 40 /* bottom */)
         val taskInfo = ActivityManager.RunningTaskInfo()
         taskInfo.topActivity = ComponentName("com.android.systemui", "FakeActivity")
-        taskInfo.topActivityInfo = ActivityInfo().apply {
-            applicationInfo = ApplicationInfo()
-        }
+        taskInfo.topActivityInfo = ActivityInfo().apply { applicationInfo = ApplicationInfo() }
 
         return RemoteAnimationTarget(
-                0, RemoteAnimationTarget.MODE_OPENING, SurfaceControl(), false, Rect(), Rect(), 0,
-                Point(), Rect(), bounds, WindowConfiguration(), false, SurfaceControl(), Rect(),
-                taskInfo, false
+            0,
+            RemoteAnimationTarget.MODE_OPENING,
+            SurfaceControl(),
+            false,
+            Rect(),
+            Rect(),
+            0,
+            Point(),
+            Rect(),
+            bounds,
+            WindowConfiguration(),
+            false,
+            SurfaceControl(),
+            Rect(),
+            taskInfo,
+            false
         )
     }
 }
@@ -213,17 +225,17 @@
  * A simple implementation of [ActivityLaunchAnimator.Controller] which throws if it is called
  * outside of the main thread.
  */
-private class TestLaunchAnimatorController(
-    override var launchContainer: ViewGroup
-) : ActivityLaunchAnimator.Controller {
-    override fun createAnimatorState() = LaunchAnimator.State(
+private class TestLaunchAnimatorController(override var launchContainer: ViewGroup) :
+    ActivityLaunchAnimator.Controller {
+    override fun createAnimatorState() =
+        LaunchAnimator.State(
             top = 100,
             bottom = 200,
             left = 300,
             right = 400,
             topCornerRadius = 10f,
             bottomCornerRadius = 20f
-    )
+        )
 
     private fun assertOnMainThread() {
         if (Looper.myLooper() != Looper.getMainLooper()) {
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 4218e09..7c1e384 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt
@@ -5,7 +5,6 @@
 import android.graphics.Color
 import android.graphics.drawable.ColorDrawable
 import android.os.Bundle
-import android.service.dreams.IDreamManager
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import android.testing.ViewUtils
@@ -38,19 +37,16 @@
 @RunWith(AndroidTestingRunner::class)
 @TestableLooper.RunWithLooper
 class DialogLaunchAnimatorTest : SysuiTestCase() {
-    private val launchAnimator = LaunchAnimator(TEST_TIMINGS, TEST_INTERPOLATORS)
     private lateinit var dialogLaunchAnimator: DialogLaunchAnimator
     private val attachedViews = mutableSetOf<View>()
 
-    @Mock lateinit var dreamManager: IDreamManager
     @Mock lateinit var interactionJankMonitor: InteractionJankMonitor
     @get:Rule val rule = MockitoJUnit.rule()
 
     @Before
     fun setUp() {
-        dialogLaunchAnimator = DialogLaunchAnimator(
-            dreamManager, interactionJankMonitor, launchAnimator, isForTesting = true
-        )
+        dialogLaunchAnimator =
+            fakeDialogLaunchAnimator(interactionJankMonitor = interactionJankMonitor)
     }
 
     @After
@@ -153,6 +149,22 @@
     }
 
     @Test
+    fun testActivityLaunchWhenLockedWithoutAlternateAuth() {
+        val dialogLaunchAnimator =
+            fakeDialogLaunchAnimator(isUnlocked = false, isShowingAlternateAuthOnUnlock = false)
+        val dialog = createAndShowDialog(dialogLaunchAnimator)
+        assertNull(dialogLaunchAnimator.createActivityLaunchController(dialog.contentView))
+    }
+
+    @Test
+    fun testActivityLaunchWhenLockedWithAlternateAuth() {
+        val dialogLaunchAnimator =
+            fakeDialogLaunchAnimator(isUnlocked = false, isShowingAlternateAuthOnUnlock = true)
+        val dialog = createAndShowDialog(dialogLaunchAnimator)
+        assertNotNull(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).
@@ -193,11 +205,13 @@
         verify(interactionJankMonitor).end(InteractionJankMonitor.CUJ_USER_DIALOG_OPEN)
     }
 
-    private fun createAndShowDialog(): TestDialog {
+    private fun createAndShowDialog(
+        animator: DialogLaunchAnimator = dialogLaunchAnimator,
+    ): TestDialog {
         val touchSurface = createTouchSurface()
         return runOnMainThreadAndWaitForIdleSync {
             val dialog = TestDialog(context)
-            dialogLaunchAnimator.showFromView(dialog, touchSurface)
+            animator.showFromView(dialog, touchSurface)
             dialog
         }
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/TestValues.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/TestValues.kt
deleted file mode 100644
index dadf94e..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/TestValues.kt
+++ /dev/null
@@ -1,23 +0,0 @@
-package com.android.systemui.animation
-
-/**
- * A [LaunchAnimator.Timings] to be used in tests.
- *
- * Note that all timings except the total duration are non-zero to avoid divide-by-zero exceptions
- * when computing the progress of a sub-animation (the contents fade in/out).
- */
-val TEST_TIMINGS = LaunchAnimator.Timings(
-    totalDuration = 0L,
-    contentBeforeFadeOutDelay = 1L,
-    contentBeforeFadeOutDuration = 1L,
-    contentAfterFadeInDelay = 1L,
-    contentAfterFadeInDuration = 1L
-)
-
-/** A [LaunchAnimator.Interpolators] to be used in tests. */
-val TEST_INTERPOLATORS = LaunchAnimator.Interpolators(
-    positionInterpolator = Interpolators.STANDARD,
-    positionXInterpolator = Interpolators.STANDARD,
-    contentBeforeFadeOutInterpolator = Interpolators.STANDARD,
-    contentAfterFadeInInterpolator = Interpolators.STANDARD
-)
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationUtilsTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationUtilsTest.java
index 2915f5a..e099c92 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationUtilsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationUtilsTest.java
@@ -20,6 +20,7 @@
 import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_CAST_INFO;
 import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_DATE;
 import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_HOME_CONTROLS;
+import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_SMARTSPACE;
 import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_TIME;
 import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_WEATHER;
 import static com.android.systemui.dreams.complication.ComplicationUtils.convertComplicationType;
@@ -60,6 +61,8 @@
                 .isEqualTo(COMPLICATION_TYPE_CAST_INFO);
         assertThat(convertComplicationType(DreamBackend.COMPLICATION_TYPE_HOME_CONTROLS))
                 .isEqualTo(COMPLICATION_TYPE_HOME_CONTROLS);
+        assertThat(convertComplicationType(DreamBackend.COMPLICATION_TYPE_SMARTSPACE))
+                .isEqualTo(COMPLICATION_TYPE_SMARTSPACE);
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FakeFeatureFlags.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/FakeFeatureFlags.kt
index b2a4e33..7b1455c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FakeFeatureFlags.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FakeFeatureFlags.kt
@@ -19,7 +19,6 @@
 import android.util.SparseArray
 import android.util.SparseBooleanArray
 import androidx.core.util.containsKey
-import java.lang.IllegalStateException
 
 class FakeFeatureFlags : FeatureFlags {
     private val booleanFlags = SparseBooleanArray()
@@ -57,7 +56,10 @@
         stringFlags.put(flag.id, value)
     }
 
-    override fun isEnabled(flag: BooleanFlag): Boolean = requireBooleanValue(flag.id)
+
+    override fun isEnabled(flag: UnreleasedFlag): Boolean = requireBooleanValue(flag.id)
+
+    override fun isEnabled(flag: ReleasedFlag): Boolean = requireBooleanValue(flag.id)
 
     override fun isEnabled(flag: ResourceBooleanFlag): Boolean = requireBooleanValue(flag.id)
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FakeFeatureFlagsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/FakeFeatureFlagsTest.kt
index 7d4b4f5..ff579a1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FakeFeatureFlagsTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FakeFeatureFlagsTest.kt
@@ -29,11 +29,12 @@
 @RunWith(AndroidTestingRunner::class)
 class FakeFeatureFlagsTest : SysuiTestCase() {
 
-    private val booleanFlag = BooleanFlag(-1000)
-    private val stringFlag = StringFlag(-1001)
-    private val resourceBooleanFlag = ResourceBooleanFlag(-1002, resourceId = -1)
-    private val resourceStringFlag = ResourceStringFlag(-1003, resourceId = -1)
-    private val sysPropBooleanFlag = SysPropBooleanFlag(-1004, name = "test")
+    private val unreleasedFlag = UnreleasedFlag(-1000)
+    private val releasedFlag = ReleasedFlag(-1001)
+    private val stringFlag = StringFlag(-1002)
+    private val resourceBooleanFlag = ResourceBooleanFlag(-1003, resourceId = -1)
+    private val resourceStringFlag = ResourceStringFlag(-1004, resourceId = -1)
+    private val sysPropBooleanFlag = SysPropBooleanFlag(-1005, name = "test")
 
     /**
      * FakeFeatureFlags does not honor any default values. All flags which are accessed must be
@@ -49,56 +50,66 @@
             assertThat(ex.message).contains("TEAMFOOD")
         }
         try {
-            assertThat(flags.isEnabled(booleanFlag)).isFalse()
+            assertThat(flags.isEnabled(unreleasedFlag)).isFalse()
             fail("Expected an exception when accessing an unspecified flag.")
         } catch (ex: IllegalStateException) {
             assertThat(ex.message).contains("UNKNOWN(id=-1000)")
         }
         try {
-            assertThat(flags.isEnabled(resourceBooleanFlag)).isFalse()
-            fail("Expected an exception when accessing an unspecified flag.")
-        } catch (ex: IllegalStateException) {
-            assertThat(ex.message).contains("UNKNOWN(id=-1002)")
-        }
-        try {
-            assertThat(flags.isEnabled(sysPropBooleanFlag)).isFalse()
-            fail("Expected an exception when accessing an unspecified flag.")
-        } catch (ex: IllegalStateException) {
-            assertThat(ex.message).contains("UNKNOWN(id=-1004)")
-        }
-        try {
-            assertThat(flags.getString(stringFlag)).isEmpty()
+            assertThat(flags.isEnabled(releasedFlag)).isFalse()
             fail("Expected an exception when accessing an unspecified flag.")
         } catch (ex: IllegalStateException) {
             assertThat(ex.message).contains("UNKNOWN(id=-1001)")
         }
         try {
-            assertThat(flags.getString(resourceStringFlag)).isEmpty()
+            assertThat(flags.isEnabled(resourceBooleanFlag)).isFalse()
             fail("Expected an exception when accessing an unspecified flag.")
         } catch (ex: IllegalStateException) {
             assertThat(ex.message).contains("UNKNOWN(id=-1003)")
         }
+        try {
+            assertThat(flags.isEnabled(sysPropBooleanFlag)).isFalse()
+            fail("Expected an exception when accessing an unspecified flag.")
+        } catch (ex: IllegalStateException) {
+            assertThat(ex.message).contains("UNKNOWN(id=-1005)")
+        }
+        try {
+            assertThat(flags.getString(stringFlag)).isEmpty()
+            fail("Expected an exception when accessing an unspecified flag.")
+        } catch (ex: IllegalStateException) {
+            assertThat(ex.message).contains("UNKNOWN(id=-1002)")
+        }
+        try {
+            assertThat(flags.getString(resourceStringFlag)).isEmpty()
+            fail("Expected an exception when accessing an unspecified flag.")
+        } catch (ex: IllegalStateException) {
+            assertThat(ex.message).contains("UNKNOWN(id=-1004)")
+        }
     }
 
     @Test
     fun specifiedFlagsReturnCorrectValues() {
         val flags = FakeFeatureFlags()
-        flags.set(booleanFlag, false)
+        flags.set(unreleasedFlag, false)
+        flags.set(releasedFlag, false)
         flags.set(resourceBooleanFlag, false)
         flags.set(sysPropBooleanFlag, false)
         flags.set(resourceStringFlag, "")
 
-        assertThat(flags.isEnabled(booleanFlag)).isFalse()
+        assertThat(flags.isEnabled(unreleasedFlag)).isFalse()
+        assertThat(flags.isEnabled(releasedFlag)).isFalse()
         assertThat(flags.isEnabled(resourceBooleanFlag)).isFalse()
         assertThat(flags.isEnabled(sysPropBooleanFlag)).isFalse()
         assertThat(flags.getString(resourceStringFlag)).isEmpty()
 
-        flags.set(booleanFlag, true)
+        flags.set(unreleasedFlag, true)
+        flags.set(releasedFlag, true)
         flags.set(resourceBooleanFlag, true)
         flags.set(sysPropBooleanFlag, true)
         flags.set(resourceStringFlag, "Android")
 
-        assertThat(flags.isEnabled(booleanFlag)).isTrue()
+        assertThat(flags.isEnabled(unreleasedFlag)).isTrue()
+        assertThat(flags.isEnabled(releasedFlag)).isTrue()
         assertThat(flags.isEnabled(resourceBooleanFlag)).isTrue()
         assertThat(flags.isEnabled(sysPropBooleanFlag)).isTrue()
         assertThat(flags.getString(resourceStringFlag)).isEqualTo("Android")
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsDebugTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsDebugTest.kt
index 51f3404..4511193 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsDebugTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsDebugTest.kt
@@ -35,6 +35,10 @@
 import com.android.systemui.util.mockito.withArgCaptor
 import com.android.systemui.util.settings.SecureSettings
 import com.google.common.truth.Truth.assertThat
+import java.io.PrintWriter
+import java.io.Serializable
+import java.io.StringWriter
+import java.util.function.Consumer
 import org.junit.Assert
 import org.junit.Before
 import org.junit.Test
@@ -48,12 +52,8 @@
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.verifyNoMoreInteractions
 import org.mockito.Mockito.verifyZeroInteractions
-import org.mockito.MockitoAnnotations
-import java.io.PrintWriter
-import java.io.Serializable
-import java.io.StringWriter
-import java.util.function.Consumer
 import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
 
 /**
  * NOTE: This test is for the version of FeatureFlagManager in src-release, which should not allow
@@ -75,10 +75,11 @@
     private val flagMap = mutableMapOf<Int, Flag<*>>()
     private lateinit var broadcastReceiver: BroadcastReceiver
     private lateinit var clearCacheAction: Consumer<Int>
+    private val serverFlagReader = ServerFlagReaderFake()
 
     private val deviceConfig = DeviceConfigProxyFake()
-    private val teamfoodableFlagA = BooleanFlag(500, false, true)
-    private val teamfoodableFlagB = BooleanFlag(501, true, true)
+    private val teamfoodableFlagA = UnreleasedFlag(500, true)
+    private val teamfoodableFlagB = ReleasedFlag(501, true)
 
     @Before
     fun setup() {
@@ -93,6 +94,7 @@
             resources,
             dumpManager,
             deviceConfig,
+            serverFlagReader,
             flagMap,
             commandRegistry,
             barService
@@ -109,40 +111,41 @@
     }
 
     @Test
-    fun testReadBooleanFlag() {
+    fun readBooleanFlag() {
         // Remember that the TEAMFOOD flag is id#1 and has special behavior.
         whenever(flagManager.readFlagValue<Boolean>(eq(3), any())).thenReturn(true)
         whenever(flagManager.readFlagValue<Boolean>(eq(4), any())).thenReturn(false)
-        assertThat(mFeatureFlagsDebug.isEnabled(BooleanFlag(2, true))).isTrue()
-        assertThat(mFeatureFlagsDebug.isEnabled(BooleanFlag(3, false))).isTrue()
-        assertThat(mFeatureFlagsDebug.isEnabled(BooleanFlag(4, true))).isFalse()
-        assertThat(mFeatureFlagsDebug.isEnabled(BooleanFlag(5, false))).isFalse()
+
+        assertThat(mFeatureFlagsDebug.isEnabled(ReleasedFlag(2))).isTrue()
+        assertThat(mFeatureFlagsDebug.isEnabled(UnreleasedFlag(3))).isTrue()
+        assertThat(mFeatureFlagsDebug.isEnabled(ReleasedFlag(4))).isFalse()
+        assertThat(mFeatureFlagsDebug.isEnabled(UnreleasedFlag(5))).isFalse()
     }
 
     @Test
-    fun testTeamFoodFlag_False() {
+    fun teamFoodFlag_False() {
         whenever(flagManager.readFlagValue<Boolean>(eq(1), any())).thenReturn(false)
         assertThat(mFeatureFlagsDebug.isEnabled(teamfoodableFlagA)).isFalse()
         assertThat(mFeatureFlagsDebug.isEnabled(teamfoodableFlagB)).isTrue()
 
         // Regular boolean flags should still test the same.
         // Only our teamfoodableFlag should change.
-        testReadBooleanFlag()
+        readBooleanFlag()
     }
 
     @Test
-    fun testTeamFoodFlag_True() {
+    fun teamFoodFlag_True() {
         whenever(flagManager.readFlagValue<Boolean>(eq(1), any())).thenReturn(true)
         assertThat(mFeatureFlagsDebug.isEnabled(teamfoodableFlagA)).isTrue()
         assertThat(mFeatureFlagsDebug.isEnabled(teamfoodableFlagB)).isTrue()
 
         // Regular boolean flags should still test the same.
         // Only our teamfoodableFlag should change.
-        testReadBooleanFlag()
+        readBooleanFlag()
     }
 
     @Test
-    fun testTeamFoodFlag_Overridden() {
+    fun teamFoodFlag_Overridden() {
         whenever(flagManager.readFlagValue<Boolean>(eq(teamfoodableFlagA.id), any()))
                 .thenReturn(true)
         whenever(flagManager.readFlagValue<Boolean>(eq(teamfoodableFlagB.id), any()))
@@ -153,11 +156,11 @@
 
         // Regular boolean flags should still test the same.
         // Only our teamfoodableFlag should change.
-        testReadBooleanFlag()
+        readBooleanFlag()
     }
 
     @Test
-    fun testReadResourceBooleanFlag() {
+    fun readResourceBooleanFlag() {
         whenever(resources.getBoolean(1001)).thenReturn(false)
         whenever(resources.getBoolean(1002)).thenReturn(true)
         whenever(resources.getBoolean(1003)).thenReturn(false)
@@ -182,7 +185,7 @@
     }
 
     @Test
-    fun testReadSysPropBooleanFlag() {
+    fun readSysPropBooleanFlag() {
         whenever(systemProperties.getBoolean(anyString(), anyBoolean())).thenAnswer {
             if ("b".equals(it.getArgument<String?>(0))) {
                 return@thenAnswer true
@@ -198,7 +201,7 @@
     }
 
     @Test
-    fun testReadDeviceConfigBooleanFlag() {
+    fun readDeviceConfigBooleanFlag() {
         val namespace = "test_namespace"
         deviceConfig.setProperty(namespace, "a", "true", false)
         deviceConfig.setProperty(namespace, "b", "false", false)
@@ -213,7 +216,7 @@
     }
 
     @Test
-    fun testReadStringFlag() {
+    fun readStringFlag() {
         whenever(flagManager.readFlagValue<String>(eq(3), any())).thenReturn("foo")
         whenever(flagManager.readFlagValue<String>(eq(4), any())).thenReturn("bar")
         assertThat(mFeatureFlagsDebug.getString(StringFlag(1, "biz"))).isEqualTo("biz")
@@ -223,7 +226,7 @@
     }
 
     @Test
-    fun testReadResourceStringFlag() {
+    fun readResourceStringFlag() {
         whenever(resources.getString(1001)).thenReturn("")
         whenever(resources.getString(1002)).thenReturn("resource2")
         whenever(resources.getString(1003)).thenReturn("resource3")
@@ -253,8 +256,8 @@
     }
 
     @Test
-    fun testBroadcastReceiverIgnoresInvalidData() {
-        addFlag(BooleanFlag(1, false))
+    fun broadcastReceiver_IgnoresInvalidData() {
+        addFlag(UnreleasedFlag(1))
         addFlag(ResourceBooleanFlag(2, 1002))
         addFlag(StringFlag(3, "flag3"))
         addFlag(ResourceStringFlag(4, 1004))
@@ -272,10 +275,10 @@
     }
 
     @Test
-    fun testIntentWithIdButNoValueKeyClears() {
-        addFlag(BooleanFlag(1, false))
+    fun intentWithId_NoValueKeyClears() {
+        addFlag(UnreleasedFlag(1))
 
-        // trying to erase an id not in the map does noting
+        // trying to erase an id not in the map does nothing
         broadcastReceiver.onReceive(
             mockContext,
             Intent(FlagManager.ACTION_SET_FLAG).putExtra(FlagManager.EXTRA_ID, 0)
@@ -291,9 +294,9 @@
     }
 
     @Test
-    fun testSetBooleanFlag() {
-        addFlag(BooleanFlag(1, false))
-        addFlag(BooleanFlag(2, false))
+    fun setBooleanFlag() {
+        addFlag(UnreleasedFlag(1))
+        addFlag(UnreleasedFlag(2))
         addFlag(ResourceBooleanFlag(3, 1003))
         addFlag(ResourceBooleanFlag(4, 1004))
 
@@ -311,7 +314,7 @@
     }
 
     @Test
-    fun testSetStringFlag() {
+    fun setStringFlag() {
         addFlag(StringFlag(1, "flag1"))
         addFlag(ResourceStringFlag(2, 1002))
 
@@ -323,7 +326,7 @@
     }
 
     @Test
-    fun testSetFlagClearsCache() {
+    fun setFlag_ClearsCache() {
         val flag1 = addFlag(StringFlag(1, "flag1"))
         whenever(flagManager.readFlagValue<String>(eq(1), any())).thenReturn("original")
 
@@ -345,12 +348,30 @@
     }
 
     @Test
-    fun testRegisterCommand() {
+    fun serverSide_Overrides_MakesFalse() {
+        val flag = ReleasedFlag(100)
+
+        serverFlagReader.setFlagValue(flag.id, false)
+
+        assertThat(mFeatureFlagsDebug.isEnabled(flag)).isFalse()
+    }
+
+    @Test
+    fun serverSide_Overrides_MakesTrue() {
+        val flag = UnreleasedFlag(100)
+
+        serverFlagReader.setFlagValue(flag.id, true)
+
+        assertThat(mFeatureFlagsDebug.isEnabled(flag)).isTrue()
+    }
+
+    @Test
+    fun statusBarCommand_IsRegistered() {
         verify(commandRegistry).registerCommand(anyString(), any())
     }
 
     @Test
-    fun testNoOpCommand() {
+    fun noOpCommand() {
         val cmd = captureCommand()
 
         cmd.execute(pw, ArrayList())
@@ -360,37 +381,73 @@
     }
 
     @Test
-    fun testReadFlagCommand() {
-        addFlag(BooleanFlag(1, false))
+    fun readFlagCommand() {
+        addFlag(UnreleasedFlag(1))
         val cmd = captureCommand()
         cmd.execute(pw, listOf("1"))
         verify(flagManager).readFlagValue<Boolean>(eq(1), any())
     }
 
     @Test
-    fun testSetFlagCommand() {
-        addFlag(BooleanFlag(1, false))
+    fun setFlagCommand() {
+        addFlag(UnreleasedFlag(1))
         val cmd = captureCommand()
         cmd.execute(pw, listOf("1", "on"))
         verifyPutData(1, "{\"type\":\"boolean\",\"value\":true}")
     }
 
     @Test
-    fun testToggleFlagCommand() {
-        addFlag(BooleanFlag(1, true))
+    fun toggleFlagCommand() {
+        addFlag(ReleasedFlag(1))
         val cmd = captureCommand()
         cmd.execute(pw, listOf("1", "toggle"))
         verifyPutData(1, "{\"type\":\"boolean\",\"value\":false}", 2)
     }
 
     @Test
-    fun testEraseFlagCommand() {
-        addFlag(BooleanFlag(1, true))
+    fun eraseFlagCommand() {
+        addFlag(ReleasedFlag(1))
         val cmd = captureCommand()
         cmd.execute(pw, listOf("1", "erase"))
         verify(secureSettings).putStringForUser(eq("key-1"), eq(""), anyInt())
     }
 
+    @Test
+    fun dumpFormat() {
+        val flag1 = ReleasedFlag(1)
+        val flag2 = ResourceBooleanFlag(2, 1002)
+        val flag3 = UnreleasedFlag(3)
+        val flag4 = StringFlag(4, "")
+        val flag5 = StringFlag(5, "flag5default")
+        val flag6 = ResourceStringFlag(6, 1006)
+        val flag7 = ResourceStringFlag(7, 1007)
+
+        whenever(resources.getBoolean(1002)).thenReturn(true)
+        whenever(resources.getString(1006)).thenReturn("resource1006")
+        whenever(resources.getString(1007)).thenReturn("resource1007")
+        whenever(flagManager.readFlagValue(eq(7), eq(StringFlagSerializer)))
+            .thenReturn("override7")
+
+        // WHEN the flags have been accessed
+        assertThat(mFeatureFlagsDebug.isEnabled(flag1)).isTrue()
+        assertThat(mFeatureFlagsDebug.isEnabled(flag2)).isTrue()
+        assertThat(mFeatureFlagsDebug.isEnabled(flag3)).isFalse()
+        assertThat(mFeatureFlagsDebug.getString(flag4)).isEmpty()
+        assertThat(mFeatureFlagsDebug.getString(flag5)).isEqualTo("flag5default")
+        assertThat(mFeatureFlagsDebug.getString(flag6)).isEqualTo("resource1006")
+        assertThat(mFeatureFlagsDebug.getString(flag7)).isEqualTo("override7")
+
+        // THEN the dump contains the flags and the default values
+        val dump = dumpToString()
+        assertThat(dump).contains(" sysui_flag_1: true\n")
+        assertThat(dump).contains(" sysui_flag_2: true\n")
+        assertThat(dump).contains(" sysui_flag_3: false\n")
+        assertThat(dump).contains(" sysui_flag_4: [length=0] \"\"\n")
+        assertThat(dump).contains(" sysui_flag_5: [length=12] \"flag5default\"\n")
+        assertThat(dump).contains(" sysui_flag_6: [length=12] \"resource1006\"\n")
+        assertThat(dump).contains(" sysui_flag_7: [length=9] \"override7\"\n")
+    }
+
     private fun verifyPutData(id: Int, data: String, numReads: Int = 1) {
         inOrder(flagManager, secureSettings).apply {
             verify(flagManager, times(numReads)).readFlagValue(eq(id), any<FlagSerializer<*>>())
@@ -421,42 +478,6 @@
         return captor.value.invoke()
     }
 
-    @Test
-    fun testDump() {
-        val flag1 = BooleanFlag(1, true)
-        val flag2 = ResourceBooleanFlag(2, 1002)
-        val flag3 = BooleanFlag(3, false)
-        val flag4 = StringFlag(4, "")
-        val flag5 = StringFlag(5, "flag5default")
-        val flag6 = ResourceStringFlag(6, 1006)
-        val flag7 = ResourceStringFlag(7, 1007)
-
-        whenever(resources.getBoolean(1002)).thenReturn(true)
-        whenever(resources.getString(1006)).thenReturn("resource1006")
-        whenever(resources.getString(1007)).thenReturn("resource1007")
-        whenever(flagManager.readFlagValue(eq(7), eq(StringFlagSerializer)))
-            .thenReturn("override7")
-
-        // WHEN the flags have been accessed
-        assertThat(mFeatureFlagsDebug.isEnabled(flag1)).isTrue()
-        assertThat(mFeatureFlagsDebug.isEnabled(flag2)).isTrue()
-        assertThat(mFeatureFlagsDebug.isEnabled(flag3)).isFalse()
-        assertThat(mFeatureFlagsDebug.getString(flag4)).isEmpty()
-        assertThat(mFeatureFlagsDebug.getString(flag5)).isEqualTo("flag5default")
-        assertThat(mFeatureFlagsDebug.getString(flag6)).isEqualTo("resource1006")
-        assertThat(mFeatureFlagsDebug.getString(flag7)).isEqualTo("override7")
-
-        // THEN the dump contains the flags and the default values
-        val dump = dumpToString()
-        assertThat(dump).contains(" sysui_flag_1: true\n")
-        assertThat(dump).contains(" sysui_flag_2: true\n")
-        assertThat(dump).contains(" sysui_flag_3: false\n")
-        assertThat(dump).contains(" sysui_flag_4: [length=0] \"\"\n")
-        assertThat(dump).contains(" sysui_flag_5: [length=12] \"flag5default\"\n")
-        assertThat(dump).contains(" sysui_flag_6: [length=12] \"resource1006\"\n")
-        assertThat(dump).contains(" sysui_flag_7: [length=9] \"override7\"\n")
-    }
-
     private fun dumpToString(): String {
         val sw = StringWriter()
         val pw = PrintWriter(sw)
@@ -464,4 +485,4 @@
         pw.flush()
         return sw.toString()
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsReleaseTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsReleaseTest.kt
index 6b683f4..e94b520 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsReleaseTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsReleaseTest.kt
@@ -30,8 +30,8 @@
 import org.mockito.Mock
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.verifyNoMoreInteractions
-import org.mockito.MockitoAnnotations
 import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
 
 /**
  * NOTE: This test is for the version of FeatureFlagManager in src-release, which should not allow
@@ -44,13 +44,18 @@
     @Mock private lateinit var mResources: Resources
     @Mock private lateinit var mSystemProperties: SystemPropertiesHelper
     @Mock private lateinit var mDumpManager: DumpManager
+    private val serverFlagReader = ServerFlagReaderFake()
 
     private val deviceConfig = DeviceConfigProxyFake()
 
     @Before
     fun setup() {
         MockitoAnnotations.initMocks(this)
-        mFeatureFlagsRelease = FeatureFlagsRelease(mResources, mSystemProperties, deviceConfig,
+        mFeatureFlagsRelease = FeatureFlagsRelease(
+            mResources,
+            mSystemProperties,
+            deviceConfig,
+            serverFlagReader,
             mDumpManager)
     }
 
@@ -113,4 +118,22 @@
         whenever(mSystemProperties.getBoolean(flagName, flagDefault)).thenReturn(flagDefault)
         assertThat(mFeatureFlagsRelease.isEnabled(flag)).isEqualTo(flagDefault)
     }
-}
\ No newline at end of file
+
+    @Test
+    fun serverSide_OverridesReleased_MakesFalse() {
+        val flag = ReleasedFlag(100)
+
+        serverFlagReader.setFlagValue(flag.id, false)
+
+        assertThat(mFeatureFlagsRelease.isEnabled(flag)).isFalse()
+    }
+
+    @Test
+    fun serverSide_OverridesUnreleased_Ignored() {
+        val flag = UnreleasedFlag(100)
+
+        serverFlagReader.setFlagValue(flag.id, true)
+
+        assertThat(mFeatureFlagsRelease.isEnabled(flag)).isFalse()
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FlagManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/FlagManagerTest.kt
index a2eca81..17324a0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FlagManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FlagManagerTest.kt
@@ -26,6 +26,7 @@
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.withArgCaptor
 import com.google.common.truth.Truth.assertThat
+import java.util.function.Consumer
 import org.junit.Assert.assertThrows
 import org.junit.Before
 import org.junit.Test
@@ -33,9 +34,8 @@
 import org.mockito.Mockito.times
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.verifyNoMoreInteractions
-import org.mockito.MockitoAnnotations
-import java.util.function.Consumer
 import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
 
 /**
  * NOTE: This test is for the version of FeatureFlagManager in src-release, which should not allow
@@ -64,14 +64,14 @@
         verifyNoMoreInteractions(mFlagSettingsHelper)
 
         // adding the first listener registers the observer
-        mFlagManager.addListener(BooleanFlag(1, true), listener1)
+        mFlagManager.addListener(ReleasedFlag(1), listener1)
         val observer = withArgCaptor<ContentObserver> {
             verify(mFlagSettingsHelper).registerContentObserver(any(), any(), capture())
         }
         verifyNoMoreInteractions(mFlagSettingsHelper)
 
         // adding another listener does nothing
-        mFlagManager.addListener(BooleanFlag(2, true), listener2)
+        mFlagManager.addListener(ReleasedFlag(2), listener2)
         verifyNoMoreInteractions(mFlagSettingsHelper)
 
         // removing the original listener does nothing with second one still present
@@ -89,7 +89,7 @@
         val listener = mock<FlagListenable.Listener>()
         val clearCacheAction = mock<Consumer<Int>>()
         mFlagManager.clearCacheAction = clearCacheAction
-        mFlagManager.addListener(BooleanFlag(1, true), listener)
+        mFlagManager.addListener(ReleasedFlag(1), listener)
         val observer = withArgCaptor<ContentObserver> {
             verify(mFlagSettingsHelper).registerContentObserver(any(), any(), capture())
         }
@@ -101,8 +101,8 @@
     fun testObserverInvokesListeners() {
         val listener1 = mock<FlagListenable.Listener>()
         val listener10 = mock<FlagListenable.Listener>()
-        mFlagManager.addListener(BooleanFlag(1, true), listener1)
-        mFlagManager.addListener(BooleanFlag(10, true), listener10)
+        mFlagManager.addListener(ReleasedFlag(1), listener1)
+        mFlagManager.addListener(ReleasedFlag(10), listener10)
         val observer = withArgCaptor<ContentObserver> {
             verify(mFlagSettingsHelper).registerContentObserver(any(), any(), capture())
         }
@@ -127,8 +127,8 @@
     fun testOnlySpecificFlagListenerIsInvoked() {
         val listener1 = mock<FlagListenable.Listener>()
         val listener10 = mock<FlagListenable.Listener>()
-        mFlagManager.addListener(BooleanFlag(1, true), listener1)
-        mFlagManager.addListener(BooleanFlag(10, true), listener10)
+        mFlagManager.addListener(ReleasedFlag(1), listener1)
+        mFlagManager.addListener(ReleasedFlag(10), listener10)
 
         mFlagManager.dispatchListenersAndMaybeRestart(1, null)
         val flagEvent1 = withArgCaptor<FlagListenable.FlagEvent> {
@@ -148,8 +148,8 @@
     @Test
     fun testSameListenerCanBeUsedForMultipleFlags() {
         val listener = mock<FlagListenable.Listener>()
-        mFlagManager.addListener(BooleanFlag(1, true), listener)
-        mFlagManager.addListener(BooleanFlag(10, true), listener)
+        mFlagManager.addListener(ReleasedFlag(1), listener)
+        mFlagManager.addListener(ReleasedFlag(10), listener)
 
         mFlagManager.dispatchListenersAndMaybeRestart(1, null)
         val flagEvent1 = withArgCaptor<FlagListenable.FlagEvent> {
@@ -177,7 +177,7 @@
     @Test
     fun testListenerCanSuppressRestart() {
         val restartAction = mock<Consumer<Boolean>>()
-        mFlagManager.addListener(BooleanFlag(1, true)) { event ->
+        mFlagManager.addListener(ReleasedFlag(1)) { event ->
             event.requestNoRestart()
         }
         mFlagManager.dispatchListenersAndMaybeRestart(1, restartAction)
@@ -188,7 +188,7 @@
     @Test
     fun testListenerOnlySuppressesRestartForOwnFlag() {
         val restartAction = mock<Consumer<Boolean>>()
-        mFlagManager.addListener(BooleanFlag(10, true)) { event ->
+        mFlagManager.addListener(ReleasedFlag(10)) { event ->
             event.requestNoRestart()
         }
         mFlagManager.dispatchListenersAndMaybeRestart(1, restartAction)
@@ -199,10 +199,10 @@
     @Test
     fun testRestartWhenNotAllListenersRequestSuppress() {
         val restartAction = mock<Consumer<Boolean>>()
-        mFlagManager.addListener(BooleanFlag(10, true)) { event ->
+        mFlagManager.addListener(ReleasedFlag(10)) { event ->
             event.requestNoRestart()
         }
-        mFlagManager.addListener(BooleanFlag(10, true)) {
+        mFlagManager.addListener(ReleasedFlag(10)) {
             // do not request
         }
         mFlagManager.dispatchListenersAndMaybeRestart(1, restartAction)
@@ -295,4 +295,4 @@
         assertThat(StringFlagSerializer.toSettingsData("foo"))
             .isEqualTo("{\"type\":\"string\",\"value\":\"foo\"}")
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FlagsTest.java b/packages/SystemUI/tests/src/com/android/systemui/flags/FlagsTest.java
index 25c3028..250cc48 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FlagsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FlagsTest.java
@@ -107,11 +107,11 @@
     }
 
     private static class DuplicateFlagContainer {
-        public static final BooleanFlag A_FLAG = new BooleanFlag(0);
-        public static final BooleanFlag B_FLAG = new BooleanFlag(0);
+        public static final BooleanFlag A_FLAG = new UnreleasedFlag(0);
+        public static final BooleanFlag B_FLAG = new UnreleasedFlag(0);
         public static final StringFlag C_FLAG = new StringFlag(0);
 
-        public static final BooleanFlag D_FLAG = new BooleanFlag(1);
+        public static final BooleanFlag D_FLAG = new UnreleasedFlag(1);
 
         public static final DoubleFlag E_FLAG = new DoubleFlag(3);
         public static final DoubleFlag F_FLAG = new DoubleFlag(3);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest.kt
similarity index 81%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest.kt
index bcc76ab..810c6dc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.keyguard.data.repository
+package com.android.systemui.keyguard.data.quickaffordance
 
 import androidx.test.filters.SmallTest
 import com.android.systemui.R
@@ -22,11 +22,10 @@
 import com.android.systemui.controls.controller.ControlsController
 import com.android.systemui.controls.dagger.ControlsComponent
 import com.android.systemui.controls.management.ControlsListingController
-import com.android.systemui.keyguard.data.quickaffordance.HomeControlsKeyguardQuickAffordanceConfig
-import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig
 import com.android.systemui.util.mockito.mock
 import com.google.common.truth.Truth.assertThat
 import java.util.Optional
+import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.flow.onEach
 import kotlinx.coroutines.test.runBlockingTest
@@ -50,18 +49,19 @@
     companion object {
         @Parameters(
             name =
-                "feature enabled = {0}, has favorites = {1}, has service infos = {2} - expected" +
-                    " visible = {3}"
+                "feature enabled = {0}, has favorites = {1}, has service infos = {2}, can show" +
+                    " while locked = {3} - expected visible = {4}"
         )
         @JvmStatic
         fun data() =
-            (0 until 8)
+            (0 until 16)
                 .map { combination ->
                     arrayOf(
-                        /* isFeatureEnabled= */ combination and 0b100 != 0,
-                        /* hasFavorites= */ combination and 0b010 != 0,
-                        /* hasServiceInfos= */ combination and 0b001 != 0,
-                        /* isVisible= */ combination == 0b111,
+                        /* isFeatureEnabled= */ combination and 0b1000 != 0,
+                        /* hasFavorites= */ combination and 0b0100 != 0,
+                        /* hasServiceInfos= */ combination and 0b0010 != 0,
+                        /* canShowWhileLocked= */ combination and 0b0001 != 0,
+                        /* isVisible= */ combination == 0b1111,
                     )
                 }
                 .toList()
@@ -79,7 +79,8 @@
     @JvmField @Parameter(0) var isFeatureEnabled: Boolean = false
     @JvmField @Parameter(1) var hasFavorites: Boolean = false
     @JvmField @Parameter(2) var hasServiceInfos: Boolean = false
-    @JvmField @Parameter(3) var isVisible: Boolean = false
+    @JvmField @Parameter(3) var canShowWhileLocked: Boolean = false
+    @JvmField @Parameter(4) var isVisible: Boolean = false
 
     @Before
     fun setUp() {
@@ -89,6 +90,8 @@
         whenever(component.getControlsController()).thenReturn(Optional.of(controlsController))
         whenever(component.getControlsListingController())
             .thenReturn(Optional.of(controlsListingController))
+        whenever(component.canShowWhileLockedSetting)
+            .thenReturn(MutableStateFlow(canShowWhileLocked))
 
         underTest =
             HomeControlsKeyguardQuickAffordanceConfig(
@@ -111,14 +114,16 @@
         val values = mutableListOf<KeyguardQuickAffordanceConfig.State>()
         val job = underTest.state.onEach(values::add).launchIn(this)
 
-        verify(controlsListingController).addCallback(callbackCaptor.capture())
-        callbackCaptor.value.onServicesUpdated(
-            if (hasServiceInfos) {
-                listOf(mock())
-            } else {
-                emptyList()
-            }
-        )
+        if (canShowWhileLocked) {
+            verify(controlsListingController).addCallback(callbackCaptor.capture())
+            callbackCaptor.value.onServicesUpdated(
+                if (hasServiceInfos) {
+                    listOf(mock())
+                } else {
+                    emptyList()
+                }
+            )
+        }
 
         assertThat(values.last())
             .isInstanceOf(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt
index 592e80b..ef588f5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt
@@ -51,6 +51,7 @@
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
+        whenever(component.canShowWhileLockedSetting).thenReturn(MutableStateFlow(true))
 
         underTest =
             HomeControlsKeyguardQuickAffordanceConfig(
@@ -60,7 +61,26 @@
     }
 
     @Test
-    fun `state - when listing controller is missing - returns None`() = runBlockingTest {
+    fun `state - when cannot show while locked - returns Hidden`() = runBlockingTest {
+        whenever(component.canShowWhileLockedSetting).thenReturn(MutableStateFlow(false))
+        whenever(component.isEnabled()).thenReturn(true)
+        whenever(component.getTileImageId()).thenReturn(R.drawable.controls_icon)
+        whenever(component.getTileTitleId()).thenReturn(R.string.quick_controls_title)
+        val controlsController = mock<ControlsController>()
+        whenever(component.getControlsController()).thenReturn(Optional.of(controlsController))
+        whenever(component.getControlsListingController()).thenReturn(Optional.empty())
+        whenever(controlsController.getFavorites()).thenReturn(listOf(mock()))
+
+        val values = mutableListOf<KeyguardQuickAffordanceConfig.State>()
+        val job = underTest.state.onEach(values::add).launchIn(this)
+
+        assertThat(values.last())
+            .isInstanceOf(KeyguardQuickAffordanceConfig.State.Hidden::class.java)
+        job.cancel()
+    }
+
+    @Test
+    fun `state - when listing controller is missing - returns Hidden`() = runBlockingTest {
         whenever(component.isEnabled()).thenReturn(true)
         whenever(component.getTileImageId()).thenReturn(R.drawable.controls_icon)
         whenever(component.getTileTitleId()).thenReturn(R.string.quick_controls_title)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/log/LogBufferTest.kt b/packages/SystemUI/tests/src/com/android/systemui/log/LogBufferTest.kt
index 4abb973..56aff3c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/log/LogBufferTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/log/LogBufferTest.kt
@@ -24,16 +24,11 @@
     @Before
     fun setup() {
         outputWriter = StringWriter()
-        buffer = createBuffer(UNBOUNDED_STACK_TRACE, NESTED_TRACE_DEPTH)
+        buffer = createBuffer()
     }
 
-    private fun createBuffer(rootTraceDepth: Int, nestedTraceDepth: Int): LogBuffer {
-        return LogBuffer("TestBuffer",
-                1,
-                logcatEchoTracker,
-                false,
-                rootStackTraceDepth = rootTraceDepth,
-                nestedStackTraceDepth = nestedTraceDepth)
+    private fun createBuffer(): LogBuffer {
+        return LogBuffer("TestBuffer", 1, logcatEchoTracker, false)
     }
 
     @Test
@@ -56,95 +51,83 @@
     }
 
     @Test
-    fun dump_writesExceptionAndStacktraceLimitedToGivenDepth() {
-        buffer = createBuffer(rootTraceDepth = 2, nestedTraceDepth = -1)
-        // stack trace depth of 5
-        val exception = createTestException("Exception message", "TestClass", 5)
+    fun dump_writesExceptionAndStacktrace() {
+        buffer = createBuffer()
+        val exception = createTestException("Exception message", "TestClass")
         buffer.log("Tag", LogLevel.ERROR, { str1 = "Extra message" }, { str1!! }, exception)
 
         val dumpedString = dumpBuffer()
 
-        // logs are limited to depth 2
-        assertThat(dumpedString).contains("E Tag: Extra message")
-        assertThat(dumpedString).contains("E Tag: java.lang.RuntimeException: Exception message")
-        assertThat(dumpedString).contains("E Tag: \tat TestClass.TestMethod(TestClass.java:1)")
-        assertThat(dumpedString).contains("E Tag: \tat TestClass.TestMethod(TestClass.java:2)")
-        assertThat(dumpedString)
-                .doesNotContain("E Tag: \tat TestClass.TestMethod(TestClass.java:3)")
+        assertThat(dumpedString).contains("Extra message")
+        assertThat(dumpedString).contains("java.lang.RuntimeException: Exception message")
+        assertThat(dumpedString).contains("at TestClass.TestMethod(TestClass.java:1)")
+        assertThat(dumpedString).contains("at TestClass.TestMethod(TestClass.java:2)")
     }
 
     @Test
-    fun dump_writesCauseAndStacktraceLimitedToGivenDepth() {
-        buffer = createBuffer(rootTraceDepth = 0, nestedTraceDepth = 2)
+    fun dump_writesCauseAndStacktrace() {
+        buffer = createBuffer()
         val exception = createTestException("Exception message",
                 "TestClass",
-                1,
-                cause = createTestException("The real cause!", "TestClass", 5))
+                cause = createTestException("The real cause!", "TestClass"))
         buffer.log("Tag", LogLevel.ERROR, { str1 = "Extra message" }, { str1!! }, exception)
 
         val dumpedString = dumpBuffer()
 
-        // logs are limited to depth 2
         assertThat(dumpedString)
-                .contains("E Tag: Caused by: java.lang.RuntimeException: The real cause!")
-        assertThat(dumpedString).contains("E Tag: \tat TestClass.TestMethod(TestClass.java:1)")
-        assertThat(dumpedString).contains("E Tag: \tat TestClass.TestMethod(TestClass.java:2)")
-        assertThat(dumpedString)
-                .doesNotContain("E Tag: \tat TestClass.TestMethod(TestClass.java:3)")
+                .contains("Caused by: java.lang.RuntimeException: The real cause!")
+        assertThat(dumpedString).contains("at TestClass.TestMethod(TestClass.java:1)")
+        assertThat(dumpedString).contains("at TestClass.TestMethod(TestClass.java:2)")
     }
 
     @Test
-    fun dump_writesSuppressedExceptionAndStacktraceLimitedToGivenDepth() {
-        buffer = createBuffer(rootTraceDepth = 0, nestedTraceDepth = 2)
+    fun dump_writesSuppressedExceptionAndStacktrace() {
+        buffer = createBuffer()
         val exception = RuntimeException("Root exception message")
         exception.addSuppressed(
                 createTestException(
                         "First suppressed exception",
                         "FirstClass",
-                        5,
-                        createTestException("Cause of suppressed exp", "ThirdClass", 5)
+                        createTestException("Cause of suppressed exp", "ThirdClass")
                 ))
         exception.addSuppressed(
-                createTestException("Second suppressed exception", "SecondClass", 5))
+                createTestException("Second suppressed exception", "SecondClass"))
         buffer.log("Tag", LogLevel.ERROR, { str1 = "Extra message" }, { str1!! }, exception)
 
         val dumpedStr = dumpBuffer()
 
-        // logs are limited to depth 2
         // first suppressed exception
         assertThat(dumpedStr)
-                .contains("E Tag: Suppressed: " +
+                .contains("Suppressed: " +
                         "java.lang.RuntimeException: First suppressed exception")
-        assertThat(dumpedStr).contains("E Tag: \tat FirstClass.TestMethod(FirstClass.java:1)")
-        assertThat(dumpedStr).contains("E Tag: \tat FirstClass.TestMethod(FirstClass.java:2)")
-        assertThat(dumpedStr)
-                .doesNotContain("E Tag: \tat FirstClass.TestMethod(FirstClass.java:3)")
+        assertThat(dumpedStr).contains("at FirstClass.TestMethod(FirstClass.java:1)")
+        assertThat(dumpedStr).contains("at FirstClass.TestMethod(FirstClass.java:2)")
 
         assertThat(dumpedStr)
-                .contains("E Tag: Caused by: java.lang.RuntimeException: Cause of suppressed exp")
-        assertThat(dumpedStr).contains("E Tag: \tat ThirdClass.TestMethod(ThirdClass.java:1)")
-        assertThat(dumpedStr).contains("E Tag: \tat ThirdClass.TestMethod(ThirdClass.java:2)")
-        assertThat(dumpedStr)
-                .doesNotContain("E Tag: \tat ThirdClass.TestMethod(ThirdClass.java:3)")
+                .contains("Caused by: java.lang.RuntimeException: Cause of suppressed exp")
+        assertThat(dumpedStr).contains("at ThirdClass.TestMethod(ThirdClass.java:1)")
+        assertThat(dumpedStr).contains("at ThirdClass.TestMethod(ThirdClass.java:2)")
 
         // second suppressed exception
         assertThat(dumpedStr)
-                .contains("E Tag: Suppressed: " +
+                .contains("Suppressed: " +
                         "java.lang.RuntimeException: Second suppressed exception")
-        assertThat(dumpedStr).contains("E Tag: \tat SecondClass.TestMethod(SecondClass.java:1)")
-        assertThat(dumpedStr).contains("E Tag: \tat SecondClass.TestMethod(SecondClass.java:2)")
-        assertThat(dumpedStr)
-                .doesNotContain("E Tag: \tat SecondClass.TestMethod(SecondClass.java:3)")
+        assertThat(dumpedStr).contains("at SecondClass.TestMethod(SecondClass.java:1)")
+        assertThat(dumpedStr).contains("at SecondClass.TestMethod(SecondClass.java:2)")
     }
 
     private fun createTestException(
-        message: String,
-        errorClass: String,
-        stackTraceLength: Int,
-        cause: Throwable? = null
+            message: String,
+            errorClass: String,
+            cause: Throwable? = null,
     ): Exception {
         val exception = RuntimeException(message, cause)
-        exception.stackTrace = createStackTraceElements(errorClass, stackTraceLength)
+        exception.stackTrace = (1..5).map { lineNumber ->
+            StackTraceElement(errorClass,
+                    "TestMethod",
+                    "$errorClass.java",
+                    lineNumber)
+        }.toTypedArray()
         return exception
     }
 
@@ -152,16 +135,4 @@
         buffer.dump(PrintWriter(outputWriter), tailLength = 100)
         return outputWriter.toString()
     }
-
-    private fun createStackTraceElements(
-        errorClass: String,
-        stackTraceLength: Int
-    ): Array<StackTraceElement> {
-        return (1..stackTraceLength).map { lineNumber ->
-            StackTraceElement(errorClass,
-                    "TestMethod",
-                    "$errorClass.java",
-                    lineNumber)
-        }.toTypedArray()
-    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt
index 6a532d7..6468fe1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt
@@ -17,9 +17,9 @@
 package com.android.systemui.media
 
 import android.app.smartspace.SmartspaceAction
-import androidx.test.filters.SmallTest
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
+import androidx.test.filters.SmallTest
 import com.android.internal.logging.InstanceId
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.broadcast.BroadcastDispatcher
@@ -29,18 +29,18 @@
 import com.android.systemui.util.mockito.eq
 import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
+import java.util.concurrent.Executor
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.ArgumentMatchers.anyBoolean
 import org.mockito.ArgumentMatchers.anyInt
 import org.mockito.Mock
-import org.mockito.Mockito.`when`
 import org.mockito.Mockito.never
 import org.mockito.Mockito.reset
 import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
 import org.mockito.MockitoAnnotations
-import java.util.concurrent.Executor
 
 private const val KEY = "TEST_KEY"
 private const val KEY_ALT = "TEST_KEY_2"
@@ -433,7 +433,7 @@
         val dataCurrentAndActive = dataCurrent.copy(active = true)
         verify(listener).onMediaDataLoaded(eq(KEY), eq(KEY), eq(dataCurrentAndActive), eq(true),
                 eq(100), eq(true))
-        assertThat(mediaDataFilter.hasActiveMediaOrRecommendation()).isFalse()
+        assertThat(mediaDataFilter.hasActiveMediaOrRecommendation()).isTrue()
         // Smartspace update shouldn't be propagated for the empty rec list.
         verify(listener, never()).onSmartspaceMediaDataLoaded(any(), any(), anyBoolean())
         verify(logger, never()).logRecommendationAdded(any(), any())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaResumeListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaResumeListenerTest.kt
index 3d3ac83..83168cb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaResumeListenerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaResumeListenerTest.kt
@@ -305,7 +305,7 @@
         // Then we save an update with the current time
         verify(sharedPrefsEditor).putString(any(), (capture(componentCaptor)))
         componentCaptor.value.split(ResumeMediaBrowser.DELIMITER.toRegex())
-                ?.dropLastWhile { it.isEmpty() }.forEach {
+                .dropLastWhile { it.isEmpty() }.forEach {
             val result = it.split("/")
             assertThat(result.size).isEqualTo(3)
             assertThat(result[2].toLong()).isEqualTo(currentTime)
@@ -392,7 +392,7 @@
         // Then we store the new lastPlayed time
         verify(sharedPrefsEditor).putString(any(), (capture(componentCaptor)))
         componentCaptor.value.split(ResumeMediaBrowser.DELIMITER.toRegex())
-                ?.dropLastWhile { it.isEmpty() }.forEach {
+                .dropLastWhile { it.isEmpty() }.forEach {
                     val result = it.split("/")
                     assertThat(result.size).isEqualTo(3)
                     assertThat(result[2].toLong()).isEqualTo(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 568e0cb..260bb87 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
@@ -254,4 +254,30 @@
 
         verify(mMediaOutputController).connectDevice(mMediaDevice2);
     }
+
+    @Test
+    public void onItemClick_onGroupActionTriggered_verifySeekbarDisabled() {
+        when(mMediaOutputController.getSelectedMediaDevice()).thenReturn(mMediaDevices);
+        List<MediaDevice> selectableDevices = new ArrayList<>();
+        selectableDevices.add(mMediaDevice1);
+        when(mMediaOutputController.getSelectableMediaDevice()).thenReturn(selectableDevices);
+        when(mMediaOutputController.hasAdjustVolumeUserRestriction()).thenReturn(true);
+        mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0);
+
+        mViewHolder.mContainerLayout.performClick();
+
+        assertThat(mViewHolder.mSeekBar.isEnabled()).isFalse();
+    }
+
+    @Test
+    public void onBindViewHolder_volumeControlChangeToEnabled_enableSeekbarAgain() {
+        when(mMediaOutputController.isVolumeControlEnabled(mMediaDevice1)).thenReturn(false);
+        mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0);
+        assertThat(mViewHolder.mSeekBar.isEnabled()).isFalse();
+
+        when(mMediaOutputController.isVolumeControlEnabled(mMediaDevice1)).thenReturn(true);
+        mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0);
+
+        assertThat(mViewHolder.mSeekBar.isEnabled()).isTrue();
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java
index edcf479..8073103 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java
@@ -39,6 +39,7 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.keyguard.KeyguardViewController;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.accessibility.AccessibilityButtonModeObserver;
 import com.android.systemui.accessibility.AccessibilityButtonTargetsObserver;
@@ -112,7 +113,7 @@
         mNavBarHelper = new NavBarHelper(mContext, mAccessibilityManager,
                 mAccessibilityButtonModeObserver, mAccessibilityButtonTargetObserver,
                 mSystemActions, mOverviewProxyService, mAssistManagerLazy,
-                () -> Optional.of(mock(CentralSurfaces.class)),
+                () -> Optional.of(mock(CentralSurfaces.class)), mock(KeyguardViewController.class),
                 mNavigationModeController, mUserTracker, mDumpManager);
 
     }
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 fe5f433..51f0953 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
@@ -72,6 +72,7 @@
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.UiEventLogger;
+import com.android.keyguard.KeyguardViewController;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.SysuiTestableContext;
 import com.android.systemui.accessibility.AccessibilityButtonModeObserver;
@@ -193,6 +194,8 @@
     @Mock
     private CentralSurfaces mCentralSurfaces;
     @Mock
+    private KeyguardViewController mKeyguardViewController;
+    @Mock
     private UserContextProvider mUserContextProvider;
     @Mock
     private Resources mResources;
@@ -237,8 +240,8 @@
                     mock(AccessibilityButtonTargetsObserver.class),
                     mSystemActions, mOverviewProxyService,
                     () -> mock(AssistManager.class), () -> Optional.of(mCentralSurfaces),
-                    mock(NavigationModeController.class), mock(UserTracker.class),
-                    mock(DumpManager.class)));
+                    mKeyguardViewController, mock(NavigationModeController.class),
+                    mock(UserTracker.class), mock(DumpManager.class)));
             mNavigationBar = createNavBar(mContext);
             mExternalDisplayNavigationBar = createNavBar(mSysuiTestableContextExternal);
         });
@@ -377,7 +380,7 @@
 
         // Verify navbar didn't alter and showing back icon when the keyguard is showing without
         // requesting IME insets visible.
-        doReturn(true).when(mCentralSurfaces).isKeyguardShowing();
+        doReturn(true).when(mKeyguardViewController).isShowing();
         mNavigationBar.setImeWindowStatus(DEFAULT_DISPLAY, null, IME_VISIBLE,
                 BACK_DISPOSITION_DEFAULT, true);
         assertFalse((mNavigationBar.getNavigationIconHints() & NAVIGATION_HINT_BACK_ALT) != 0);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt
index 07c8af9..be14cc5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt
@@ -26,7 +26,6 @@
 import com.android.systemui.colorextraction.SysuiColorExtractor
 import com.android.systemui.demomode.DemoModeController
 import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
 import com.android.systemui.qs.carrier.QSCarrierGroup
 import com.android.systemui.qs.carrier.QSCarrierGroupController
 import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider
@@ -46,10 +45,10 @@
 import org.mockito.Answers
 import org.mockito.ArgumentMatchers.anyInt
 import org.mockito.Mock
-import org.mockito.Mockito.`when`
 import org.mockito.Mockito.anyBoolean
 import org.mockito.Mockito.reset
 import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
 import org.mockito.MockitoAnnotations
 
 @SmallTest
@@ -162,7 +161,6 @@
 
     @Test
     fun testRSSISlot_notCombined() {
-        `when`(featureFlags.isEnabled(Flags.COMBINED_STATUS_BAR_SIGNAL_ICONS)).thenReturn(false)
         controller.init()
 
         val captor = argumentCaptor<List<String>>()
@@ -174,20 +172,6 @@
     }
 
     @Test
-    fun testRSSISlot_combined() {
-        `when`(featureFlags.isEnabled(Flags.COMBINED_STATUS_BAR_SIGNAL_ICONS)).thenReturn(true)
-        controller.init()
-
-        val captor = argumentCaptor<List<String>>()
-        verify(view).onAttach(any(), any(), capture(captor), any(), anyBoolean())
-
-        assertThat(captor.value).containsExactly(
-            mContext.getString(com.android.internal.R.string.status_bar_no_calling),
-            mContext.getString(com.android.internal.R.string.status_bar_call_strength)
-        )
-    }
-
-    @Test
     fun testSingleCarrierCallback() {
         controller.init()
         reset(view)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java
index 09ce37b..1e7722a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java
@@ -44,7 +44,6 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.keyguard.CarrierTextManager;
-import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.statusbar.connectivity.IconState;
 import com.android.systemui.statusbar.connectivity.MobileDataIndicators;
@@ -88,7 +87,6 @@
     @Mock
     private QSCarrier mQSCarrier3;
     private TestableLooper mTestableLooper;
-    @Mock private FeatureFlags mFeatureFlags;
     @Mock
     private QSCarrierGroupController.OnSingleCarrierChangedListener mOnSingleCarrierChangedListener;
 
@@ -130,7 +128,7 @@
         mQSCarrierGroupController = new QSCarrierGroupController.Builder(
                 mActivityStarter, handler, TestableLooper.get(this).getLooper(),
                 mNetworkController, mCarrierTextControllerBuilder, mContext, mCarrierConfigTracker,
-                mFeatureFlags, mSlotIndexResolver)
+                mSlotIndexResolver)
                 .setQSCarrierGroup(mQSCarrierGroup)
                 .build();
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierTest.java
index 5212255..99a17a6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierTest.java
@@ -22,13 +22,11 @@
 
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
-import android.util.FeatureFlagUtils;
 import android.view.LayoutInflater;
 import android.view.View;
 
 import androidx.test.filters.SmallTest;
 
-import com.android.settingslib.graph.SignalDrawable;
 import com.android.settingslib.mobile.TelephonyIcons;
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
@@ -59,14 +57,14 @@
 
     @Test
     public void testUpdateState_first() {
-        CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false, false);
+        CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false);
 
         assertTrue(mQSCarrier.updateState(c, false));
     }
 
     @Test
     public void testUpdateState_same() {
-        CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false, false);
+        CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false);
 
         assertTrue(mQSCarrier.updateState(c, false));
         assertFalse(mQSCarrier.updateState(c, false));
@@ -74,7 +72,7 @@
 
     @Test
     public void testUpdateState_changed() {
-        CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false, false);
+        CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false);
 
         assertTrue(mQSCarrier.updateState(c, false));
 
@@ -85,14 +83,14 @@
 
     @Test
     public void testUpdateState_singleCarrier_first() {
-        CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false, false);
+        CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false);
 
         assertTrue(mQSCarrier.updateState(c, true));
     }
 
     @Test
     public void testUpdateState_singleCarrier_noShowIcon() {
-        CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false, false);
+        CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false);
 
         mQSCarrier.updateState(c, true);
 
@@ -101,7 +99,7 @@
 
     @Test
     public void testUpdateState_multiCarrier_showIcon() {
-        CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false, false);
+        CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false);
 
         mQSCarrier.updateState(c, false);
 
@@ -110,7 +108,7 @@
 
     @Test
     public void testUpdateState_changeSingleMultiSingle() {
-        CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false, false);
+        CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false);
 
         mQSCarrier.updateState(c, true);
         assertEquals(View.GONE, mQSCarrier.getRSSIView().getVisibility());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
index 5336ef0..ba49f3f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
@@ -37,6 +37,7 @@
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Matchers.argThat;
+import static org.mockito.Mockito.inOrder;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -78,6 +79,7 @@
 import org.mockito.ArgumentCaptor;
 import org.mockito.ArgumentMatcher;
 import org.mockito.Captor;
+import org.mockito.InOrder;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
@@ -144,7 +146,25 @@
         when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE);
 
         mTile.click(null /* view */);
-        verify(mQsLogger).logTileClick(SPEC, StatusBarState.SHADE, Tile.STATE_ACTIVE);
+        verify(mQsLogger).logTileClick(eq(SPEC), eq(StatusBarState.SHADE), eq(Tile.STATE_ACTIVE),
+                anyInt());
+    }
+
+    @Test
+    public void testHandleClick_log() {
+        mTile.click(null);
+        mTile.click(null);
+        mTestableLooper.processAllMessages();
+        mTile.click(null);
+        mTestableLooper.processAllMessages();
+
+        InOrder inOrder = inOrder(mQsLogger);
+        inOrder.verify(mQsLogger).logTileClick(eq(SPEC), anyInt(), anyInt(), eq(0));
+        inOrder.verify(mQsLogger).logTileClick(eq(SPEC), anyInt(), anyInt(), eq(1));
+        inOrder.verify(mQsLogger).logHandleClick(SPEC, 0);
+        inOrder.verify(mQsLogger).logHandleClick(SPEC, 1);
+        inOrder.verify(mQsLogger).logTileClick(eq(SPEC), anyInt(), anyInt(), eq(2));
+        inOrder.verify(mQsLogger).logHandleClick(SPEC, 2);
     }
 
     @Test
@@ -183,7 +203,25 @@
         when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE);
 
         mTile.secondaryClick(null /* view */);
-        verify(mQsLogger).logTileSecondaryClick(SPEC, StatusBarState.SHADE, Tile.STATE_ACTIVE);
+        verify(mQsLogger).logTileSecondaryClick(eq(SPEC), eq(StatusBarState.SHADE),
+                eq(Tile.STATE_ACTIVE), anyInt());
+    }
+
+    @Test
+    public void testHandleSecondaryClick_log() {
+        mTile.secondaryClick(null);
+        mTile.secondaryClick(null);
+        mTestableLooper.processAllMessages();
+        mTile.secondaryClick(null);
+        mTestableLooper.processAllMessages();
+
+        InOrder inOrder = inOrder(mQsLogger);
+        inOrder.verify(mQsLogger).logTileSecondaryClick(eq(SPEC), anyInt(), anyInt(), eq(0));
+        inOrder.verify(mQsLogger).logTileSecondaryClick(eq(SPEC), anyInt(), anyInt(), eq(1));
+        inOrder.verify(mQsLogger).logHandleSecondaryClick(SPEC, 0);
+        inOrder.verify(mQsLogger).logHandleSecondaryClick(SPEC, 1);
+        inOrder.verify(mQsLogger).logTileSecondaryClick(eq(SPEC), anyInt(), anyInt(), eq(2));
+        inOrder.verify(mQsLogger).logHandleSecondaryClick(SPEC, 2);
     }
 
     @Test
@@ -210,7 +248,25 @@
         when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE);
 
         mTile.longClick(null /* view */);
-        verify(mQsLogger).logTileLongClick(SPEC, StatusBarState.SHADE, Tile.STATE_ACTIVE);
+        verify(mQsLogger).logTileLongClick(eq(SPEC), eq(StatusBarState.SHADE),
+                eq(Tile.STATE_ACTIVE), anyInt());
+    }
+
+    @Test
+    public void testHandleLongClick_log() {
+        mTile.longClick(null);
+        mTile.longClick(null);
+        mTestableLooper.processAllMessages();
+        mTile.longClick(null);
+        mTestableLooper.processAllMessages();
+
+        InOrder inOrder = inOrder(mQsLogger);
+        inOrder.verify(mQsLogger).logTileLongClick(eq(SPEC), anyInt(), anyInt(), eq(0));
+        inOrder.verify(mQsLogger).logTileLongClick(eq(SPEC), anyInt(), anyInt(), eq(1));
+        inOrder.verify(mQsLogger).logHandleLongClick(SPEC, 0);
+        inOrder.verify(mQsLogger).logHandleLongClick(SPEC, 1);
+        inOrder.verify(mQsLogger).logTileLongClick(eq(SPEC), anyInt(), anyInt(), eq(2));
+        inOrder.verify(mQsLogger).logHandleLongClick(SPEC, 2);
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ImageCaptureImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ImageCaptureImplTest.kt
new file mode 100644
index 0000000..ce3f20d
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ImageCaptureImplTest.kt
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.screenshot
+
+import android.app.IActivityTaskManager
+import android.graphics.Rect
+import android.hardware.display.DisplayManager
+import android.os.Binder
+import android.os.IBinder
+import android.testing.AndroidTestingRunner
+import android.view.Display
+import android.view.SurfaceControl.ScreenshotHardwareBuffer
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.mockito.mock
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Test the logic within ImageCaptureImpl
+ */
+@RunWith(AndroidTestingRunner::class)
+class ImageCaptureImplTest : SysuiTestCase() {
+    private val displayManager = mock<DisplayManager>()
+    private val atmService = mock<IActivityTaskManager>()
+    private val capture = TestableImageCaptureImpl(displayManager, atmService)
+
+    @Test
+    fun captureDisplayWithCrop() {
+        capture.captureDisplay(Display.DEFAULT_DISPLAY, Rect(1, 2, 3, 4))
+        assertThat(capture.token).isNotNull()
+        assertThat(capture.width!!).isEqualTo(2)
+        assertThat(capture.height!!).isEqualTo(2)
+        assertThat(capture.crop!!).isEqualTo(Rect(1, 2, 3, 4))
+    }
+
+    @Test
+    fun captureDisplayWithNullCrop() {
+        capture.captureDisplay(Display.DEFAULT_DISPLAY, null)
+        assertThat(capture.token).isNotNull()
+        assertThat(capture.width!!).isEqualTo(0)
+        assertThat(capture.height!!).isEqualTo(0)
+        assertThat(capture.crop!!).isEqualTo(Rect())
+    }
+
+    class TestableImageCaptureImpl(
+        displayManager: DisplayManager,
+        atmService: IActivityTaskManager
+    ) :
+        ImageCaptureImpl(displayManager, atmService) {
+
+        var token: IBinder? = null
+        var width: Int? = null
+        var height: Int? = null
+        var crop: Rect? = null
+
+        override fun physicalDisplayToken(displayId: Int): IBinder {
+            return Binder()
+        }
+
+        override fun captureDisplay(displayToken: IBinder, width: Int, height: Int, crop: Rect):
+                ScreenshotHardwareBuffer {
+            this.token = displayToken
+            this.width = width
+            this.height = height
+            this.crop = crop
+            return ScreenshotHardwareBuffer(null, null, false, false)
+        }
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/RequestProcessorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/RequestProcessorTest.kt
new file mode 100644
index 0000000..002f23a
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/RequestProcessorTest.kt
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.screenshot
+
+import android.content.ComponentName
+import android.graphics.Bitmap
+import android.graphics.ColorSpace
+import android.graphics.Insets
+import android.graphics.Rect
+import android.hardware.HardwareBuffer
+import android.net.Uri
+import android.view.WindowManager
+import android.view.WindowManager.ScreenshotSource
+import com.android.internal.util.ScreenshotHelper.HardwareBitmapBundler
+import com.android.internal.util.ScreenshotHelper.ScreenshotRequest
+import com.android.systemui.screenshot.TakeScreenshotService.RequestCallback
+import com.android.systemui.util.mockito.argumentCaptor
+import com.android.systemui.util.mockito.mock
+import com.google.common.truth.Truth.assertThat
+import java.util.function.Consumer
+import org.junit.Test
+import org.mockito.Mockito.eq
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.isNull
+
+class RequestProcessorTest {
+    private val controller = mock<ScreenshotController>()
+    private val bitmapCaptor = argumentCaptor<Bitmap>()
+
+    @Test
+    fun testFullScreenshot() {
+        val request = ScreenshotRequest(ScreenshotSource.SCREENSHOT_KEY_CHORD)
+        val onSavedListener = mock<Consumer<Uri>>()
+        val callback = mock<RequestCallback>()
+        val processor = RequestProcessor(controller)
+
+        processor.processRequest(WindowManager.TAKE_SCREENSHOT_FULLSCREEN, onSavedListener,
+            request, callback)
+
+        verify(controller).takeScreenshotFullscreen(/* topComponent */ isNull(),
+            eq(onSavedListener), eq(callback))
+    }
+
+    @Test
+    fun testSelectedRegionScreenshot() {
+        val request = ScreenshotRequest(ScreenshotSource.SCREENSHOT_KEY_CHORD)
+        val onSavedListener = mock<Consumer<Uri>>()
+        val callback = mock<RequestCallback>()
+        val processor = RequestProcessor(controller)
+
+        processor.processRequest(WindowManager.TAKE_SCREENSHOT_SELECTED_REGION, onSavedListener,
+            request, callback)
+
+        verify(controller).takeScreenshotPartial(/* topComponent */ isNull(),
+            eq(onSavedListener), eq(callback))
+    }
+
+    @Test
+    fun testProvidedImageScreenshot() {
+        val taskId = 1111
+        val userId = 2222
+        val bounds = Rect(50, 50, 150, 150)
+        val topComponent = ComponentName("test", "test")
+        val processor = RequestProcessor(controller)
+
+        val buffer = HardwareBuffer.create(100, 100, HardwareBuffer.RGBA_8888, 1,
+            HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE)
+        val bitmap = Bitmap.wrapHardwareBuffer(buffer, ColorSpace.get(ColorSpace.Named.SRGB))!!
+        val bitmapBundle = HardwareBitmapBundler.hardwareBitmapToBundle(bitmap)
+
+        val request = ScreenshotRequest(ScreenshotSource.SCREENSHOT_OTHER, bitmapBundle,
+            bounds, Insets.NONE, taskId, userId, topComponent)
+
+        val onSavedListener = mock<Consumer<Uri>>()
+        val callback = mock<RequestCallback>()
+
+        processor.processRequest(WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE, onSavedListener,
+            request, callback)
+
+        verify(controller).handleImageAsScreenshot(
+            bitmapCaptor.capture(), eq(bounds), eq(Insets.NONE), eq(taskId), eq(userId),
+            eq(topComponent), eq(onSavedListener), eq(callback)
+        )
+
+        assertThat(bitmapCaptor.value.equalsHardwareBitmap(bitmap)).isTrue()
+    }
+
+    private fun Bitmap.equalsHardwareBitmap(bitmap: Bitmap): Boolean {
+        return bitmap.hardwareBuffer == this.hardwareBuffer &&
+                bitmap.colorSpace == this.colorSpace
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
index 3efdf2e..fc28349 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
@@ -47,6 +47,7 @@
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.database.ContentObserver;
+import android.graphics.PointF;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.PowerManager;
@@ -461,6 +462,7 @@
 
         NotificationWakeUpCoordinator coordinator =
                 new NotificationWakeUpCoordinator(
+                        mDumpManager,
                         mock(HeadsUpManagerPhone.class),
                         new StatusBarStateControllerImpl(new UiEventLoggerFake(), mDumpManager,
                                 mInteractionJankMonitor),
@@ -902,6 +904,76 @@
     }
 
     @Test
+    public void keyguardStatusView_splitShade_dozing_alwaysDozingOn_isCentered() {
+        when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2);
+        mStatusBarStateController.setState(KEYGUARD);
+        enableSplitShade(/* enabled= */ true);
+
+        setDozing(/* dozing= */ true, /* dozingAlwaysOn= */ true);
+
+        assertThat(isKeyguardStatusViewCentered()).isTrue();
+    }
+
+    @Test
+    public void keyguardStatusView_splitShade_dozing_alwaysDozingOff_isNotCentered() {
+        when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2);
+        mStatusBarStateController.setState(KEYGUARD);
+        enableSplitShade(/* enabled= */ true);
+
+        setDozing(/* dozing= */ true, /* dozingAlwaysOn= */ false);
+
+        assertThat(isKeyguardStatusViewCentered()).isFalse();
+    }
+
+    @Test
+    public void keyguardStatusView_splitShade_notDozing_alwaysDozingOn_isNotCentered() {
+        when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2);
+        mStatusBarStateController.setState(KEYGUARD);
+        enableSplitShade(/* enabled= */ true);
+
+        setDozing(/* dozing= */ false, /* dozingAlwaysOn= */ true);
+
+        assertThat(isKeyguardStatusViewCentered()).isFalse();
+    }
+
+    @Test
+    public void keyguardStatusView_splitShade_pulsing_isCentered() {
+        when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2);
+        when(mNotificationListContainer.hasPulsingNotifications()).thenReturn(true);
+        mStatusBarStateController.setState(KEYGUARD);
+        enableSplitShade(/* enabled= */ true);
+
+        setDozing(/* dozing= */ false, /* dozingAlwaysOn= */ true);
+
+        assertThat(isKeyguardStatusViewCentered()).isFalse();
+    }
+
+    @Test
+    public void keyguardStatusView_splitShade_notPulsing_isNotCentered() {
+        when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2);
+        when(mNotificationListContainer.hasPulsingNotifications()).thenReturn(false);
+        mStatusBarStateController.setState(KEYGUARD);
+        enableSplitShade(/* enabled= */ true);
+
+        setDozing(/* dozing= */ false, /* dozingAlwaysOn= */ true);
+
+        assertThat(isKeyguardStatusViewCentered()).isFalse();
+    }
+
+    @Test
+    public void keyguardStatusView_singleShade_isCentered() {
+        enableSplitShade(/* enabled= */ false);
+        // The conditions below would make the clock NOT be centered on split shade.
+        // On single shade it should always be centered though.
+        when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2);
+        when(mNotificationListContainer.hasPulsingNotifications()).thenReturn(false);
+        mStatusBarStateController.setState(KEYGUARD);
+        setDozing(/* dozing= */ false, /* dozingAlwaysOn= */ false);
+
+        assertThat(isKeyguardStatusViewCentered()).isFalse();
+    }
+
+    @Test
     public void testDisableUserSwitcherAfterEnabling_returnsViewStubToTheViewHierarchy() {
         givenViewAttached();
         when(mResources.getBoolean(
@@ -1473,4 +1545,19 @@
                 .thenReturn(splitShadeFullTransitionDistance);
         mNotificationPanelViewController.updateResources();
     }
+
+    private void setDozing(boolean dozing, boolean dozingAlwaysOn) {
+        when(mDozeParameters.getAlwaysOn()).thenReturn(dozingAlwaysOn);
+        mNotificationPanelViewController.setDozing(
+                /* dozing= */ dozing,
+                /* animate= */ false,
+                /* wakeUpTouchLocation= */ new PointF()
+        );
+    }
+
+    private boolean isKeyguardStatusViewCentered() {
+        mNotificationPanelViewController.updateResources();
+        return getConstraintSetLayout(R.id.keyguard_status_view).endToEnd
+                == ConstraintSet.PARENT_ID;
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/smartspace/LockscreenTargetFilterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/smartspace/LockscreenAndDreamTargetFilterTest.kt
similarity index 93%
rename from packages/SystemUI/tests/src/com/android/systemui/smartspace/LockscreenTargetFilterTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/smartspace/LockscreenAndDreamTargetFilterTest.kt
index 185a838..6b34ca7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/smartspace/LockscreenTargetFilterTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/smartspace/LockscreenAndDreamTargetFilterTest.kt
@@ -27,13 +27,14 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.settings.UserTracker
-import com.android.systemui.smartspace.filters.LockscreenTargetFilter
+import com.android.systemui.smartspace.filters.LockscreenAndDreamTargetFilter
 import com.android.systemui.util.concurrency.Execution
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.eq
 import com.android.systemui.util.mockito.withArgCaptor
 import com.android.systemui.util.settings.SecureSettings
 import com.google.common.truth.Truth.assertThat
+import java.util.concurrent.Executor
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -41,18 +42,17 @@
 import org.mockito.ArgumentMatchers.anyBoolean
 import org.mockito.ArgumentMatchers.anyInt
 import org.mockito.Mock
-import org.mockito.Mockito.`when`
 import org.mockito.Mockito.atLeast
 import org.mockito.Mockito.clearInvocations
 import org.mockito.Mockito.mock
 import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
 import org.mockito.MockitoAnnotations
-import java.util.concurrent.Executor
 
 @SmallTest
 @TestableLooper.RunWithLooper
 @RunWith(AndroidTestingRunner::class)
-class LockscreenTargetFilterTest : SysuiTestCase() {
+class LockscreenAndDreamTargetFilterTest : SysuiTestCase() {
     @Mock
     private lateinit var secureSettings: SecureSettings
 
@@ -99,7 +99,7 @@
         `when`(secureSettings
                 .getIntForUser(eq(setting) ?: setting, anyInt(), anyInt()))
                 .thenReturn(0)
-        var filter = LockscreenTargetFilter(secureSettings, userTracker, execution, handler,
+        var filter = LockscreenAndDreamTargetFilter(secureSettings, userTracker, execution, handler,
                 contentResolver, uiExecution)
 
         filter.addListener(listener)
@@ -132,7 +132,7 @@
         `when`(secureSettings
                 .getIntForUser(eq(setting) ?: setting, anyInt(), anyInt()))
                 .thenReturn(0)
-        var filter = LockscreenTargetFilter(secureSettings, userTracker, execution, handler,
+        var filter = LockscreenAndDreamTargetFilter(secureSettings, userTracker, execution, handler,
                 contentResolver, uiExecution)
 
         filter.addListener(listener)
@@ -146,4 +146,4 @@
 
         verify(listener).onCriteriaChanged()
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java
index 0d1879c..f8a0d2f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java
@@ -70,8 +70,6 @@
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.demomode.DemoModeController;
 import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
 import com.android.systemui.log.LogBuffer;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
@@ -127,8 +125,8 @@
     protected CarrierConfigTracker mCarrierConfigTracker;
     protected FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock());
     protected Handler mMainHandler;
-    protected FeatureFlags mFeatureFlags;
     protected WifiStatusTrackerFactory mWifiStatusTrackerFactory;
+    protected MobileSignalControllerFactory mMobileFactory;
 
     protected int mSubId;
 
@@ -158,9 +156,6 @@
 
     @Before
     public void setUp() throws Exception {
-        mFeatureFlags = mock(FeatureFlags.class);
-        when(mFeatureFlags.isEnabled(Flags.COMBINED_STATUS_BAR_SIGNAL_ICONS)).thenReturn(false);
-
         mInstrumentation = InstrumentationRegistry.getInstrumentation();
         Settings.Global.putInt(mContext.getContentResolver(), Global.AIRPLANE_MODE_ON, 0);
         TestableResources res = mContext.getOrCreateTestableResources();
@@ -224,6 +219,11 @@
 
         mWifiStatusTrackerFactory = new WifiStatusTrackerFactory(
                 mContext, mMockWm, mMockNsm, mMockCm, mMainHandler);
+        mMobileFactory = new MobileSignalControllerFactory(
+                mContext,
+                mCallbackHandler,
+                mCarrierConfigTracker
+        );
 
         mNetworkController = new NetworkControllerImpl(mContext,
                 mMockCm,
@@ -243,8 +243,8 @@
                 mDemoModeController,
                 mCarrierConfigTracker,
                 mWifiStatusTrackerFactory,
+                mMobileFactory,
                 mMainHandler,
-                mFeatureFlags,
                 mock(DumpManager.class),
                 mock(LogBuffer.class)
         );
@@ -438,10 +438,6 @@
         updateSignalStrength();
     }
 
-    public void setImsType(int imsType) {
-        mMobileSignalController.setImsType(imsType);
-    }
-
     public void setIsGsm(boolean gsm) {
         when(mSignalStrength.isGsm()).thenReturn(gsm);
         updateSignalStrength();
@@ -637,5 +633,4 @@
     protected void assertDataNetworkNameEquals(String expected) {
         assertEquals("Data network name", expected, mNetworkController.getMobileDataNetworkName());
     }
-
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerDataTest.java
index e3dd6f4..ed8a3e1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerDataTest.java
@@ -145,8 +145,8 @@
                 mDemoModeController,
                 mock(CarrierConfigTracker.class),
                 mWifiStatusTrackerFactory,
+                mMobileFactory,
                 new Handler(TestableLooper.get(this).getLooper()),
-                mFeatureFlags,
                 mock(DumpManager.class),
                 mock(LogBuffer.class));
         setupNetworkController();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerSignalTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerSignalTest.java
index 698899a..a76676e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerSignalTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerSignalTest.java
@@ -85,8 +85,8 @@
                 mDemoModeController,
                 mCarrierConfigTracker,
                 mWifiStatusTrackerFactory,
+                mMobileFactory,
                 mMainHandler,
-                mFeatureFlags,
                 mock(DumpManager.class),
                 mock(LogBuffer.class)
         );
@@ -121,8 +121,8 @@
                 mDemoModeController,
                 mCarrierConfigTracker,
                 mWifiStatusTrackerFactory,
+                mMobileFactory,
                 mMainHandler,
-                mFeatureFlags,
                 mock(DumpManager.class),
                 mock(LogBuffer.class));
         TestableLooper.get(this).processAllMessages();
@@ -155,8 +155,8 @@
                 mDemoModeController,
                 mock(CarrierConfigTracker.class),
                 mWifiStatusTrackerFactory,
+                mMobileFactory,
                 mMainHandler,
-                mFeatureFlags,
                 mock(DumpManager.class),
                 mock(LogBuffer.class));
         setupNetworkController();
@@ -192,8 +192,8 @@
                 mDemoModeController,
                 mock(CarrierConfigTracker.class),
                 mWifiStatusTrackerFactory,
+                mMobileFactory,
                 mMainHandler,
-                mFeatureFlags,
                 mock(DumpManager.class),
                 mock(LogBuffer.class));
         mNetworkController.registerListeners();
@@ -277,8 +277,8 @@
                 mDemoModeController,
                 mock(CarrierConfigTracker.class),
                 mWifiStatusTrackerFactory,
+                mMobileFactory,
                 mMainHandler,
-                mFeatureFlags,
                 mock(DumpManager.class),
                 mock(LogBuffer.class));
         setupNetworkController();
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 3f71491..68170ea 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
@@ -30,7 +30,6 @@
 import android.net.vcn.VcnTransportInfo;
 import android.net.wifi.WifiInfo;
 import android.net.wifi.WifiManager;
-import android.telephony.CellSignalStrength;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper.RunWithLooper;
 
@@ -285,44 +284,6 @@
         verifyLastMobileDataIndicatorsForVcn(false, 1, 0, false);
     }
 
-    @Test
-    public void testCallStrengh() {
-        if (true) return;
-        String testSsid = "Test SSID";
-        setWifiEnabled(true);
-        setWifiState(true, testSsid);
-        // Set the ImsType to be IMS_TYPE_WLAN
-        setImsType(2);
-        setWifiLevel(1);
-        for (int testLevel = 0; testLevel < WifiIcons.WIFI_LEVEL_COUNT; testLevel++) {
-            setWifiLevel(testLevel);
-            verifyLastCallStrength(TelephonyIcons.WIFI_CALL_STRENGTH_ICONS[testLevel]);
-        }
-        // Set the ImsType to be IMS_TYPE_WWAN
-        setImsType(1);
-        setupDefaultSignal();
-        for (int testStrength = 0;
-                testStrength < CellSignalStrength.getNumSignalStrengthLevels(); testStrength++) {
-            setLevel(testStrength);
-            verifyLastCallStrength(TelephonyIcons.MOBILE_CALL_STRENGTH_ICONS[testStrength]);
-        }
-    }
-
-    @Test
-    public void testNonPrimaryWiFi() {
-        if (true) return;
-        String testSsid = "Test SSID";
-        setWifiEnabled(true);
-        setWifiState(true, testSsid);
-        // Set the ImsType to be IMS_TYPE_WLAN
-        setImsType(2);
-        setWifiLevel(1);
-        verifyLastCallStrength(TelephonyIcons.WIFI_CALL_STRENGTH_ICONS[1]);
-        when(mWifiInfo.isPrimary()).thenReturn(false);
-        setWifiLevel(3);
-        verifyLastCallStrength(TelephonyIcons.WIFI_CALL_STRENGTH_ICONS[1]);
-    }
-
     protected void setWifiActivity(int activity) {
         // TODO: Not this, because this variable probably isn't sticking around.
         mNetworkController.mWifiSignalController.setActivity(activity);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
index 2a3509c..7b7f45a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
@@ -965,6 +965,7 @@
 
     @Test
     public void deviceStateChange_unfolded_shadeOpen_setsLeaveOpenOnKeyguardHide() {
+        when(mKeyguardStateController.isShowing()).thenReturn(false);
         setFoldedStates(FOLD_STATE_FOLDED);
         setGoToSleepStates(FOLD_STATE_FOLDED);
         when(mNotificationPanelViewController.isFullyExpanded()).thenReturn(true);
@@ -975,6 +976,19 @@
     }
 
     @Test
+    public void deviceStateChange_unfolded_shadeOpen_onKeyguard_doesNotSetLeaveOpenOnKeyguardHide() {
+        when(mKeyguardStateController.isShowing()).thenReturn(true);
+        setFoldedStates(FOLD_STATE_FOLDED);
+        setGoToSleepStates(FOLD_STATE_FOLDED);
+        when(mNotificationPanelViewController.isFullyExpanded()).thenReturn(true);
+
+        setDeviceState(FOLD_STATE_UNFOLDED);
+
+        verify(mStatusBarStateController, never()).setLeaveOpenOnKeyguardHide(true);
+    }
+
+
+    @Test
     public void deviceStateChange_unfolded_shadeClose_doesNotSetLeaveOpenOnKeyguardHide() {
         setFoldedStates(FOLD_STATE_FOLDED);
         setGoToSleepStates(FOLD_STATE_FOLDED);
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 1046dbc..2dcdcfc 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
@@ -202,12 +202,12 @@
     }
 
     @Test
-    public void onPanelExpansionChanged_propagatesToBouncer_evenAfterHidden() {
+    public void onPanelExpansionChanged_hideBouncer_afterKeyguardHidden() {
         mStatusBarKeyguardViewManager.hide(0, 0);
         when(mBouncer.inTransit()).thenReturn(true);
 
         mStatusBarKeyguardViewManager.onPanelExpansionChanged(EXPANSION_EVENT);
-        verify(mBouncer).setExpansion(eq(EXPANSION_EVENT.getFraction()));
+        verify(mBouncer).setExpansion(eq(KeyguardBouncer.EXPANSION_HIDDEN));
     }
 
     @Test
@@ -249,6 +249,23 @@
     }
 
     @Test
+    public void onPanelExpansionChanged_neverTranslatesBouncerWhenDismissBouncer() {
+        // Since KeyguardBouncer.EXPANSION_VISIBLE = 0 panel expansion, if the unlock is dismissing
+        // the bouncer, there may be an onPanelExpansionChanged(0) call to collapse the panel
+        // which would mistakenly cause the bouncer to show briefly before its visibility
+        // is set to hide. Therefore, we don't want to propagate panelExpansionChanged to the
+        // bouncer if the bouncer is dismissing as a result of a biometric unlock.
+        when(mBiometricUnlockController.getMode())
+                .thenReturn(BiometricUnlockController.MODE_DISMISS_BOUNCER);
+        mStatusBarKeyguardViewManager.onPanelExpansionChanged(
+                expansionEvent(
+                        /* fraction= */ KeyguardBouncer.EXPANSION_VISIBLE,
+                        /* expanded= */ true,
+                        /* tracking= */ false));
+        verify(mBouncer, never()).setExpansion(anyFloat());
+    }
+
+    @Test
     public void onPanelExpansionChanged_neverTranslatesBouncerWhenLaunchingApp() {
         when(mCentralSurfaces.isInLaunchTransition()).thenReturn(true);
         mStatusBarKeyguardViewManager.onPanelExpansionChanged(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoProcessorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoProcessorTest.kt
new file mode 100644
index 0000000..515a7c9
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoProcessorTest.kt
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.pipeline
+
+import android.net.NetworkCapabilities
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.pipeline.repository.NetworkCapabilityInfo
+import com.android.systemui.util.mockito.mock
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.CoroutineStart
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.InternalCoroutinesApi
+import kotlinx.coroutines.cancel
+import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.yield
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.`when` as whenever
+
+@OptIn(InternalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class ConnectivityInfoProcessorTest : SysuiTestCase() {
+
+    private val statusBarPipelineFlags = mock<StatusBarPipelineFlags>()
+
+    @Before
+    fun setUp() {
+        whenever(statusBarPipelineFlags.isNewPipelineEnabled()).thenReturn(true)
+    }
+
+    @Test
+    fun collectorInfoUpdated_processedInfoAlsoUpdated() = runBlocking {
+        // GIVEN a processor hooked up to a collector
+        val scope = CoroutineScope(Dispatchers.Unconfined)
+        val collector = FakeConnectivityInfoCollector()
+        val processor = ConnectivityInfoProcessor(
+                collector,
+                context,
+                scope,
+                statusBarPipelineFlags,
+        )
+
+        var mostRecentValue: ProcessedConnectivityInfo? = null
+        val job = launch(start = CoroutineStart.UNDISPATCHED) {
+            processor.processedInfoFlow.collect {
+                mostRecentValue = it
+            }
+        }
+
+        // WHEN the collector emits a value
+        val networkCapabilityInfo = mapOf(
+                10 to NetworkCapabilityInfo(mock(), NetworkCapabilities.Builder().build())
+        )
+        collector.emitValue(RawConnectivityInfo(networkCapabilityInfo))
+        // Because our job uses [CoroutineStart.UNDISPATCHED], it executes in the same thread as
+        // this test. So, our test needs to yield to let the job run.
+        // Note: Once we upgrade our Kotlin coroutines testing library, we won't need this.
+        yield()
+
+        // THEN the processor receives it
+        assertThat(mostRecentValue?.networkCapabilityInfo).isEqualTo(networkCapabilityInfo)
+
+        job.cancel()
+        scope.cancel()
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/FakeConnectivityInfoCollector.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/FakeConnectivityInfoCollector.kt
new file mode 100644
index 0000000..710e5f6
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/FakeConnectivityInfoCollector.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.pipeline
+
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asStateFlow
+
+/**
+ * A test-friendly implementation of [ConnectivityInfoCollector] that just emits whatever value it
+ * receives in [emitValue].
+ */
+class FakeConnectivityInfoCollector : ConnectivityInfoCollector {
+    private val _rawConnectivityInfoFlow = MutableStateFlow(RawConnectivityInfo())
+    override val rawConnectivityInfoFlow = _rawConnectivityInfoFlow.asStateFlow()
+
+    suspend fun emitValue(value: RawConnectivityInfo) {
+        _rawConnectivityInfoFlow.emit(value)
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index fee17c7..18acf3f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -33,6 +33,7 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
@@ -621,7 +622,7 @@
         assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showDot());
 
         // Send update
-        mEntryListener.onEntryUpdated(mRow);
+        mEntryListener.onEntryUpdated(mRow, /* fromSystem= */ true);
 
         // Nothing should have changed
         // Notif is suppressed after expansion
@@ -789,7 +790,7 @@
     @Test
     public void testAddNotif_notBubble() {
         mEntryListener.onEntryAdded(mNonBubbleNotifRow.getEntry());
-        mEntryListener.onEntryUpdated(mNonBubbleNotifRow.getEntry());
+        mEntryListener.onEntryUpdated(mNonBubbleNotifRow.getEntry(), /* fromSystem= */ true);
 
         assertThat(mBubbleController.hasBubbles()).isFalse();
     }
@@ -827,7 +828,7 @@
         NotificationListenerService.Ranking ranking = new RankingBuilder(
                 mRow.getRanking()).setCanBubble(false).build();
         mRow.setRanking(ranking);
-        mEntryListener.onEntryUpdated(mRow);
+        mEntryListener.onEntryUpdated(mRow, /* fromSystem= */ true);
 
         assertFalse(mBubbleController.hasBubbles());
         verify(mDeleteIntent, never()).send();
@@ -1432,6 +1433,100 @@
         assertThat(mBubbleData.hasBubbleInStackWithKey(mBubbleEntry.getKey())).isFalse();
     }
 
+    /**
+     * Verifies that if a bubble is in the overflow and a non-interruptive notification update
+     * comes in for it, it stays in the overflow but the entry is updated.
+     */
+    @Test
+    public void testNonInterruptiveUpdate_doesntBubbleFromOverflow() {
+        mEntryListener.onEntryAdded(mRow);
+        mEntryListener.onEntryUpdated(mRow, /* fromSystem= */ true);
+        assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
+
+        // Dismiss the bubble so it's in the overflow
+        mBubbleController.removeBubble(
+                mRow.getKey(), Bubbles.DISMISS_USER_GESTURE);
+        assertThat(mBubbleData.hasOverflowBubbleWithKey(mRow.getKey())).isTrue();
+
+        // Update the entry to not show in shade
+        setMetadataFlags(mRow,
+                Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION, /* enableFlag= */ true);
+        mBubbleController.updateBubble(mBubbleEntry,
+                /* suppressFlyout= */ false, /* showInShade= */ true);
+
+        // Check that the update was applied - shouldn't be show in shade
+        assertBubbleNotificationSuppressedFromShade(mBubbleEntry);
+        // Check that it wasn't inflated (1 because it would've been inflated via onEntryAdded)
+        verify(mBubbleController, times(1)).inflateAndAdd(
+                any(Bubble.class), anyBoolean(), anyBoolean());
+    }
+
+    /**
+     * Verifies that if a bubble is active, and a non-interruptive notification update comes in for
+     * it, it doesn't trigger a new inflate and add for that bubble.
+     */
+    @Test
+    public void testNonInterruptiveUpdate_doesntTriggerInflate() {
+        mEntryListener.onEntryAdded(mRow);
+        mEntryListener.onEntryUpdated(mRow, /* fromSystem= */ true);
+        assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
+
+        // Update the entry to not show in shade
+        setMetadataFlags(mRow,
+                Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION, /* enableFlag= */ true);
+        mBubbleController.updateBubble(mBubbleEntry,
+                /* suppressFlyout= */ false, /* showInShade= */ true);
+
+        // Check that the update was applied - shouldn't be show in shade
+        assertBubbleNotificationSuppressedFromShade(mBubbleEntry);
+        // Check that it wasn't inflated (1 because it would've been inflated via onEntryAdded)
+        verify(mBubbleController, times(1)).inflateAndAdd(
+                any(Bubble.class), anyBoolean(), anyBoolean());
+    }
+
+    /**
+     * Verifies that if a bubble is in the overflow and a non-interruptive notification update
+     * comes in for it with FLAG_BUBBLE that the flag is removed.
+     */
+    @Test
+    public void testNonInterruptiveUpdate_doesntOverrideOverflowFlagBubble() {
+        mEntryListener.onEntryAdded(mRow);
+        mEntryListener.onEntryUpdated(mRow, /* fromSystem= */ true);
+        assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
+
+        // Dismiss the bubble so it's in the overflow
+        mBubbleController.removeBubble(
+                mRow.getKey(), Bubbles.DISMISS_USER_GESTURE);
+        assertThat(mBubbleData.hasOverflowBubbleWithKey(mRow.getKey())).isTrue();
+        // Once it's in the overflow it's not actively a bubble (doesn't have FLAG_BUBBLE)
+        Bubble b = mBubbleData.getOverflowBubbleWithKey(mBubbleEntry.getKey());
+        assertThat(b.isBubble()).isFalse();
+
+        // Send a non-notifying update that has FLAG_BUBBLE
+        mRow.getSbn().getNotification().flags = FLAG_BUBBLE;
+        assertThat(mRow.getSbn().getNotification().isBubbleNotification()).isTrue();
+        mBubbleController.updateBubble(mBubbleEntry,
+                /* suppressFlyout= */ false, /* showInShade= */ true);
+
+        // Verify that it still doesn't have FLAG_BUBBLE because it's in the overflow.
+        b = mBubbleData.getOverflowBubbleWithKey(mBubbleEntry.getKey());
+        assertThat(b.isBubble()).isFalse();
+    }
+
+    @Test
+    public void testNonSystemUpdatesIgnored() {
+        mEntryListener.onEntryAdded(mRow);
+        assertThat(mBubbleController.hasBubbles()).isTrue();
+
+        mEntryListener.onEntryUpdated(mRow, /* fromSystem= */ false);
+        mEntryListener.onEntryUpdated(mRow, /* fromSystem= */ false);
+        mEntryListener.onEntryUpdated(mRow, /* fromSystem= */ false);
+
+        // Check that it wasn't inflated (1 because it would've been inflated via onEntryAdded)
+        verify(mBubbleController, times(1)).inflateAndAdd(
+                any(Bubble.class), anyBoolean(), anyBoolean());
+    }
+
     /** Creates a bubble using the userId and package. */
     private Bubble createBubble(int userId, String pkg) {
         final UserHandle userHandle = new UserHandle(userId);
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java
index c52ea60f..c83189d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java
@@ -15,6 +15,8 @@
  */
 package com.android.systemui;
 
+import static com.android.systemui.animation.FakeDialogLaunchAnimatorKt.fakeDialogLaunchAnimator;
+
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.when;
@@ -34,6 +36,7 @@
 
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.settingslib.bluetooth.LocalBluetoothManager;
+import com.android.systemui.animation.DialogLaunchAnimator;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.broadcast.FakeBroadcastDispatcher;
 import com.android.systemui.broadcast.logging.BroadcastDispatcherLogger;
@@ -119,6 +122,7 @@
         // is missing (constructing the actual one would throw).
         // TODO(b/219008720): Remove this.
         mDependency.injectMockDependency(SystemUIDialogManager.class);
+        mDependency.injectTestDependency(DialogLaunchAnimator.class, fakeDialogLaunchAnimator());
     }
 
     @After
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/animation/FakeDialogLaunchAnimator.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/animation/FakeDialogLaunchAnimator.kt
new file mode 100644
index 0000000..990db77
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/animation/FakeDialogLaunchAnimator.kt
@@ -0,0 +1,46 @@
+/*
+ * 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.animation
+
+import com.android.internal.jank.InteractionJankMonitor
+import org.mockito.Mockito.mock
+
+/** A [DialogLaunchAnimator] to be used in tests. */
+@JvmOverloads
+fun fakeDialogLaunchAnimator(
+    isUnlocked: Boolean = true,
+    isShowingAlternateAuthOnUnlock: Boolean = false,
+    interactionJankMonitor: InteractionJankMonitor = mock(InteractionJankMonitor::class.java),
+): DialogLaunchAnimator {
+    return DialogLaunchAnimator(
+        FakeCallback(
+            isUnlocked = isUnlocked,
+            isShowingAlternateAuthOnUnlock = isShowingAlternateAuthOnUnlock,
+        ),
+        interactionJankMonitor,
+        fakeLaunchAnimator(),
+        isForTesting = true,
+    )
+}
+
+private class FakeCallback(
+    private val isDreaming: Boolean = false,
+    private val isUnlocked: Boolean = true,
+    private val isShowingAlternateAuthOnUnlock: Boolean = false,
+) : DialogLaunchAnimator.Callback {
+    override fun isDreaming(): Boolean = isDreaming
+    override fun isUnlocked(): Boolean = isUnlocked
+    override fun isShowingAlternateAuthOnUnlock() = isShowingAlternateAuthOnUnlock
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/animation/FakeLaunchAnimator.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/animation/FakeLaunchAnimator.kt
new file mode 100644
index 0000000..5b431e7
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/animation/FakeLaunchAnimator.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.animation
+
+/** A [LaunchAnimator] to be used in tests. */
+fun fakeLaunchAnimator(): LaunchAnimator {
+    return LaunchAnimator(TEST_TIMINGS, TEST_INTERPOLATORS)
+}
+
+/**
+ * A [LaunchAnimator.Timings] to be used in tests.
+ *
+ * Note that all timings except the total duration are non-zero to avoid divide-by-zero exceptions
+ * when computing the progress of a sub-animation (the contents fade in/out).
+ */
+private val TEST_TIMINGS =
+    LaunchAnimator.Timings(
+        totalDuration = 0L,
+        contentBeforeFadeOutDelay = 1L,
+        contentBeforeFadeOutDuration = 1L,
+        contentAfterFadeInDelay = 1L,
+        contentAfterFadeInDuration = 1L
+    )
+
+/** A [LaunchAnimator.Interpolators] to be used in tests. */
+private val TEST_INTERPOLATORS =
+    LaunchAnimator.Interpolators(
+        positionInterpolator = Interpolators.STANDARD,
+        positionXInterpolator = Interpolators.STANDARD,
+        contentBeforeFadeOutInterpolator = Interpolators.STANDARD,
+        contentAfterFadeInInterpolator = Interpolators.STANDARD
+    )
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/mockito/KotlinMockitoHelpers.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/util/mockito/KotlinMockitoHelpers.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/util/mockito/KotlinMockitoHelpers.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/util/mockito/KotlinMockitoHelpers.kt
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 9b11a68..a185b58 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -1115,10 +1115,7 @@
             registerForBroadcastsLocked(provider, getWidgetIds(provider.widgets));
 
             saveGroupStateAsync(userId);
-
-            if (DEBUG) {
-                Slog.i(TAG, "Bound widget " + appWidgetId + " to provider " + provider.id);
-            }
+            Slog.i(TAG, "Bound widget " + appWidgetId + " to provider " + provider.id);
         }
 
         return true;
@@ -4251,6 +4248,10 @@
         IAppWidgetHost callbacks;
         boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
 
+        private static final boolean DEBUG = true;
+
+        private static final String TAG = "AppWidgetServiceHost";
+
         int tag = TAG_UNDEFINED; // for use while saving state (the index)
         // Sequence no for the last update successfully sent. This is updated whenever a
         // widget update is successfully sent to the host callbacks. As all new/undelivered updates
@@ -4321,6 +4322,11 @@
             final SparseArray<String> uids = new SparseArray<>();
             for (int i = widgets.size() - 1; i >= 0; i--) {
                 final Widget widget = widgets.get(i);
+                if (widget.provider == null) {
+                    if (DEBUG) {
+                        Slog.e(TAG, "Widget with no provider " + widget.toString());
+                    }
+                }
                 final ProviderId providerId = widget.provider.id;
                 uids.put(providerId.uid, providerId.componentName.getPackageName());
             }
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
index 35e9060..b255188 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
@@ -56,6 +56,7 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.ConcurrentHashMap;
 
@@ -208,6 +209,10 @@
     }
 
     @VisibleForTesting
+    VirtualDeviceManagerInternal getLocalServiceInstance() {
+        return mLocalService;
+    }
+
     class VirtualDeviceManagerImpl extends IVirtualDeviceManager.Stub implements
             VirtualDeviceImpl.PendingTrampolineCallback {
 
@@ -308,10 +313,11 @@
             final long tokenTwo = Binder.clearCallingIdentity();
             try {
                 virtualDeviceImpl.onVirtualDisplayCreatedLocked(gwpc, displayId);
-                return displayId;
             } finally {
                 Binder.restoreCallingIdentity(tokenTwo);
             }
+            mLocalService.onVirtualDisplayCreated(displayId);
+            return displayId;
         }
 
         @Nullable
@@ -378,6 +384,10 @@
     }
 
     private final class LocalService extends VirtualDeviceManagerInternal {
+        @GuardedBy("mVirtualDeviceManagerLock")
+        private final ArrayList<VirtualDeviceManagerInternal.VirtualDisplayListener>
+                mVirtualDisplayListeners = new ArrayList<>();
+
         @Override
         public boolean isValidVirtualDevice(IVirtualDevice virtualDevice) {
             synchronized (mVirtualDeviceManagerLock) {
@@ -386,10 +396,30 @@
         }
 
         @Override
+        public void onVirtualDisplayCreated(int displayId) {
+            final VirtualDisplayListener[] listeners;
+            synchronized (mVirtualDeviceManagerLock) {
+                listeners = mVirtualDisplayListeners.toArray(new VirtualDisplayListener[0]);
+            }
+            mHandler.post(() -> {
+                for (VirtualDisplayListener listener : listeners) {
+                    listener.onVirtualDisplayCreated(displayId);
+                }
+            });
+        }
+
+        @Override
         public void onVirtualDisplayRemoved(IVirtualDevice virtualDevice, int displayId) {
+            final VirtualDisplayListener[] listeners;
             synchronized (mVirtualDeviceManagerLock) {
                 ((VirtualDeviceImpl) virtualDevice).onVirtualDisplayRemovedLocked(displayId);
+                listeners = mVirtualDisplayListeners.toArray(new VirtualDisplayListener[0]);
             }
+            mHandler.post(() -> {
+                for (VirtualDisplayListener listener : listeners) {
+                    listener.onVirtualDisplayRemoved(displayId);
+                }
+            });
         }
 
         @Override
@@ -435,6 +465,22 @@
             }
             return false;
         }
+
+        @Override
+        public void registerVirtualDisplayListener(
+                @NonNull VirtualDisplayListener listener) {
+            synchronized (mVirtualDeviceManagerLock) {
+                mVirtualDisplayListeners.add(listener);
+            }
+        }
+
+        @Override
+        public void unregisterVirtualDisplayListener(
+                @NonNull VirtualDisplayListener listener) {
+            synchronized (mVirtualDeviceManagerLock) {
+                mVirtualDisplayListeners.remove(listener);
+            }
+        }
     }
 
     private static final class PendingTrampolineMap {
diff --git a/services/core/java/com/android/server/VpnManagerService.java b/services/core/java/com/android/server/VpnManagerService.java
index c9a420e..32dc470 100644
--- a/services/core/java/com/android/server/VpnManagerService.java
+++ b/services/core/java/com/android/server/VpnManagerService.java
@@ -138,6 +138,12 @@
                 INetd netd, int userId) {
             return new Vpn(looper, context, nms, netd, userId, new VpnProfileStore());
         }
+
+        /** Create a LockDownVpnTracker. */
+        public LockdownVpnTracker createLockDownVpnTracker(Context context, Handler handler,
+                Vpn vpn, VpnProfile profile) {
+            return new LockdownVpnTracker(context, handler, vpn,  profile);
+        }
     }
 
     public VpnManagerService(Context context, Dependencies deps) {
@@ -502,8 +508,7 @@
                 logw("VPN for user " + user + " not ready yet. Skipping lockdown");
                 return false;
             }
-            setLockdownTracker(
-                    new LockdownVpnTracker(mContext, mHandler, vpn,  profile));
+            setLockdownTracker(mDeps.createLockDownVpnTracker(mContext, mHandler, vpn,  profile));
         }
 
         return true;
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index ef784bb..5393b2a 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -160,6 +160,7 @@
     public static final String[] AIDL_INTERFACE_PREFIXES_OF_INTEREST = new String[] {
             "android.hardware.biometrics.face.IFace/",
             "android.hardware.biometrics.fingerprint.IFingerprint/",
+            "android.hardware.graphics.composer3.IComposer/",
             "android.hardware.input.processor.IInputProcessor/",
             "android.hardware.light.ILights/",
             "android.hardware.power.IPower/",
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 002aa77..e7ea903 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -3595,11 +3595,12 @@
 
     /**
      * Bump the given service record into executing state.
-     * @param oomAdjReason The caller requests it to perform the oomAdjUpdate if it's not null.
+     * @param oomAdjReason The caller requests it to perform the oomAdjUpdate not {@link
+     *         OomAdjuster#OOM_ADJ_REASON_NONE}.
      * @return {@code true} if it performed oomAdjUpdate.
      */
-    private boolean bumpServiceExecutingLocked(ServiceRecord r, boolean fg, String why,
-            @Nullable String oomAdjReason) {
+    private boolean bumpServiceExecutingLocked(
+            ServiceRecord r, boolean fg, String why, @OomAdjuster.OomAdjReason int oomAdjReason) {
         if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, ">>> EXECUTING "
                 + why + " of " + r + " in app " + r.app);
         else if (DEBUG_SERVICE_EXECUTING) Slog.v(TAG_SERVICE_EXECUTING, ">>> EXECUTING "
@@ -3651,7 +3652,7 @@
             }
         }
         boolean oomAdjusted = false;
-        if (oomAdjReason != null && r.app != null
+        if (oomAdjReason != OomAdjuster.OOM_ADJ_REASON_NONE && r.app != null
                 && r.app.mState.getCurProcState() > ActivityManager.PROCESS_STATE_SERVICE) {
             // Force an immediate oomAdjUpdate, so the client app could be in the correct process
             // state before doing any service related transactions
@@ -4385,7 +4386,7 @@
 
         final ProcessServiceRecord psr = app.mServices;
         final boolean newService = psr.startService(r);
-        bumpServiceExecutingLocked(r, execInFg, "create", null /* oomAdjReason */);
+        bumpServiceExecutingLocked(r, execInFg, "create", OomAdjuster.OOM_ADJ_REASON_NONE);
         mAm.updateLruProcessLocked(app, false, null);
         updateServiceForegroundLocked(psr, /* oomAdj= */ false);
         // Force an immediate oomAdjUpdate, so the client app could be in the correct process state
@@ -4511,7 +4512,7 @@
             mAm.grantImplicitAccess(r.userId, si.intent, si.callingId,
                     UserHandle.getAppId(r.appInfo.uid)
             );
-            bumpServiceExecutingLocked(r, execInFg, "start", null /* oomAdjReason */);
+            bumpServiceExecutingLocked(r, execInFg, "start", OomAdjuster.OOM_ADJ_REASON_NONE);
             if (r.fgRequired && !r.fgWaiting) {
                 if (!r.isForeground) {
                     if (DEBUG_BACKGROUND_CHECK) {
@@ -4780,7 +4781,7 @@
                 updateServiceForegroundLocked(r.app.mServices, false);
                 try {
                     oomAdjusted |= bumpServiceExecutingLocked(r, false, "destroy",
-                            oomAdjusted ? null : OomAdjuster.OOM_ADJ_REASON_UNBIND_SERVICE);
+                            oomAdjusted ? 0 : OomAdjuster.OOM_ADJ_REASON_UNBIND_SERVICE);
                     mDestroyingServices.add(r);
                     r.destroying = true;
                     r.app.getThread().scheduleStopService(r);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 5729a06..7f1cf03 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -8894,9 +8894,9 @@
                     InputStreamReader input = null;
                     try {
                         java.lang.Process logcat = new ProcessBuilder(
-                                // Time out after 10s, but kill logcat with SEGV
+                                // Time out after 10s of inactivity, but kill logcat with SEGV
                                 // so we can investigate why it didn't finish.
-                                "/system/bin/timeout", "-s", "SEGV", "10s",
+                                "/system/bin/timeout", "-i", "-s", "SEGV", "10s",
                                 // Merge several logcat streams, and take the last N lines.
                                 "/system/bin/logcat", "-v", "threadtime", "-b", "events", "-b", "system",
                                 "-b", "main", "-b", "crash", "-t", String.valueOf(lines))
@@ -14692,6 +14692,18 @@
                 }
             }
 
+            if (!Build.IS_DEBUGGABLE && callingUid != ROOT_UID && callingUid != SHELL_UID
+                    && callingUid != SYSTEM_UID) {
+                // If it's not debug build and not called from root/shell/system uid, reject it.
+                final String msg = "Permission Denial: instrumentation test "
+                        + className + " from pid=" + callingPid + ", uid=" + callingUid
+                        + ", pkgName=" + mInternal.getPackageNameByPid(callingPid)
+                        + " not allowed because it's not started from SHELL";
+                Slog.wtfQuiet(TAG, msg);
+                reportStartInstrumentationFailureLocked(watcher, className, msg);
+                throw new SecurityException(msg);
+            }
+
             boolean disableHiddenApiChecks = ai.usesNonSdkApi()
                     || (flags & INSTR_FLAG_DISABLE_HIDDEN_API_CHECKS) != 0;
             boolean disableTestApiChecks = disableHiddenApiChecks
@@ -15598,7 +15610,7 @@
      * {@link #enqueueOomAdjTargetLocked}.
      */
     @GuardedBy("this")
-    void updateOomAdjPendingTargetsLocked(String oomAdjReason) {
+    void updateOomAdjPendingTargetsLocked(@OomAdjuster.OomAdjReason int oomAdjReason) {
         mOomAdjuster.updateOomAdjPendingTargetsLocked(oomAdjReason);
     }
 
@@ -15617,7 +15629,7 @@
     }
 
     @GuardedBy("this")
-    final void updateOomAdjLocked(String oomAdjReason) {
+    final void updateOomAdjLocked(@OomAdjuster.OomAdjReason int oomAdjReason) {
         mOomAdjuster.updateOomAdjLocked(oomAdjReason);
     }
 
@@ -15629,7 +15641,8 @@
      * @return whether updateOomAdjLocked(app) was successful.
      */
     @GuardedBy("this")
-    final boolean updateOomAdjLocked(ProcessRecord app, String oomAdjReason) {
+    final boolean updateOomAdjLocked(
+            ProcessRecord app, @OomAdjuster.OomAdjReason int oomAdjReason) {
         return mOomAdjuster.updateOomAdjLocked(app, oomAdjReason);
     }
 
@@ -15862,14 +15875,16 @@
         mOomAdjuster.setUidTempAllowlistStateLSP(uid, onAllowlist);
     }
 
-    private void trimApplications(boolean forceFullOomAdj, String oomAdjReason) {
+    private void trimApplications(
+            boolean forceFullOomAdj, @OomAdjuster.OomAdjReason int oomAdjReason) {
         synchronized (this) {
             trimApplicationsLocked(forceFullOomAdj, oomAdjReason);
         }
     }
 
     @GuardedBy("this")
-    private void trimApplicationsLocked(boolean forceFullOomAdj, String oomAdjReason) {
+    private void trimApplicationsLocked(
+            boolean forceFullOomAdj, @OomAdjuster.OomAdjReason int oomAdjReason) {
         // First remove any unused application processes whose package
         // has been removed.
         boolean didSomething = false;
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 6609d4a..dbabe99 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -989,26 +989,40 @@
 
     @NeverCompile
     int runCompact(PrintWriter pw) {
-        String processName = getNextArgRequired();
-        String uid = getNextArgRequired();
-        String op = getNextArgRequired();
         ProcessRecord app;
-        synchronized (mInternal.mProcLock) {
-            app = mInternal.getProcessRecordLocked(processName, Integer.parseInt(uid));
-        }
-        pw.println("Process record found pid: " + app.mPid);
-        if (op.equals("full")) {
-            pw.println("Executing full compaction for " + app.mPid);
+        String op = getNextArgRequired();
+        boolean isFullCompact = op.equals("full");
+        boolean isSomeCompact = op.equals("some");
+        if (isFullCompact || isSomeCompact) {
+            String processName = getNextArgRequired();
+            String uid = getNextArgRequired();
             synchronized (mInternal.mProcLock) {
-                mInternal.mOomAdjuster.mCachedAppOptimizer.compactAppFull(app, true);
+                app = mInternal.getProcessRecordLocked(processName, Integer.parseInt(uid));
             }
-            pw.println("Finished full compaction for " + app.mPid);
-        } else if (op.equals("some")) {
-            pw.println("Executing some compaction for " + app.mPid);
+            pw.println("Process record found pid: " + app.mPid);
+            if (isFullCompact) {
+                pw.println("Executing full compaction for " + app.mPid);
+                synchronized (mInternal.mProcLock) {
+                    mInternal.mOomAdjuster.mCachedAppOptimizer.compactApp(app,
+                            CachedAppOptimizer.CompactProfile.FULL,
+                            CachedAppOptimizer.CompactSource.APP, true);
+                }
+                pw.println("Finished full compaction for " + app.mPid);
+            } else if (isSomeCompact) {
+                pw.println("Executing some compaction for " + app.mPid);
+                synchronized (mInternal.mProcLock) {
+                    mInternal.mOomAdjuster.mCachedAppOptimizer.compactApp(app,
+                            CachedAppOptimizer.CompactProfile.SOME,
+                            CachedAppOptimizer.CompactSource.APP, true);
+                }
+                pw.println("Finished some compaction for " + app.mPid);
+            }
+        } else if (op.equals("system")) {
+            pw.println("Executing system compaction");
             synchronized (mInternal.mProcLock) {
-                mInternal.mOomAdjuster.mCachedAppOptimizer.compactAppSome(app, true);
+                mInternal.mOomAdjuster.mCachedAppOptimizer.compactAllSystem();
             }
-            pw.println("Finished some compaction for " + app.mPid);
+            pw.println("Finished system compaction");
         } else {
             getErrPrintWriter().println("Error: unknown compact command '" + op + "'");
             return -1;
@@ -3570,10 +3584,11 @@
             pw.println("      --allow-background-activity-starts: The receiver may start activities");
             pw.println("          even if in the background.");
             pw.println("      --async: Send without waiting for the completion of the receiver.");
-            pw.println("  compact <process_name> <Package UID> [some|full]");
+            pw.println("  compact [some|full|system] <process_name> <Package UID>");
             pw.println("      Force process compaction.");
             pw.println("      some: execute file compaction.");
             pw.println("      full: execute anon + file compaction.");
+            pw.println("      system: system compaction.");
             pw.println("  instrument [-r] [-e <NAME> <VALUE>] [-p <FILE>] [-w]");
             pw.println("          [--user <USER_ID> | current]");
             pw.println("          [--no-hidden-api-checks [--no-test-api-access]]");
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index c88d82c..45265ac 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -25,7 +25,6 @@
 import android.app.ApplicationExitInfo;
 import android.database.ContentObserver;
 import android.net.Uri;
-import android.os.Debug;
 import android.os.Handler;
 import android.os.Message;
 import android.os.PowerManagerInternal;
@@ -40,20 +39,21 @@
 import android.util.EventLog;
 import android.util.Slog;
 import android.util.SparseArray;
-
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.os.ProcLocksReader;
 import com.android.internal.util.FrameworkStatsLog;
 import com.android.server.ServiceThread;
-
 import java.io.FileReader;
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.EnumMap;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.LinkedHashMap;
+import java.util.LinkedList;
 import java.util.Map;
 import java.util.Random;
 import java.util.Set;
@@ -99,7 +99,7 @@
     private static final int COMPACT_ACTION_NONE = 0;
     private static final int COMPACT_ACTION_FILE = 1;
     private static final int COMPACT_ACTION_ANON = 2;
-    private static final int COMPACT_ACTION_FULL = 3;
+    private static final int COMPACT_ACTION_ALL = 3;
 
     private static final String COMPACT_ACTION_STRING[] = {"", "file", "anon", "all"};
 
@@ -112,7 +112,7 @@
     // Defaults for phenotype flags.
     @VisibleForTesting static final Boolean DEFAULT_USE_COMPACTION = false;
     @VisibleForTesting static final Boolean DEFAULT_USE_FREEZER = true;
-    @VisibleForTesting static final int DEFAULT_COMPACT_ACTION_2 = COMPACT_ACTION_FULL;
+    @VisibleForTesting static final int DEFAULT_COMPACT_ACTION_2 = COMPACT_ACTION_ALL;
     @VisibleForTesting static final int DEFAULT_COMPACT_ACTION_1 = COMPACT_ACTION_FILE;
     @VisibleForTesting static final long DEFAULT_COMPACT_THROTTLE_1 = 5_000;
     @VisibleForTesting static final long DEFAULT_COMPACT_THROTTLE_2 = 10_000;
@@ -147,14 +147,33 @@
     @VisibleForTesting
     interface ProcessDependencies {
         long[] getRss(int pid);
-        void performCompaction(String action, int pid) throws IOException;
+        void performCompaction(CompactAction action, int pid) throws IOException;
+    }
+
+    // This indicates the compaction we want to perform
+    public enum CompactProfile {
+        SOME, // File compaction
+        FULL // File+anon compaction
+    }
+
+    // Low level actions that can be performed for compaction
+    // currently determined by the compaction profile
+    public enum CompactAction {
+        NONE, // No compaction
+        FILE, // File+anon compaction
+        ANON,
+        ALL
+    }
+
+    // This indicates the process OOM memory state that initiated the compaction request
+    public enum CompactSource { APP, PERSISTENT, BFGS }
+
+    public enum CancelCompactReason {
+        SCREEN_ON, // screen was turned on which cancels all compactions.
+        OOM_IMPROVEMENT // process moved out of cached state and into a more perceptible state.
     }
 
     // Handler constants.
-    static final int COMPACT_PROCESS_SOME = 1;
-    static final int COMPACT_PROCESS_FULL = 2;
-    static final int COMPACT_PROCESS_PERSISTENT = 3;
-    static final int COMPACT_PROCESS_BFGS = 4;
     static final int COMPACT_PROCESS_MSG = 1;
     static final int COMPACT_SYSTEM_MSG = 2;
     static final int SET_FROZEN_PROCESS_MSG = 3;
@@ -165,6 +184,12 @@
     // on swap resources as file.
     static final double COMPACT_DOWNGRADE_FREE_SWAP_THRESHOLD = 0.2;
 
+    // Size of history for the last 20 compactions for any process
+    static final int LAST_COMPACTED_ANY_PROCESS_STATS_HISTORY_SIZE = 20;
+
+    // Amount of processes supported to record for their last compaction.
+    static final int LAST_COMPACTION_FOR_PROCESS_STATS_SIZE = 256;
+
     static final int DO_FREEZE = 1;
     static final int REPORT_UNFREEZE = 2;
 
@@ -273,15 +298,16 @@
 
     private final SettingsContentObserver mSettingsObserver;
 
-    private final Object mPhenotypeFlagLock = new Object();
+    @VisibleForTesting
+    final Object mPhenotypeFlagLock = new Object();
 
     // Configured by phenotype. Updates from the server take effect immediately.
     @GuardedBy("mPhenotypeFlagLock")
-    @VisibleForTesting volatile String mCompactActionSome =
-            compactActionIntToString(DEFAULT_COMPACT_ACTION_1);
+    @VisibleForTesting
+    volatile CompactAction mCompactActionSome = compactActionIntToAction(DEFAULT_COMPACT_ACTION_1);
     @GuardedBy("mPhenotypeFlagLock")
-    @VisibleForTesting volatile String mCompactActionFull =
-            compactActionIntToString(DEFAULT_COMPACT_ACTION_2);
+    @VisibleForTesting
+    volatile CompactAction mCompactActionFull = compactActionIntToAction(DEFAULT_COMPACT_ACTION_2);
     @GuardedBy("mPhenotypeFlagLock")
     @VisibleForTesting volatile long mCompactThrottleSomeSome = DEFAULT_COMPACT_THROTTLE_1;
     @GuardedBy("mPhenotypeFlagLock")
@@ -333,31 +359,139 @@
     // facilitate removal of the oldest entry.
     @VisibleForTesting
     @GuardedBy("mProcLock")
-    LinkedHashMap<Integer, LastCompactionStats> mLastCompactionStats =
-            new LinkedHashMap<Integer, LastCompactionStats>() {
+    LinkedHashMap<Integer, SingleCompactionStats> mLastCompactionStats =
+            new LinkedHashMap<Integer, SingleCompactionStats>() {
                 @Override
                 protected boolean removeEldestEntry(Map.Entry eldest) {
-                    return size() > 100;
+                    return size() > LAST_COMPACTION_FOR_PROCESS_STATS_SIZE;
                 }
-    };
+            };
 
-    // Compaction Stats
-    private int mSomeCompactionCount;
-    private int mFullCompactionCount;
-    private int mPersistentCompactionCount;
-    private int mBfgsCompactionCount;
-    private long mSomeCompactRequest;
-    private long mFullCompactRequest;
-    private long mPersistentCompactRequest;
-    private long mBfgsCompactRequest;
-    private long mProcCompactionsRequested;
-    private long mProcCompactionsPerformed;
-    private long mProcCompactionsNoPidThrottled;
-    private long mProcCompactionsOomAdjThrottled;
-    private long mProcCompactionsTimeThrottled;
-    private long mProcCompactionsRSSThrottled;
-    private long mProcCompactionsMiscThrottled;
+    LinkedList<SingleCompactionStats> mCompactionStatsHistory =
+            new LinkedList<SingleCompactionStats>() {
+                @Override
+                public boolean add(SingleCompactionStats e) {
+                    if (size() >= LAST_COMPACTED_ANY_PROCESS_STATS_HISTORY_SIZE) {
+                        this.remove();
+                    }
+                    return super.add(e);
+                }
+            };
+
+    class AggregatedCompactionStats {
+        // Throttling stats
+        public long mFullCompactRequested;
+        public long mSomeCompactRequested;
+        public long mFullCompactPerformed;
+        public long mSomeCompactPerformed;
+        public long mProcCompactionsNoPidThrottled;
+        public long mProcCompactionsOomAdjThrottled;
+        public long mProcCompactionsTimeThrottled;
+        public long mProcCompactionsRSSThrottled;
+        public long mProcCompactionsMiscThrottled;
+
+        // Memory stats
+        public long mTotalDeltaAnonRssKBs;
+        public long mTotalZramConsumedKBs;
+        public long mTotalAnonMemFreedKBs;
+        public long mSumOrigAnonRss;
+        public double mMaxCompactEfficiency;
+
+        // Cpu time
+        public long mTotalCpuTimeMillis;
+
+        public long getThrottledSome() { return mSomeCompactRequested - mSomeCompactPerformed; }
+
+        public long getThrottledFull() { return mFullCompactRequested - mFullCompactPerformed; }
+
+        public void addMemStats(long anonRssSaved, long zramConsumed, long memFreed,
+                long origAnonRss, long totalCpuTimeMillis) {
+            final double compactEfficiency = memFreed / (double) origAnonRss;
+            if (compactEfficiency > mMaxCompactEfficiency) {
+                mMaxCompactEfficiency = compactEfficiency;
+            }
+            mTotalDeltaAnonRssKBs += anonRssSaved;
+            mTotalZramConsumedKBs += zramConsumed;
+            mTotalAnonMemFreedKBs += memFreed;
+            mSumOrigAnonRss += origAnonRss;
+            mTotalCpuTimeMillis += totalCpuTimeMillis;
+        }
+
+        public void dump(PrintWriter pw) {
+            long totalCompactRequested = mSomeCompactRequested + mFullCompactRequested;
+            long totalCompactPerformed = mSomeCompactPerformed + mFullCompactPerformed;
+            pw.println("    Performed / Requested:");
+            pw.println("      Some: (" + mSomeCompactPerformed + "/" + mSomeCompactRequested + ")");
+            pw.println("      Full: (" + mFullCompactPerformed + "/" + mFullCompactRequested + ")");
+
+            long throttledSome = getThrottledSome();
+            long throttledFull = getThrottledFull();
+
+            if (throttledSome > 0 || throttledFull > 0) {
+                pw.println("    Throttled:");
+                pw.println("       Some: " + throttledSome + " Full: " + throttledFull);
+                pw.println("    Throttled by Type:");
+                final long compactionsThrottled = totalCompactRequested - totalCompactPerformed;
+                // Any throttle that was not part of the previous categories
+                final long unaccountedThrottled = compactionsThrottled
+                        - mProcCompactionsNoPidThrottled - mProcCompactionsOomAdjThrottled
+                        - mProcCompactionsTimeThrottled - mProcCompactionsRSSThrottled
+                        - mProcCompactionsMiscThrottled;
+                pw.println("       NoPid: " + mProcCompactionsNoPidThrottled
+                        + " OomAdj: " + mProcCompactionsOomAdjThrottled + " Time: "
+                        + mProcCompactionsTimeThrottled + " RSS: " + mProcCompactionsRSSThrottled
+                        + " Misc: " + mProcCompactionsMiscThrottled
+                        + " Unaccounted: " + unaccountedThrottled);
+                final double compactThrottlePercentage =
+                        (compactionsThrottled / (double) totalCompactRequested) * 100.0;
+                pw.println("    Throttle Percentage: " + compactThrottlePercentage);
+            }
+
+            if (mFullCompactPerformed > 0) {
+                pw.println("    -----Memory Stats----");
+                pw.println("    Total Delta Anon RSS (KB) : " + mTotalDeltaAnonRssKBs);
+                pw.println("    Total Physical ZRAM Consumed (KB): " + mTotalZramConsumedKBs);
+                pw.println("    Total Anon Memory Freed (KB): " + mTotalAnonMemFreedKBs);
+                // This tells us how much anon memory we were able to free thanks to running
+                // compaction
+                pw.println("    Avg Compaction Efficiency (Anon Freed/Anon RSS): "
+                        + (mTotalAnonMemFreedKBs / (double) mSumOrigAnonRss));
+                pw.println("    Max Compaction Efficiency: " + mMaxCompactEfficiency);
+                // This tells us how effective is the compression algorithm in physical memory
+                pw.println("    Avg Compression Ratio (1 - ZRAM Consumed/DeltaAnonRSS): "
+                        + (1.0 - mTotalZramConsumedKBs / (double) mTotalDeltaAnonRssKBs));
+                long avgKBsPerProcCompact = mFullCompactPerformed > 0
+                        ? (mTotalAnonMemFreedKBs / mFullCompactPerformed)
+                        : 0;
+                pw.println("    Avg Anon Mem Freed/Compaction (KB) : " + avgKBsPerProcCompact);
+                double compactionCost =
+                        mTotalCpuTimeMillis / (mTotalAnonMemFreedKBs / 1024.0); // ms/MB
+                pw.println("    Compaction Cost (ms/MB): " + compactionCost);
+            }
+        }
+    }
+
+    class AggregatedProcessCompactionStats extends AggregatedCompactionStats {
+        public final String processName;
+
+        AggregatedProcessCompactionStats(String processName) { this.processName = processName; }
+    }
+
+    class AggregatedSourceCompactionStats extends AggregatedCompactionStats {
+        public final CompactSource sourceType;
+
+        AggregatedSourceCompactionStats(CompactSource sourceType) { this.sourceType = sourceType; }
+    }
+
+    private final LinkedHashMap<String, AggregatedProcessCompactionStats> mPerProcessCompactStats =
+            new LinkedHashMap<>(256);
+    private final EnumMap<CompactSource, AggregatedSourceCompactionStats> mPerSourceCompactStats =
+            new EnumMap<>(CompactSource.class);
+    private long mTotalCompactionDowngrades;
     private long mSystemCompactionsPerformed;
+    private long mSystemTotalMemFreed;
+    private EnumMap<CancelCompactReason, Integer> mTotalCompactionsCancelled =
+            new EnumMap<>(CancelCompactReason.class);
 
     private final ProcessDependencies mProcessDependencies;
     private final ProcLocksReader mProcLocksReader;
@@ -450,34 +584,61 @@
             pw.println("  "  + KEY_COMPACT_PROC_STATE_THROTTLE + "="
                     + Arrays.toString(mProcStateThrottle.toArray(new Integer[0])));
 
-            pw.println(" Requested:  " + mSomeCompactRequest + " some, " + mFullCompactRequest
-                    + " full, " + mPersistentCompactRequest + " persistent, "
-                    + mBfgsCompactRequest + " BFGS compactions.");
-            pw.println(" Performed: " + mSomeCompactionCount + " some, " + mFullCompactionCount
-                    + " full, " + mPersistentCompactionCount + " persistent, "
-                    + mBfgsCompactionCount + " BFGS compactions.");
-            pw.println(" Process Compactions Requested: " + mProcCompactionsRequested);
-            pw.println(" Process Compactions Performed: " + mProcCompactionsPerformed);
-            long compactionsThrottled = mProcCompactionsRequested - mProcCompactionsPerformed;
-            pw.println(" Process Compactions Throttled: " + compactionsThrottled);
-            double compactThrottlePercentage =
-                    (compactionsThrottled / (double) mProcCompactionsRequested) * 100.0;
-            pw.println(" Process Compactions Throttle Percentage: " + compactThrottlePercentage);
-            pw.println("        NoPid Throttled: " + mProcCompactionsNoPidThrottled);
-            pw.println("        OomAdj Throttled: " + mProcCompactionsOomAdjThrottled);
-            pw.println("        Time Throttled: " + mProcCompactionsTimeThrottled);
-            pw.println("        RSS Throttled: " + mProcCompactionsRSSThrottled);
-            pw.println("        Misc Throttled: " + mProcCompactionsMiscThrottled);
-            long unaccountedThrottled = compactionsThrottled - mProcCompactionsNoPidThrottled
-                    - mProcCompactionsOomAdjThrottled - mProcCompactionsTimeThrottled
-                    - mProcCompactionsRSSThrottled - mProcCompactionsMiscThrottled;
-            // Any throttle that was not part of the previous categories
-            pw.println("        Unaccounted Throttled: " + unaccountedThrottled);
+            pw.println(" Per-Process Compaction Stats");
+            long totalCompactPerformedSome = 0;
+            long totalCompactPerformedFull = 0;
+            for (AggregatedProcessCompactionStats stats : mPerProcessCompactStats.values()) {
+                pw.println("-----" + stats.processName + "-----");
+                totalCompactPerformedSome += stats.mSomeCompactPerformed;
+                totalCompactPerformedFull += stats.mFullCompactPerformed;
+                stats.dump(pw);
+                pw.println();
+            }
+            pw.println();
+            pw.println(" Per-Source Compaction Stats");
+            for (AggregatedSourceCompactionStats stats : mPerSourceCompactStats.values()) {
+                pw.println("-----" + stats.sourceType + "-----");
+                stats.dump(pw);
+                pw.println();
+            }
+            pw.println();
 
-            pw.println(" System Compactions Performed: " + mSystemCompactionsPerformed);
+            pw.println("Total Compactions Performed by profile: " + totalCompactPerformedSome
+                    + " some, " + totalCompactPerformedFull + " full");
+            pw.println("Total compactions downgraded: " + mTotalCompactionDowngrades);
+            pw.println("Total compactions cancelled by reason: ");
+            for (CancelCompactReason reason : mTotalCompactionsCancelled.keySet()) {
+                pw.println("    " + reason + ": " + mTotalCompactionsCancelled.get(reason));
+            }
+            pw.println();
 
+            pw.println(" System Compaction Memory Stats");
+            pw.println("    Compactions Performed: " + mSystemCompactionsPerformed);
+            pw.println("    Total Memory Freed (KB): " + mSystemTotalMemFreed);
+            double avgKBsPerSystemCompact = mSystemCompactionsPerformed > 0
+                    ? mSystemTotalMemFreed / mSystemCompactionsPerformed
+                    : 0;
+            pw.println("    Avg Mem Freed per Compact (KB): " + avgKBsPerSystemCompact);
+            pw.println();
             pw.println("  Tracking last compaction stats for " + mLastCompactionStats.size()
                     + " processes.");
+            pw.println("Last Compaction per process stats:");
+            pw.println("    (ProcessName,Source,DeltaAnonRssKBs,ZramConsumedKBs,AnonMemFreedKBs,"
+                    + "CompactEfficiency,CompactCost(ms/MB),procState,oomAdj,oomAdjReason)");
+            for (Map.Entry<Integer, SingleCompactionStats> entry :
+                    mLastCompactionStats.entrySet()) {
+                SingleCompactionStats stats = entry.getValue();
+                stats.dump(pw);
+            }
+            pw.println();
+            pw.println("Last 20 Compactions Stats:");
+            pw.println("    (ProcessName,Source,DeltaAnonRssKBs,ZramConsumedKBs,AnonMemFreedKBs,"
+                    + "CompactEfficiency,CompactCost(ms/MB),procState,oomAdj,oomAdjReason)");
+            for (SingleCompactionStats stats : mCompactionStatsHistory) {
+                stats.dump(pw);
+            }
+            pw.println();
+
             pw.println("  " + KEY_USE_FREEZER + "=" + mUseFreezer);
             pw.println("  " + KEY_FREEZER_STATSD_SAMPLE_RATE + "=" + mFreezerStatsdSampleRate);
             pw.println("  " + KEY_FREEZER_DEBOUNCE_TIMEOUT + "=" + mFreezerDebounceTimeout);
@@ -500,25 +661,9 @@
                     }
                 }
             }
-            if (DEBUG_COMPACTION) {
-                for (Map.Entry<Integer, LastCompactionStats> entry
-                        : mLastCompactionStats.entrySet()) {
-                    int pid = entry.getKey();
-                    LastCompactionStats stats = entry.getValue();
-                    pw.println("    " + pid + ": "
-                            + Arrays.toString(stats.getRssAfterCompaction()));
-                }
-            }
         }
     }
 
-    @GuardedBy("mProcLock")
-    void compactAppSome(ProcessRecord app, boolean force) {
-        app.mOptRecord.setReqCompactAction(COMPACT_PROCESS_SOME);
-        ++mSomeCompactRequest;
-        compactApp(app, force, "some");
-    }
-
     // This method returns true only if requirements are met. Note, that requirements are different
     // from throttles applied at the time a compaction is trying to be executed in the sense that
     // these are not subject to change dependent on time or memory as throttles usually do.
@@ -546,46 +691,38 @@
     }
 
     @GuardedBy("mProcLock")
-    void compactAppFull(ProcessRecord app, boolean force) {
-        boolean oomAdjEnteredCached = (app.mState.getSetAdj() < mCompactThrottleMinOomAdj
-                                              || app.mState.getSetAdj() > mCompactThrottleMaxOomAdj)
-                && app.mState.getCurAdj() >= mCompactThrottleMinOomAdj
-                && app.mState.getCurAdj() <= mCompactThrottleMaxOomAdj;
-        if (DEBUG_COMPACTION) {
-            Slog.d(TAG_AM,
-                    " compactAppFull requested for " + app.processName + " force: " + force
-                            + " oomAdjEnteredCached: " + oomAdjEnteredCached);
+    boolean compactApp(
+            ProcessRecord app, CompactProfile compactProfile, CompactSource source, boolean force) {
+        app.mOptRecord.setReqCompactSource(source);
+        app.mOptRecord.setReqCompactProfile(compactProfile);
+        AggregatedSourceCompactionStats perSourceStats = getPerSourceAggregatedCompactStat(source);
+        AggregatedCompactionStats perProcStats =
+                getPerProcessAggregatedCompactStat(app.processName);
+        switch (compactProfile) {
+            case SOME:
+                ++perProcStats.mSomeCompactRequested;
+                ++perSourceStats.mSomeCompactRequested;
+                break;
+            case FULL:
+                ++perProcStats.mFullCompactRequested;
+                ++perSourceStats.mFullCompactRequested;
+                break;
+            default:
+                Slog.e(TAG_AM,
+                        "Unimplemented compaction type, consider adding it.");
+                return false;
         }
-        ++mFullCompactRequest;
-        // Apply OOM adj score throttle for Full App Compaction.
-        if (force || oomAdjEnteredCached) {
-            app.mOptRecord.setReqCompactAction(COMPACT_PROCESS_FULL);
-            compactApp(app, force, "Full");
-        } else {
-            if (DEBUG_COMPACTION) {
-                Slog.d(TAG_AM, "Skipping full compaction for " + app.processName
-                        + " oom adj score changed from " + app.mState.getSetAdj()
-                        + " to " + app.mState.getCurAdj());
-            }
-        }
-    }
 
-    @GuardedBy("mProcLock")
-    void compactAppPersistent(ProcessRecord app) {
-        app.mOptRecord.setReqCompactAction(COMPACT_PROCESS_PERSISTENT);
-        ++mPersistentCompactRequest;
-        compactApp(app, false, "Persistent");
-    }
-
-    @GuardedBy("mProcLock")
-    boolean compactApp(ProcessRecord app, boolean force, String compactRequestType) {
-        if (!app.mOptRecord.hasPendingCompact() && meetsCompactionRequirements(app)) {
+        if (!app.mOptRecord.hasPendingCompact() && (meetsCompactionRequirements(app) || force)) {
             final String processName = (app.processName != null ? app.processName : "");
             if (DEBUG_COMPACTION) {
-                Slog.d(TAG_AM, "compactApp " + compactRequestType + " " + processName);
+                Slog.d(TAG_AM,
+                        "compactApp " + app.mOptRecord.getReqCompactSource().name() + " "
+                                + app.mOptRecord.getReqCompactProfile().name() + " " + processName);
             }
             Trace.instantForTrack(Trace.TRACE_TAG_ACTIVITY_MANAGER, ATRACE_COMPACTION_TRACK,
-                    "compactApp " + compactRequestType + " " + processName);
+                    "compactApp " + app.mOptRecord.getReqCompactSource().name() + " "
+                            + app.mOptRecord.getReqCompactProfile().name() + " " + processName);
             app.mOptRecord.setHasPendingCompact(true);
             app.mOptRecord.setForceCompact(force);
             mPendingCompactionProcesses.add(app);
@@ -596,14 +733,53 @@
 
         if (DEBUG_COMPACTION) {
             Slog.d(TAG_AM,
-                    " compactApp Skipped for " + app.processName
-                            + " pendingCompact= " + app.mOptRecord.hasPendingCompact()
-                            + " meetsCompactionRequirements=" + meetsCompactionRequirements(app)
-                            + ". Requested compact: " + app.mOptRecord.getReqCompactAction());
+                    " compactApp Skipped for " + app.processName + " pendingCompact= "
+                            + app.mOptRecord.hasPendingCompact() + " meetsCompactionRequirements="
+                            + meetsCompactionRequirements(app) + ". Requested compact profile: "
+                            + app.mOptRecord.getReqCompactProfile().name() + ". Compact source "
+                            + app.mOptRecord.getReqCompactSource().name());
         }
         return false;
     }
 
+    private CompactAction resolveCompactActionForProfile(CompactProfile profile) {
+        CompactAction action;
+        switch (profile) {
+            case SOME:
+                action = CompactAction.FILE;
+                break;
+            case FULL:
+                action = CompactAction.ALL;
+                break;
+            default:
+                action = CompactAction.NONE;
+        }
+        return action;
+    }
+
+    private AggregatedProcessCompactionStats getPerProcessAggregatedCompactStat(
+            String processName) {
+        if (processName == null) {
+            processName = "";
+        }
+        AggregatedProcessCompactionStats stats = mPerProcessCompactStats.get(processName);
+        if (stats == null) {
+            stats = new AggregatedProcessCompactionStats(processName);
+            mPerProcessCompactStats.put(processName, stats);
+        }
+        return stats;
+    }
+
+    private AggregatedSourceCompactionStats getPerSourceAggregatedCompactStat(
+            CompactSource source) {
+        AggregatedSourceCompactionStats stats = mPerSourceCompactStats.get(source);
+        if (stats == null) {
+            stats = new AggregatedSourceCompactionStats(source);
+            mPerSourceCompactStats.put(source, stats);
+        }
+        return stats;
+    }
+
     @GuardedBy("mProcLock")
     boolean shouldCompactPersistent(ProcessRecord app, long now) {
         return (app.mOptRecord.getLastCompactTime() == 0
@@ -611,13 +787,6 @@
     }
 
     @GuardedBy("mProcLock")
-    void compactAppBfgs(ProcessRecord app) {
-        ++mBfgsCompactRequest;
-        app.mOptRecord.setReqCompactAction(COMPACT_PROCESS_BFGS);
-        compactApp(app, false, " Bfgs");
-    }
-
-    @GuardedBy("mProcLock")
     boolean shouldCompactBFGS(ProcessRecord app, long now) {
         return (app.mOptRecord.getLastCompactTime() == 0
                 || (now - app.mOptRecord.getLastCompactTime()) > mCompactThrottleBFGS);
@@ -648,11 +817,27 @@
     static private native void cancelCompaction();
 
     /**
+     * Returns the cpu time for the current thread
+     */
+    static private native long threadCpuTimeNs();
+
+    /**
      * Retrieves the free swap percentage.
      */
     static private native double getFreeSwapPercent();
 
     /**
+     * Retrieves the total used physical ZRAM
+     */
+    static private native long getUsedZramMemory();
+
+    /**
+     * Amount of memory that has been made free due to compaction.
+     * It represents uncompressed memory size - compressed memory size.
+     */
+    static private native long getMemoryFreedCompaction();
+
+    /**
      * Reads the flag value from DeviceConfig to determine whether app compaction
      * should be enabled, and starts the freeze/compaction thread if needed.
      */
@@ -851,8 +1036,8 @@
         int compactAction2 = DeviceConfig.getInt(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
                 KEY_COMPACT_ACTION_2, DEFAULT_COMPACT_ACTION_2);
 
-        mCompactActionSome = compactActionIntToString(compactAction1);
-        mCompactActionFull = compactActionIntToString(compactAction2);
+        mCompactActionSome = compactActionIntToAction(compactAction1);
+        mCompactActionFull = compactActionIntToAction(compactAction2);
     }
 
     @GuardedBy("mPhenotypeFlagLock")
@@ -1019,13 +1204,12 @@
         return true;
     }
 
-    @VisibleForTesting
-    static String compactActionIntToString(int action) {
-        if (action < 0 || action >= COMPACT_ACTION_STRING.length) {
-            return "";
+    static CompactAction compactActionIntToAction(int action) {
+        if (action < 0 || action >= CompactAction.values().length) {
+            return CompactAction.NONE;
         }
 
-        return COMPACT_ACTION_STRING[action];
+        return CompactAction.values()[action];
     }
 
     // This will ensure app will be out of the freezer for at least mFreezerDebounceTimeout.
@@ -1202,16 +1386,17 @@
         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");
-            cancelAllCompactions();
+            cancelAllCompactions(CancelCompactReason.SCREEN_ON);
         }
     }
 
-    void cancelAllCompactions() {
+    void cancelAllCompactions(CancelCompactReason reason) {
         synchronized (mProcLock) {
             int size = mPendingCompactionProcesses.size();
             ProcessRecord record;
             for (int i=0; i < size; ++i) {
                 record = mPendingCompactionProcesses.get(i);
+                cancelCompactionForProcess(record, reason);
                 // The process record is kept alive after compactions are cleared,
                 // so make sure to reset the compaction state to avoid skipping any future
                 // compactions due to a stale value here.
@@ -1222,53 +1407,64 @@
         cancelCompaction();
     }
 
+    @GuardedBy("mProcLock")
+    void cancelCompactionForProcess(ProcessRecord app, CancelCompactReason cancelReason) {
+        boolean cancelled = false;
+        if (!mPendingCompactionProcesses.isEmpty() && mPendingCompactionProcesses.contains(app)) {
+            app.mOptRecord.setHasPendingCompact(false);
+            mPendingCompactionProcesses.remove(app);
+            cancelled = true;
+        }
+        if (DefaultProcessDependencies.mPidCompacting == app.mPid) {
+            cancelCompaction();
+            cancelled = true;
+        }
+        if (cancelled) {
+            if (mTotalCompactionsCancelled.containsKey(cancelReason)) {
+                int count = mTotalCompactionsCancelled.get(cancelReason);
+                mTotalCompactionsCancelled.put(cancelReason, count + 1);
+            } else {
+                mTotalCompactionsCancelled.put(cancelReason, 1);
+            }
+            if (DEBUG_COMPACTION) {
+                Slog.d(TAG_AM,
+                        "Cancelled pending or running compactions for process: " +
+                                app.processName != null ? app.processName : "" +
+                                " reason: " + cancelReason.name());
+            }
+        }
+    }
+
     @GuardedBy({"mService", "mProcLock"})
     void onOomAdjustChanged(int oldAdj, int newAdj, ProcessRecord app) {
         // Cancel any currently executing compactions
         // if the process moved out of cached state
-        if (DefaultProcessDependencies.mPidCompacting == app.mPid && newAdj < oldAdj
-                && newAdj < ProcessList.CACHED_APP_MIN_ADJ) {
-            cancelCompaction();
+        if (newAdj < oldAdj && newAdj < ProcessList.CACHED_APP_MIN_ADJ) {
+            cancelCompactionForProcess(app, CancelCompactReason.OOM_IMPROVEMENT);
         }
 
         if (oldAdj <= ProcessList.PERCEPTIBLE_APP_ADJ
                 && (newAdj == ProcessList.PREVIOUS_APP_ADJ || newAdj == ProcessList.HOME_APP_ADJ)) {
             // Perform a minor compaction when a perceptible app becomes the prev/home app
-            compactAppSome(app, false);
+            compactApp(app, CompactProfile.SOME, CompactSource.APP, false);
         } else if (oldAdj < ProcessList.CACHED_APP_MIN_ADJ
                 && newAdj >= ProcessList.CACHED_APP_MIN_ADJ
                 && newAdj <= ProcessList.CACHED_APP_MAX_ADJ) {
             // Perform a major compaction when any app enters cached
-            compactAppFull(app, false);
+            compactApp(app, CompactProfile.FULL, CompactSource.APP, false);
         }
     }
 
     /**
-     * This method resolves which compaction method we should use for the proposed compaction.
+     * Applies a compaction downgrade when swap is low.
      */
-    int resolveCompactionAction(int pendingAction) {
-        int resolvedAction;
-
-        switch (pendingAction) {
-            case COMPACT_PROCESS_SOME:
-                resolvedAction = COMPACT_ACTION_FILE;
-                break;
-            // For the time being, treat these as equivalent.
-            case COMPACT_PROCESS_FULL:
-            case COMPACT_PROCESS_PERSISTENT:
-            case COMPACT_PROCESS_BFGS:
-                resolvedAction = COMPACT_ACTION_FULL;
-                break;
-            default:
-                resolvedAction = COMPACT_ACTION_NONE;
-                break;
-        }
-
+    CompactProfile downgradeCompactionIfRequired(CompactProfile profile) {
         // Downgrade compaction under swap memory pressure
-        if (resolvedAction == COMPACT_ACTION_FULL) {
+        if (profile == CompactProfile.FULL) {
             double swapFreePercent = getFreeSwapPercent();
             if (swapFreePercent < COMPACT_DOWNGRADE_FREE_SWAP_THRESHOLD) {
-                resolvedAction = COMPACT_ACTION_FILE;
+                profile = CompactProfile.SOME;
+                ++mTotalCompactionDowngrades;
                 if (DEBUG_COMPACTION) {
                     Slog.d(TAG_AM,
                             "Downgraded compaction to file only due to low swap."
@@ -1277,20 +1473,69 @@
             }
         }
 
-        return resolvedAction;
+        return profile;
     }
 
     @VisibleForTesting
-    static final class LastCompactionStats {
+    static final class SingleCompactionStats {
+        private static final float STATSD_SAMPLE_RATE = 0.1f;
+        private static final Random mRandom = new Random();
         private final long[] mRssAfterCompaction;
+        public CompactSource mSourceType;
+        public String mProcessName;
+        public final int mUid;
+        public long mDeltaAnonRssKBs;
+        public long mZramConsumedKBs;
+        public long mAnonMemFreedKBs;
+        public float mCpuTimeMillis;
+        public long mOrigAnonRss;
+        public int mProcState;
+        public int mOomAdj;
+        public @OomAdjuster.OomAdjReason int mOomAdjReason;
 
-        LastCompactionStats(long[] rss) {
+        SingleCompactionStats(long[] rss, CompactSource source, String processName,
+                long deltaAnonRss, long zramConsumed, long anonMemFreed, long origAnonRss,
+                long cpuTimeMillis, int procState, int oomAdj,
+                @OomAdjuster.OomAdjReason int oomAdjReason, int uid) {
             mRssAfterCompaction = rss;
+            mSourceType = source;
+            mProcessName = processName;
+            mUid = uid;
+            mDeltaAnonRssKBs = deltaAnonRss;
+            mZramConsumedKBs = zramConsumed;
+            mAnonMemFreedKBs = anonMemFreed;
+            mCpuTimeMillis = cpuTimeMillis;
+            mOrigAnonRss = origAnonRss;
+            mProcState = procState;
+            mOomAdj = oomAdj;
+            mOomAdjReason = oomAdjReason;
+        }
+
+        double getCompactEfficiency() { return mAnonMemFreedKBs / (double) mOrigAnonRss; }
+
+        double getCompactCost() {
+            // mCpuTimeMillis / (anonMemFreedKBs/1024) and metric is in (ms/MB)
+            return mCpuTimeMillis / (double) mAnonMemFreedKBs * 1024;
         }
 
         long[] getRssAfterCompaction() {
             return mRssAfterCompaction;
         }
+
+        void dump(PrintWriter pw) {
+            pw.println("    (" + mProcessName + "," + mSourceType.name() + "," + mDeltaAnonRssKBs
+                    + "," + mZramConsumedKBs + "," + mAnonMemFreedKBs + "," + getCompactEfficiency()
+                    + "," + getCompactCost() + "," + mProcState + "," + mOomAdj + ","
+                    + OomAdjuster.oomAdjReasonToString(mOomAdjReason) + ")");
+        }
+
+        void sendStat() {
+            if (mRandom.nextFloat() < STATSD_SAMPLE_RATE) {
+                FrameworkStatsLog.write(FrameworkStatsLog.APP_COMPACTED_V2, mUid, mProcState,
+                        mOomAdj, mDeltaAnonRssKBs, mZramConsumedKBs, mCpuTimeMillis, mOrigAnonRss,
+                        mOomAdjReason);
+            }
+        }
     }
 
     private final class MemCompactionHandler extends Handler {
@@ -1298,13 +1543,12 @@
             super(mCachedAppOptimizerThread.getLooper());
         }
 
-        private boolean shouldOomAdjThrottleCompaction(ProcessRecord proc, int action) {
+        private boolean shouldOomAdjThrottleCompaction(ProcessRecord proc) {
             final String name = proc.processName;
 
             // don't compact if the process has returned to perceptible
             // and this is only a cached/home/prev compaction
-            if ((action == COMPACT_ACTION_FILE || action == COMPACT_ACTION_FULL)
-                    && (proc.mState.getSetAdj() <= ProcessList.PERCEPTIBLE_APP_ADJ)) {
+            if (proc.mState.getSetAdj() <= ProcessList.PERCEPTIBLE_APP_ADJ) {
                 if (DEBUG_COMPACTION) {
                     Slog.d(TAG_AM,
                             "Skipping compaction as process " + name + " is "
@@ -1316,51 +1560,52 @@
             return false;
         }
 
-        private boolean shouldTimeThrottleCompaction(
-                ProcessRecord proc, long start, int pendingAction) {
+        private boolean shouldTimeThrottleCompaction(ProcessRecord proc, long start,
+                CompactProfile pendingProfile, CompactSource source) {
             final ProcessCachedOptimizerRecord opt = proc.mOptRecord;
             final String name = proc.processName;
 
-            int lastCompactAction = opt.getLastCompactAction();
+            CompactProfile lastCompactProfile = opt.getLastCompactProfile();
             long lastCompactTime = opt.getLastCompactTime();
 
             // basic throttling
-            // use the Phenotype flag knobs to determine whether current/prevous
-            // compaction combo should be throtted or not
-
+            // use the Phenotype flag knobs to determine whether current/previous
+            // compaction combo should be throttled or not.
             // Note that we explicitly don't take mPhenotypeFlagLock here as the flags
             // should very seldom change, and taking the risk of using the wrong action is
             // preferable to taking the lock for every single compaction action.
             if (lastCompactTime != 0) {
-                if (pendingAction == COMPACT_PROCESS_SOME) {
-                    if ((lastCompactAction == COMPACT_PROCESS_SOME
-                                && (start - lastCompactTime < mCompactThrottleSomeSome))
-                            || (lastCompactAction == COMPACT_PROCESS_FULL
-                                    && (start - lastCompactTime < mCompactThrottleSomeFull))) {
-                        if (DEBUG_COMPACTION) {
-                            Slog.d(TAG_AM,
-                                    "Skipping some compaction for " + name
-                                            + ": too soon. throttle=" + mCompactThrottleSomeSome
-                                            + "/" + mCompactThrottleSomeFull
-                                            + " last=" + (start - lastCompactTime) + "ms ago");
+                if (source == CompactSource.APP) {
+                    if (pendingProfile == CompactProfile.SOME) {
+                        if ((lastCompactProfile == CompactProfile.SOME
+                                    && (start - lastCompactTime < mCompactThrottleSomeSome))
+                                || (lastCompactProfile == CompactProfile.FULL
+                                        && (start - lastCompactTime < mCompactThrottleSomeFull))) {
+                            if (DEBUG_COMPACTION) {
+                                Slog.d(TAG_AM,
+                                        "Skipping some compaction for " + name
+                                                + ": too soon. throttle=" + mCompactThrottleSomeSome
+                                                + "/" + mCompactThrottleSomeFull
+                                                + " last=" + (start - lastCompactTime) + "ms ago");
+                            }
+                            return true;
                         }
-                        return true;
-                    }
-                } else if (pendingAction == COMPACT_PROCESS_FULL) {
-                    if ((lastCompactAction == COMPACT_PROCESS_SOME
-                                && (start - lastCompactTime < mCompactThrottleFullSome))
-                            || (lastCompactAction == COMPACT_PROCESS_FULL
-                                    && (start - lastCompactTime < mCompactThrottleFullFull))) {
-                        if (DEBUG_COMPACTION) {
-                            Slog.d(TAG_AM,
-                                    "Skipping full compaction for " + name
-                                            + ": too soon. throttle=" + mCompactThrottleFullSome
-                                            + "/" + mCompactThrottleFullFull
-                                            + " last=" + (start - lastCompactTime) + "ms ago");
+                    } else if (pendingProfile == CompactProfile.FULL) {
+                        if ((lastCompactProfile == CompactProfile.SOME
+                                    && (start - lastCompactTime < mCompactThrottleFullSome))
+                                || (lastCompactProfile == CompactProfile.FULL
+                                        && (start - lastCompactTime < mCompactThrottleFullFull))) {
+                            if (DEBUG_COMPACTION) {
+                                Slog.d(TAG_AM,
+                                        "Skipping full compaction for " + name
+                                                + ": too soon. throttle=" + mCompactThrottleFullSome
+                                                + "/" + mCompactThrottleFullFull
+                                                + " last=" + (start - lastCompactTime) + "ms ago");
+                            }
+                            return true;
                         }
-                        return true;
                     }
-                } else if (pendingAction == COMPACT_PROCESS_PERSISTENT) {
+                } else if (source == CompactSource.PERSISTENT) {
                     if (start - lastCompactTime < mCompactThrottlePersistent) {
                         if (DEBUG_COMPACTION) {
                             Slog.d(TAG_AM,
@@ -1370,7 +1615,7 @@
                         }
                         return true;
                     }
-                } else if (pendingAction == COMPACT_PROCESS_BFGS) {
+                } else if (source == CompactSource.BFGS) {
                     if (start - lastCompactTime < mCompactThrottleBFGS) {
                         if (DEBUG_COMPACTION) {
                             Slog.d(TAG_AM,
@@ -1386,11 +1631,10 @@
             return false;
         }
 
-        private boolean shouldThrottleMiscCompaction(
-                ProcessRecord proc, int procState, int action) {
-            final String name = proc.processName;
+        private boolean shouldThrottleMiscCompaction(ProcessRecord proc, int procState) {
             if (mProcStateThrottle.contains(procState)) {
                 if (DEBUG_COMPACTION) {
+                    final String name = proc.processName;
                     Slog.d(TAG_AM,
                             "Skipping full compaction for process " + name + "; proc state is "
                                     + procState);
@@ -1398,21 +1642,13 @@
                 return true;
             }
 
-            if (COMPACT_ACTION_NONE == action) {
-                if (DEBUG_COMPACTION) {
-                    Slog.d(TAG_AM,
-                            "Skipping compaction for process " + name + "since action is None");
-                }
-                return true;
-            }
-
             return false;
         }
 
         private boolean shouldRssThrottleCompaction(
-                int action, int pid, String name, long[] rssBefore) {
+                CompactProfile profile, int pid, String name, long[] rssBefore) {
             long anonRssBefore = rssBefore[RSS_ANON_INDEX];
-            LastCompactionStats lastCompactionStats = mLastCompactionStats.get(pid);
+            SingleCompactionStats lastCompactionStats = mLastCompactionStats.get(pid);
 
             if (rssBefore[RSS_TOTAL_INDEX] == 0 && rssBefore[RSS_FILE_INDEX] == 0
                     && rssBefore[RSS_ANON_INDEX] == 0 && rssBefore[RSS_SWAP_INDEX] == 0) {
@@ -1424,7 +1660,7 @@
                 return true;
             }
 
-            if (action == COMPACT_ACTION_FULL || action == COMPACT_ACTION_ANON) {
+            if (profile == CompactProfile.FULL) {
                 if (mFullAnonRssThrottleKb > 0L && anonRssBefore < mFullAnonRssThrottleKb) {
                     if (DEBUG_COMPACTION) {
                         Slog.d(TAG_AM,
@@ -1461,13 +1697,16 @@
                     ProcessRecord proc;
                     final ProcessCachedOptimizerRecord opt;
                     int pid;
-                    String action;
+                    CompactAction resolvedAction;
                     final String name;
-                    int requestedAction, lastCompactAction;
+                    CompactProfile lastCompactProfile;
                     long lastCompactTime;
-                    int lastOomAdj = msg.arg1;
+                    int newOomAdj = msg.arg1;
                     int procState = msg.arg2;
                     boolean forceCompaction;
+                    CompactSource compactSource;
+                    CompactProfile requestedProfile;
+                    int oomAdjReason;
                     synchronized (mProcLock) {
                         if (mPendingCompactionProcesses.isEmpty()) {
                             if (DEBUG_COMPACTION) {
@@ -1479,42 +1718,53 @@
                         opt = proc.mOptRecord;
                         forceCompaction = opt.isForceCompact();
                         opt.setForceCompact(false); // since this is a one-shot operation
-
-                        requestedAction = opt.getReqCompactAction();
                         pid = proc.getPid();
                         name = proc.processName;
                         opt.setHasPendingCompact(false);
-                        lastCompactAction = opt.getLastCompactAction();
+                        compactSource = opt.getReqCompactSource();
+                        requestedProfile = opt.getReqCompactProfile();
+                        lastCompactProfile = opt.getLastCompactProfile();
                         lastCompactTime = opt.getLastCompactTime();
+                        oomAdjReason = opt.getLastOomAdjChangeReason();
                     }
 
-                    ++mProcCompactionsRequested;
+                    AggregatedSourceCompactionStats perSourceStats =
+                            getPerSourceAggregatedCompactStat(opt.getReqCompactSource());
+                    AggregatedProcessCompactionStats perProcessStats =
+                            getPerProcessAggregatedCompactStat(name);
+
                     long[] rssBefore;
                     if (pid == 0) {
                         // not a real process, either one being launched or one being killed
                         if (DEBUG_COMPACTION) {
                             Slog.d(TAG_AM, "Compaction failed, pid is 0");
                         }
-                        ++mProcCompactionsNoPidThrottled;
+                        ++perSourceStats.mProcCompactionsNoPidThrottled;
+                        ++perProcessStats.mProcCompactionsNoPidThrottled;
                         return;
                     }
 
                     if (!forceCompaction) {
-                        if (shouldOomAdjThrottleCompaction(proc, requestedAction)) {
-                            ++mProcCompactionsOomAdjThrottled;
+                        if (shouldOomAdjThrottleCompaction(proc)) {
+                            ++perProcessStats.mProcCompactionsOomAdjThrottled;
+                            ++perSourceStats.mProcCompactionsOomAdjThrottled;
                             return;
                         }
-                        if (shouldTimeThrottleCompaction(proc, start, requestedAction)) {
-                            ++mProcCompactionsTimeThrottled;
+                        if (shouldTimeThrottleCompaction(
+                                    proc, start, requestedProfile, compactSource)) {
+                            ++perProcessStats.mProcCompactionsTimeThrottled;
+                            ++perSourceStats.mProcCompactionsTimeThrottled;
                             return;
                         }
-                        if (shouldThrottleMiscCompaction(proc, procState, requestedAction)) {
-                            ++mProcCompactionsMiscThrottled;
+                        if (shouldThrottleMiscCompaction(proc, procState)) {
+                            ++perProcessStats.mProcCompactionsMiscThrottled;
+                            ++perSourceStats.mProcCompactionsMiscThrottled;
                             return;
                         }
                         rssBefore = mProcessDependencies.getRss(pid);
-                        if (shouldRssThrottleCompaction(requestedAction, pid, name, rssBefore)) {
-                            ++mProcCompactionsRSSThrottled;
+                        if (shouldRssThrottleCompaction(requestedProfile, pid, name, rssBefore)) {
+                            ++perProcessStats.mProcCompactionsRSSThrottled;
+                            ++perSourceStats.mProcCompactionsRSSThrottled;
                             return;
                         }
                     } else {
@@ -1524,71 +1774,78 @@
                         }
                     }
 
-                    // Now we've passed through all the throttles and are going to compact, update
-                    // bookkeeping.
-                    switch (requestedAction) {
-                        case COMPACT_PROCESS_SOME:
-                            mSomeCompactionCount++;
-                            break;
-                        case COMPACT_PROCESS_FULL:
-                            mFullCompactionCount++;
-                            break;
-                        case COMPACT_PROCESS_PERSISTENT:
-                            mPersistentCompactionCount++;
-                            break;
-                        case COMPACT_PROCESS_BFGS:
-                            mBfgsCompactionCount++;
-                            break;
-                        default:
-                            break;
-                    }
-
-                    int resolvedAction = resolveCompactionAction(requestedAction);
-                    action = compactActionIntToString(resolvedAction);
+                    CompactProfile resolvedProfile =
+                            downgradeCompactionIfRequired(requestedProfile);
+                    resolvedAction = resolveCompactActionForProfile(resolvedProfile);
 
                     try {
                         Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
-                                "Compact " + action + ": " + name);
-                        ++mProcCompactionsPerformed;
-                        long zramFreeKbBefore = Debug.getZramFreeKb();
-                        mProcessDependencies.performCompaction(action, pid);
+                                "Compact " + resolvedAction.name() + ": " + name
+                                        + " lastOomAdjReason: " + oomAdjReason);
+                        long zramUsedKbBefore = getUsedZramMemory();
+                        long startCpuTime = threadCpuTimeNs();
+                        mProcessDependencies.performCompaction(resolvedAction, pid);
+                        long endCpuTime = threadCpuTimeNs();
                         long[] rssAfter = mProcessDependencies.getRss(pid);
                         long end = SystemClock.uptimeMillis();
                         long time = end - start;
-                        long zramFreeKbAfter = Debug.getZramFreeKb();
+                        long deltaCpuTimeNanos = endCpuTime - startCpuTime;
+                        long zramUsedKbAfter = getUsedZramMemory();
                         long deltaTotalRss = rssAfter[RSS_TOTAL_INDEX] - rssBefore[RSS_TOTAL_INDEX];
                         long deltaFileRss = rssAfter[RSS_FILE_INDEX] - rssBefore[RSS_FILE_INDEX];
                         long deltaAnonRss = rssAfter[RSS_ANON_INDEX] - rssBefore[RSS_ANON_INDEX];
                         long deltaSwapRss = rssAfter[RSS_SWAP_INDEX] - rssBefore[RSS_SWAP_INDEX];
-                        EventLog.writeEvent(EventLogTags.AM_COMPACT, pid, name, action,
-                                rssBefore[RSS_TOTAL_INDEX], rssBefore[RSS_FILE_INDEX],
-                                rssBefore[RSS_ANON_INDEX], rssBefore[RSS_SWAP_INDEX], deltaTotalRss,
-                                deltaFileRss, deltaAnonRss, deltaSwapRss, time, lastCompactAction,
-                                lastCompactTime, lastOomAdj, procState, zramFreeKbBefore,
-                                zramFreeKbAfter - zramFreeKbBefore);
-                        // Note that as above not taking mPhenoTypeFlagLock here to avoid locking
-                        // on every single compaction for a flag that will seldom change and the
-                        // impact of reading the wrong value here is low.
-                        if (mRandom.nextFloat() < mCompactStatsdSampleRate) {
-                            FrameworkStatsLog.write(FrameworkStatsLog.APP_COMPACTED, pid, name,
-                                    requestedAction, rssBefore[RSS_TOTAL_INDEX],
-                                    rssBefore[RSS_FILE_INDEX], rssBefore[RSS_ANON_INDEX],
-                                    rssBefore[RSS_SWAP_INDEX], rssAfter[RSS_TOTAL_INDEX],
-                                    rssAfter[RSS_FILE_INDEX], rssAfter[RSS_ANON_INDEX],
-                                    rssAfter[RSS_SWAP_INDEX], time, lastCompactAction,
-                                    lastCompactTime, lastOomAdj,
-                                    ActivityManager.processStateAmToProto(procState),
-                                    zramFreeKbBefore, zramFreeKbAfter);
+                        switch (opt.getReqCompactProfile()) {
+                            case SOME:
+                                ++perSourceStats.mSomeCompactPerformed;
+                                ++perProcessStats.mSomeCompactPerformed;
+                                break;
+                            case FULL:
+                                ++perSourceStats.mFullCompactPerformed;
+                                ++perProcessStats.mFullCompactPerformed;
+                                long anonRssSavings = -deltaAnonRss;
+                                long zramConsumed = zramUsedKbAfter - zramUsedKbBefore;
+                                long memFreed = anonRssSavings - zramConsumed;
+                                long totalCpuTimeMillis = deltaCpuTimeNanos / 1000000;
+                                long origAnonRss = rssBefore[RSS_ANON_INDEX];
+
+                                // Negative stats would skew averages and will likely be due to
+                                // noise of system doing other things so we put a floor at 0 to
+                                // avoid negative values.
+                                anonRssSavings = anonRssSavings > 0 ? anonRssSavings : 0;
+                                zramConsumed = zramConsumed > 0 ? zramConsumed : 0;
+                                memFreed = memFreed > 0 ? memFreed : 0;
+
+                                perProcessStats.addMemStats(anonRssSavings, zramConsumed, memFreed,
+                                        origAnonRss, totalCpuTimeMillis);
+                                perSourceStats.addMemStats(anonRssSavings, zramConsumed, memFreed,
+                                        origAnonRss, totalCpuTimeMillis);
+                                SingleCompactionStats memStats = new SingleCompactionStats(rssAfter,
+                                        compactSource, name, anonRssSavings, zramConsumed, memFreed,
+                                        origAnonRss, totalCpuTimeMillis, procState, newOomAdj,
+                                        oomAdjReason, proc.uid);
+                                mLastCompactionStats.remove(pid);
+                                mLastCompactionStats.put(pid, memStats);
+                                mCompactionStatsHistory.add(memStats);
+                                memStats.sendStat();
+                                break;
+                            default:
+                                // We likely missed adding this category, it needs to be added
+                                // if we end up here. In the meantime, gracefully fallback to
+                                // attribute to app.
+                                Slog.wtf(TAG_AM, "Compaction: Unknown requested action");
+                                return;
                         }
+                        EventLog.writeEvent(EventLogTags.AM_COMPACT, pid, name,
+                                resolvedAction.name(), rssBefore[RSS_TOTAL_INDEX],
+                                rssBefore[RSS_FILE_INDEX], rssBefore[RSS_ANON_INDEX],
+                                rssBefore[RSS_SWAP_INDEX], deltaTotalRss, deltaFileRss,
+                                deltaAnonRss, deltaSwapRss, time, lastCompactProfile.name(),
+                                lastCompactTime, newOomAdj, procState, zramUsedKbBefore,
+                                zramUsedKbBefore - zramUsedKbAfter);
                         synchronized (mProcLock) {
                             opt.setLastCompactTime(end);
-                            opt.setLastCompactAction(resolvedAction);
-                        }
-                        if (resolvedAction == COMPACT_ACTION_FULL
-                                || resolvedAction == COMPACT_ACTION_ANON) {
-                            // Remove entry and insert again to update insertion order.
-                            mLastCompactionStats.remove(pid);
-                            mLastCompactionStats.put(pid, new LastCompactionStats(rssAfter));
+                            opt.setLastCompactProfile(requestedProfile);
                         }
                     } catch (Exception e) {
                         // nothing to do, presumably the process died
@@ -1603,7 +1860,10 @@
                 case COMPACT_SYSTEM_MSG: {
                     ++mSystemCompactionsPerformed;
                     Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "compactSystem");
+                    long memFreedBefore = getMemoryFreedCompaction();
                     compactSystem();
+                    long memFreedAfter = getMemoryFreedCompaction();
+                    mSystemTotalMemFreed += memFreedAfter - memFreedBefore;
                     Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                     break;
                 }
@@ -1809,14 +2069,14 @@
 
         // Compact process.
         @Override
-        public void performCompaction(String action, int pid) throws IOException {
+        public void performCompaction(CompactAction action, int pid) throws IOException {
             mPidCompacting = pid;
-            if (action.equals(COMPACT_ACTION_STRING[COMPACT_ACTION_FULL])) {
-                compactProcess(pid, COMPACT_ACTION_FILE_FLAG | COMPACT_ACTION_ANON_FLAG);
-            } else if (action.equals(COMPACT_ACTION_STRING[COMPACT_ACTION_FILE])) {
-                compactProcess(pid, COMPACT_ACTION_FILE_FLAG);
-            } else if (action.equals(COMPACT_ACTION_STRING[COMPACT_ACTION_ANON])) {
-                compactProcess(pid, COMPACT_ACTION_ANON_FLAG);
+            if (action == CompactAction.ALL) {
+                    compactProcess(pid, COMPACT_ACTION_FILE_FLAG | COMPACT_ACTION_ANON_FLAG);
+            } else if (action == CompactAction.FILE) {
+                    compactProcess(pid, COMPACT_ACTION_FILE_FLAG);
+            } else if (action == CompactAction.ANON) {
+                    compactProcess(pid, COMPACT_ACTION_ANON_FLAG);
             }
             mPidCompacting = -1;
         }
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 9607e40..8759f230 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -76,9 +76,11 @@
 import static com.android.server.am.ProcessList.TAG_PROCESS_OBSERVERS;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH;
 
+import android.annotation.IntDef;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.ActivityThread;
+import android.app.AppProtoEnums;
 import android.app.ApplicationExitInfo;
 import android.app.usage.UsageEvents;
 import android.compat.annotation.ChangeId;
@@ -113,6 +115,8 @@
 import com.android.server.wm.WindowProcessController;
 
 import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayDeque;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -122,20 +126,97 @@
  */
 public class OomAdjuster {
     static final String TAG = "OomAdjuster";
-    static final String OOM_ADJ_REASON_METHOD = "updateOomAdj";
-    static final String OOM_ADJ_REASON_NONE = OOM_ADJ_REASON_METHOD + "_meh";
-    static final String OOM_ADJ_REASON_ACTIVITY = OOM_ADJ_REASON_METHOD + "_activityChange";
-    static final String OOM_ADJ_REASON_FINISH_RECEIVER = OOM_ADJ_REASON_METHOD + "_finishReceiver";
-    static final String OOM_ADJ_REASON_START_RECEIVER = OOM_ADJ_REASON_METHOD + "_startReceiver";
-    static final String OOM_ADJ_REASON_BIND_SERVICE = OOM_ADJ_REASON_METHOD + "_bindService";
-    static final String OOM_ADJ_REASON_UNBIND_SERVICE = OOM_ADJ_REASON_METHOD + "_unbindService";
-    static final String OOM_ADJ_REASON_START_SERVICE = OOM_ADJ_REASON_METHOD + "_startService";
-    static final String OOM_ADJ_REASON_GET_PROVIDER = OOM_ADJ_REASON_METHOD + "_getProvider";
-    static final String OOM_ADJ_REASON_REMOVE_PROVIDER = OOM_ADJ_REASON_METHOD + "_removeProvider";
-    static final String OOM_ADJ_REASON_UI_VISIBILITY = OOM_ADJ_REASON_METHOD + "_uiVisibility";
-    static final String OOM_ADJ_REASON_ALLOWLIST = OOM_ADJ_REASON_METHOD + "_allowlistChange";
-    static final String OOM_ADJ_REASON_PROCESS_BEGIN = OOM_ADJ_REASON_METHOD + "_processBegin";
-    static final String OOM_ADJ_REASON_PROCESS_END = OOM_ADJ_REASON_METHOD + "_processEnd";
+
+    static final int OOM_ADJ_REASON_NONE = 0;
+    static final int OOM_ADJ_REASON_ACTIVITY = 1;
+    static final int OOM_ADJ_REASON_FINISH_RECEIVER = 2;
+    static final int OOM_ADJ_REASON_START_RECEIVER = 3;
+    static final int OOM_ADJ_REASON_BIND_SERVICE = 4;
+    static final int OOM_ADJ_REASON_UNBIND_SERVICE = 5;
+    static final int OOM_ADJ_REASON_START_SERVICE = 6;
+    static final int OOM_ADJ_REASON_GET_PROVIDER = 7;
+    static final int OOM_ADJ_REASON_REMOVE_PROVIDER = 8;
+    static final int OOM_ADJ_REASON_UI_VISIBILITY = 9;
+    static final int OOM_ADJ_REASON_ALLOWLIST = 10;
+    static final int OOM_ADJ_REASON_PROCESS_BEGIN = 11;
+    static final int OOM_ADJ_REASON_PROCESS_END = 12;
+
+    @IntDef(prefix = {"OOM_ADJ_REASON_"},
+            value = {OOM_ADJ_REASON_NONE, OOM_ADJ_REASON_ACTIVITY, OOM_ADJ_REASON_FINISH_RECEIVER,
+                    OOM_ADJ_REASON_START_RECEIVER, OOM_ADJ_REASON_BIND_SERVICE,
+                    OOM_ADJ_REASON_UNBIND_SERVICE, OOM_ADJ_REASON_START_SERVICE,
+                    OOM_ADJ_REASON_GET_PROVIDER, OOM_ADJ_REASON_REMOVE_PROVIDER,
+                    OOM_ADJ_REASON_UI_VISIBILITY, OOM_ADJ_REASON_ALLOWLIST,
+                    OOM_ADJ_REASON_PROCESS_BEGIN, OOM_ADJ_REASON_PROCESS_END})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface OomAdjReason {}
+
+    public static final int oomAdjReasonToProto(@OomAdjReason int oomReason) {
+        switch (oomReason) {
+            case OOM_ADJ_REASON_NONE:
+                return AppProtoEnums.OOM_ADJ_REASON_NONE;
+            case OOM_ADJ_REASON_ACTIVITY:
+                return AppProtoEnums.OOM_ADJ_REASON_ACTIVITY;
+            case OOM_ADJ_REASON_FINISH_RECEIVER:
+                return AppProtoEnums.OOM_ADJ_REASON_FINISH_RECEIVER;
+            case OOM_ADJ_REASON_START_RECEIVER:
+                return AppProtoEnums.OOM_ADJ_REASON_START_RECEIVER;
+            case OOM_ADJ_REASON_BIND_SERVICE:
+                return AppProtoEnums.OOM_ADJ_REASON_BIND_SERVICE;
+            case OOM_ADJ_REASON_UNBIND_SERVICE:
+                return AppProtoEnums.OOM_ADJ_REASON_UNBIND_SERVICE;
+            case OOM_ADJ_REASON_START_SERVICE:
+                return AppProtoEnums.OOM_ADJ_REASON_START_SERVICE;
+            case OOM_ADJ_REASON_GET_PROVIDER:
+                return AppProtoEnums.OOM_ADJ_REASON_GET_PROVIDER;
+            case OOM_ADJ_REASON_REMOVE_PROVIDER:
+                return AppProtoEnums.OOM_ADJ_REASON_REMOVE_PROVIDER;
+            case OOM_ADJ_REASON_UI_VISIBILITY:
+                return AppProtoEnums.OOM_ADJ_REASON_UI_VISIBILITY;
+            case OOM_ADJ_REASON_ALLOWLIST:
+                return AppProtoEnums.OOM_ADJ_REASON_ALLOWLIST;
+            case OOM_ADJ_REASON_PROCESS_BEGIN:
+                return AppProtoEnums.OOM_ADJ_REASON_PROCESS_BEGIN;
+            case OOM_ADJ_REASON_PROCESS_END:
+                return AppProtoEnums.OOM_ADJ_REASON_PROCESS_END;
+            default:
+                return AppProtoEnums.OOM_ADJ_REASON_UNKNOWN_TO_PROTO;
+        }
+    }
+
+    public static final String oomAdjReasonToString(@OomAdjReason int oomReason) {
+        final String OOM_ADJ_REASON_METHOD = "updateOomAdj";
+        switch (oomReason) {
+            case OOM_ADJ_REASON_NONE:
+                return OOM_ADJ_REASON_METHOD + "_meh";
+            case OOM_ADJ_REASON_ACTIVITY:
+                return OOM_ADJ_REASON_METHOD + "_activityChange";
+            case OOM_ADJ_REASON_FINISH_RECEIVER:
+                return OOM_ADJ_REASON_METHOD + "_finishReceiver";
+            case OOM_ADJ_REASON_START_RECEIVER:
+                return OOM_ADJ_REASON_METHOD + "_startReceiver";
+            case OOM_ADJ_REASON_BIND_SERVICE:
+                return OOM_ADJ_REASON_METHOD + "_bindService";
+            case OOM_ADJ_REASON_UNBIND_SERVICE:
+                return OOM_ADJ_REASON_METHOD + "_unbindService";
+            case OOM_ADJ_REASON_START_SERVICE:
+                return OOM_ADJ_REASON_METHOD + "_startService";
+            case OOM_ADJ_REASON_GET_PROVIDER:
+                return OOM_ADJ_REASON_METHOD + "_getProvider";
+            case OOM_ADJ_REASON_REMOVE_PROVIDER:
+                return OOM_ADJ_REASON_METHOD + "_removeProvider";
+            case OOM_ADJ_REASON_UI_VISIBILITY:
+                return OOM_ADJ_REASON_METHOD + "_uiVisibility";
+            case OOM_ADJ_REASON_ALLOWLIST:
+                return OOM_ADJ_REASON_METHOD + "_allowlistChange";
+            case OOM_ADJ_REASON_PROCESS_BEGIN:
+                return OOM_ADJ_REASON_METHOD + "_processBegin";
+            case OOM_ADJ_REASON_PROCESS_END:
+                return OOM_ADJ_REASON_METHOD + "_processEnd";
+            default:
+                return "_unknown";
+        }
+    }
 
     /**
      * Flag {@link android.content.Context#BIND_INCLUDE_CAPABILITIES} is used
@@ -418,14 +499,14 @@
      * Update OomAdj for all processes in LRU list
      */
     @GuardedBy("mService")
-    void updateOomAdjLocked(String oomAdjReason) {
+    void updateOomAdjLocked(@OomAdjReason int oomAdjReason) {
         synchronized (mProcLock) {
             updateOomAdjLSP(oomAdjReason);
         }
     }
 
     @GuardedBy({"mService", "mProcLock"})
-    private void updateOomAdjLSP(String oomAdjReason) {
+    private void updateOomAdjLSP(@OomAdjReason int oomAdjReason) {
         if (checkAndEnqueueOomAdjTargetLocked(null)) {
             // Simply return as there is an oomAdjUpdate ongoing
             return;
@@ -441,7 +522,7 @@
     }
 
     @GuardedBy({"mService", "mProcLock"})
-    private void performUpdateOomAdjLSP(String oomAdjReason) {
+    private void performUpdateOomAdjLSP(@OomAdjReason int oomAdjReason) {
         final ProcessRecord topApp = mService.getTopApp();
         // Clear any pending ones because we are doing a full update now.
         mPendingProcessSet.clear();
@@ -458,14 +539,14 @@
      * @param oomAdjReason
      */
     @GuardedBy("mService")
-    boolean updateOomAdjLocked(ProcessRecord app, String oomAdjReason) {
+    boolean updateOomAdjLocked(ProcessRecord app, @OomAdjReason int oomAdjReason) {
         synchronized (mProcLock) {
             return updateOomAdjLSP(app, oomAdjReason);
         }
     }
 
     @GuardedBy({"mService", "mProcLock"})
-    private boolean updateOomAdjLSP(ProcessRecord app, String oomAdjReason) {
+    private boolean updateOomAdjLSP(ProcessRecord app, @OomAdjReason int oomAdjReason) {
         if (app == null || !mConstants.OOMADJ_UPDATE_QUICK) {
             updateOomAdjLSP(oomAdjReason);
             return true;
@@ -487,10 +568,10 @@
     }
 
     @GuardedBy({"mService", "mProcLock"})
-    private boolean performUpdateOomAdjLSP(ProcessRecord app, String oomAdjReason) {
+    private boolean performUpdateOomAdjLSP(ProcessRecord app, @OomAdjReason int oomAdjReason) {
         final ProcessRecord topApp = mService.getTopApp();
 
-        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, oomAdjReason);
+        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, oomAdjReasonToString(oomAdjReason));
         mService.mOomAdjProfiler.oomAdjStarted();
         mAdjSeq++;
 
@@ -509,6 +590,7 @@
         state.setCurBoundByNonBgRestrictedApp(false);
         // Check if this process is in the pending list too, remove from pending list if so.
         mPendingProcessSet.remove(app);
+        app.mOptRecord.setLastOomAdjChangeReason(oomAdjReason);
         boolean success = performUpdateOomAdjLSP(app, cachedAdj, topApp,
                 SystemClock.uptimeMillis());
         // The 'app' here itself might or might not be in the cycle, for example,
@@ -688,7 +770,7 @@
      * {@link #enqueueOomAdjTargetLocked}.
      */
     @GuardedBy("mService")
-    void updateOomAdjPendingTargetsLocked(String oomAdjReason) {
+    void updateOomAdjPendingTargetsLocked(@OomAdjReason int oomAdjReason) {
         // First check if there is pending full update
         if (mPendingFullOomAdjUpdate) {
             mPendingFullOomAdjUpdate = false;
@@ -716,10 +798,11 @@
     }
 
     @GuardedBy("mService")
-    private void performUpdateOomAdjPendingTargetsLocked(String oomAdjReason) {
+    private void performUpdateOomAdjPendingTargetsLocked(
+            @OomAdjuster.OomAdjReason int oomAdjReason) {
         final ProcessRecord topApp = mService.getTopApp();
 
-        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, oomAdjReason);
+        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, oomAdjReasonToString(oomAdjReason));
         mService.mOomAdjProfiler.oomAdjStarted();
 
         final ArrayList<ProcessRecord> processes = mTmpProcessList;
@@ -741,11 +824,11 @@
      * get evaluated recursively here.
      */
     @GuardedBy({"mService", "mProcLock"})
-    private void updateOomAdjInnerLSP(String oomAdjReason, final ProcessRecord topApp,
+    private void updateOomAdjInnerLSP(@OomAdjReason int oomAdjReason, final ProcessRecord topApp,
             ArrayList<ProcessRecord> processes, ActiveUids uids, boolean potentialCycles,
             boolean startProfiling) {
         if (startProfiling) {
-            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, oomAdjReason);
+            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, oomAdjReasonToString(oomAdjReason));
             mService.mOomAdjProfiler.oomAdjStarted();
         }
         final long now = SystemClock.uptimeMillis();
@@ -806,6 +889,7 @@
             final ProcessStateRecord state = app.mState;
             if (!app.isKilledByAm() && app.getThread() != null) {
                 state.setProcStateChanged(false);
+                app.mOptRecord.setLastOomAdjChangeReason(oomAdjReason);
                 computeOomAdjLSP(app, ProcessList.UNKNOWN_ADJ, topApp, fullUpdate, now, false,
                         computeClients); // It won't enter cycle if not computing clients.
                 // if any app encountered a cycle, we need to perform an additional loop later
@@ -2582,11 +2666,13 @@
                         // processing of the requests. As a result, there is throttling both here
                         // and in CachedAppOptimizer.
                         && mCachedAppOptimizer.shouldCompactPersistent(app, now)) {
-                    mCachedAppOptimizer.compactAppPersistent(app);
+                    mCachedAppOptimizer.compactApp(app, CachedAppOptimizer.CompactProfile.FULL,
+                            CachedAppOptimizer.CompactSource.PERSISTENT, false);
                 } else if (state.getCurProcState()
                                 == ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE
                         && mCachedAppOptimizer.shouldCompactBFGS(app, now)) {
-                    mCachedAppOptimizer.compactAppBfgs(app);
+                    mCachedAppOptimizer.compactApp(app, CachedAppOptimizer.CompactProfile.FULL,
+                            CachedAppOptimizer.CompactSource.BFGS, false);
                 }
             }
         }
diff --git a/services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java b/services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java
index a613729..fb41a39 100644
--- a/services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java
+++ b/services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java
@@ -39,16 +39,24 @@
     private long mLastCompactTime;
 
     /**
-     * The most recent compaction action requested for this app.
+     * The most recent compaction profile requested for this app.
      */
-    @GuardedBy("mProcLock")
-    private int mReqCompactAction;
+    @GuardedBy("mProcLock") private CachedAppOptimizer.CompactProfile mReqCompactProfile;
+
+    /**
+     * Source that requested the latest compaction for this app.
+     */
+    @GuardedBy("mProcLock") private CachedAppOptimizer.CompactSource mReqCompactSource;
+
+    /**
+     * Last oom adjust change reason for this app.
+     */
+    @GuardedBy("mProcLock") private @OomAdjuster.OomAdjReason int mLastOomAdjChangeReason;
 
     /**
      * The most recent compaction action performed for this app.
      */
-    @GuardedBy("mProcLock")
-    private int mLastCompactAction;
+    @GuardedBy("mProcLock") private CachedAppOptimizer.CompactProfile mLastCompactProfile;
 
     /**
      * This process has been scheduled for a memory compaction.
@@ -105,23 +113,49 @@
     }
 
     @GuardedBy("mProcLock")
-    int getReqCompactAction() {
-        return mReqCompactAction;
+    CachedAppOptimizer.CompactProfile getReqCompactProfile() {
+        return mReqCompactProfile;
     }
 
     @GuardedBy("mProcLock")
-    void setReqCompactAction(int reqCompactAction) {
-        mReqCompactAction = reqCompactAction;
+    void setReqCompactProfile(CachedAppOptimizer.CompactProfile reqCompactProfile) {
+        mReqCompactProfile = reqCompactProfile;
     }
 
     @GuardedBy("mProcLock")
-    int getLastCompactAction() {
-        return mLastCompactAction;
+    CachedAppOptimizer.CompactSource getReqCompactSource() {
+        return mReqCompactSource;
     }
 
     @GuardedBy("mProcLock")
-    void setLastCompactAction(int lastCompactAction) {
-        mLastCompactAction = lastCompactAction;
+    void setReqCompactSource(CachedAppOptimizer.CompactSource stat) {
+        mReqCompactSource = stat;
+    }
+
+    @GuardedBy("mProcLock")
+    void setLastOomAdjChangeReason(@OomAdjuster.OomAdjReason int reason) {
+        mLastOomAdjChangeReason = reason;
+    }
+
+    @GuardedBy("mProcLock")
+    @OomAdjuster.OomAdjReason
+    int getLastOomAdjChangeReason() {
+        return mLastOomAdjChangeReason;
+    }
+
+    @GuardedBy("mProcLock")
+    CachedAppOptimizer.CompactProfile getLastCompactProfile() {
+        if (mLastCompactProfile == null) {
+            // The first compaction won't have a previous one, so assign one to avoid crashing.
+            mLastCompactProfile = CachedAppOptimizer.CompactProfile.SOME;
+        }
+
+        return mLastCompactProfile;
+    }
+
+    @GuardedBy("mProcLock")
+    void setLastCompactProfile(CachedAppOptimizer.CompactProfile lastCompactProfile) {
+        mLastCompactProfile = lastCompactProfile;
     }
 
     @GuardedBy("mProcLock")
@@ -216,7 +250,8 @@
     @GuardedBy("mProcLock")
     void dump(PrintWriter pw, String prefix, long nowUptime) {
         pw.print(prefix); pw.print("lastCompactTime="); pw.print(mLastCompactTime);
-        pw.print(" lastCompactAction="); pw.println(mLastCompactAction);
+        pw.print(" lastCompactProfile=");
+        pw.println(mLastCompactProfile);
         pw.print(prefix);
         pw.print("hasPendingCompaction=");
         pw.print(mPendingCompact);
diff --git a/services/core/java/com/android/server/app/GameManagerService.java b/services/core/java/com/android/server/app/GameManagerService.java
index dd73cbe..854b818 100644
--- a/services/core/java/com/android/server/app/GameManagerService.java
+++ b/services/core/java/com/android/server/app/GameManagerService.java
@@ -25,20 +25,6 @@
 import static com.android.internal.R.styleable.GameModeConfig_allowGameFpsOverride;
 import static com.android.internal.R.styleable.GameModeConfig_supportsBatteryGameMode;
 import static com.android.internal.R.styleable.GameModeConfig_supportsPerformanceGameMode;
-import static com.android.server.wm.CompatModePackages.DOWNSCALED;
-import static com.android.server.wm.CompatModePackages.DOWNSCALE_30;
-import static com.android.server.wm.CompatModePackages.DOWNSCALE_35;
-import static com.android.server.wm.CompatModePackages.DOWNSCALE_40;
-import static com.android.server.wm.CompatModePackages.DOWNSCALE_45;
-import static com.android.server.wm.CompatModePackages.DOWNSCALE_50;
-import static com.android.server.wm.CompatModePackages.DOWNSCALE_55;
-import static com.android.server.wm.CompatModePackages.DOWNSCALE_60;
-import static com.android.server.wm.CompatModePackages.DOWNSCALE_65;
-import static com.android.server.wm.CompatModePackages.DOWNSCALE_70;
-import static com.android.server.wm.CompatModePackages.DOWNSCALE_75;
-import static com.android.server.wm.CompatModePackages.DOWNSCALE_80;
-import static com.android.server.wm.CompatModePackages.DOWNSCALE_85;
-import static com.android.server.wm.CompatModePackages.DOWNSCALE_90;
 
 import android.Manifest;
 import android.annotation.NonNull;
@@ -48,10 +34,10 @@
 import android.app.ActivityManager;
 import android.app.GameManager;
 import android.app.GameManager.GameMode;
+import android.app.GameManagerInternal;
 import android.app.GameModeInfo;
 import android.app.GameState;
 import android.app.IGameManagerService;
-import android.app.compat.PackageOverride;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -75,9 +61,7 @@
 import android.os.Message;
 import android.os.PowerManagerInternal;
 import android.os.Process;
-import android.os.RemoteException;
 import android.os.ResultReceiver;
-import android.os.ServiceManager;
 import android.os.ShellCallback;
 import android.os.UserManager;
 import android.provider.DeviceConfig;
@@ -92,8 +76,6 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.compat.CompatibilityOverrideConfig;
-import com.android.internal.compat.IPlatformCompat;
 import com.android.internal.os.BackgroundThread;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.FrameworkStatsLog;
@@ -140,10 +122,6 @@
     static final int WRITE_SETTINGS_DELAY = 10 * 1000;  // 10 seconds
     static final int LOADING_BOOST_MAX_DURATION = 5 * 1000;  // 5 seconds
 
-    static final PackageOverride COMPAT_ENABLED = new PackageOverride.Builder().setEnabled(true)
-            .build();
-    static final PackageOverride COMPAT_DISABLED = new PackageOverride.Builder().setEnabled(false)
-            .build();
     private static final String PACKAGE_NAME_MSG_KEY = "packageName";
     private static final String USER_ID_MSG_KEY = "userId";
     private static final String GAME_MODE_INTERVENTION_LIST_FILE_NAME =
@@ -156,7 +134,6 @@
     private final Handler mHandler;
     private final PackageManager mPackageManager;
     private final UserManager mUserManager;
-    private final IPlatformCompat mPlatformCompat;
     private final PowerManagerInternal mPowerManagerInternal;
     private final File mSystemDir;
     @VisibleForTesting
@@ -180,8 +157,6 @@
         mHandler = new SettingsHandler(looper);
         mPackageManager = mContext.getPackageManager();
         mUserManager = mContext.getSystemService(UserManager.class);
-        mPlatformCompat = IPlatformCompat.Stub.asInterface(
-                ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE));
         mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
         mSystemDir = new File(Environment.getDataDirectory(), "system");
         mSystemDir.mkdirs();
@@ -212,8 +187,6 @@
         mHandler = new SettingsHandler(looper);
         mPackageManager = mContext.getPackageManager();
         mUserManager = mContext.getSystemService(UserManager.class);
-        mPlatformCompat = IPlatformCompat.Stub.asInterface(
-                ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE));
         mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
         mSystemDir = new File(dataDir, "system");
         mSystemDir.mkdirs();
@@ -326,14 +299,10 @@
                     break;
                 }
                 case POPULATE_GAME_MODE_SETTINGS: {
-                    // Scan all game packages and re-enforce the configured compat mode overrides
-                    // as the DeviceConfig may have be wiped/since last reboot and we can't risk
-                    // having overrides configured for packages that no longer have any DeviceConfig
-                    // and thus any way to escape compat mode.
                     removeMessages(POPULATE_GAME_MODE_SETTINGS, msg.obj);
                     final int userId = (int) msg.obj;
                     final String[] packageNames = getInstalledGamePackageNames(userId);
-                    updateConfigsForUser(userId, packageNames);
+                    updateConfigsForUser(userId, false /*checkGamePackage*/, packageNames);
                     break;
                 }
                 case SET_GAME_STATE: {
@@ -402,7 +371,8 @@
         @Override
         public void onPropertiesChanged(Properties properties) {
             final String[] packageNames = properties.getKeyset().toArray(new String[0]);
-            updateConfigsForUser(ActivityManager.getCurrentUser(), packageNames);
+            updateConfigsForUser(ActivityManager.getCurrentUser(), true /*checkGamePackage*/,
+                    packageNames);
         }
 
         @Override
@@ -411,39 +381,6 @@
         }
     }
 
-    // Turn the raw string to the corresponding CompatChange id.
-    static long getCompatChangeId(String raw) {
-        switch (raw) {
-            case "0.3":
-                return DOWNSCALE_30;
-            case "0.35":
-                return DOWNSCALE_35;
-            case "0.4":
-                return DOWNSCALE_40;
-            case "0.45":
-                return DOWNSCALE_45;
-            case "0.5":
-                return DOWNSCALE_50;
-            case "0.55":
-                return DOWNSCALE_55;
-            case "0.6":
-                return DOWNSCALE_60;
-            case "0.65":
-                return DOWNSCALE_65;
-            case "0.7":
-                return DOWNSCALE_70;
-            case "0.75":
-                return DOWNSCALE_75;
-            case "0.8":
-                return DOWNSCALE_80;
-            case "0.85":
-                return DOWNSCALE_85;
-            case "0.9":
-                return DOWNSCALE_90;
-        }
-        return 0;
-    }
-
     public enum FrameRate {
         FPS_DEFAULT(0),
         FPS_30(30),
@@ -553,7 +490,9 @@
 
         private static final String GAME_MODE_CONFIG_NODE_NAME = "game-mode-config";
         private final String mPackageName;
-        private final ArrayMap<Integer, GameModeConfiguration> mModeConfigs;
+        private final Object mModeConfigLock = new Object();
+        @GuardedBy("mModeConfigLock")
+        private final ArrayMap<Integer, GameModeConfiguration> mModeConfigs = new ArrayMap<>();
         private boolean mPerfModeOptedIn;
         private boolean mBatteryModeOptedIn;
         private boolean mAllowDownscale;
@@ -562,7 +501,6 @@
 
         GamePackageConfiguration(String packageName, int userId) {
             mPackageName = packageName;
-            mModeConfigs = new ArrayMap<>();
             try {
                 final ApplicationInfo ai = mPackageManager.getApplicationInfoAsUser(packageName,
                         PackageManager.GET_META_DATA, userId);
@@ -647,6 +585,13 @@
             return xmlFound;
         }
 
+        GameModeConfiguration getOrAddDefaultGameModeConfiguration(int gameMode) {
+            synchronized (mModeConfigLock) {
+                mModeConfigs.putIfAbsent(gameMode, new GameModeConfiguration(gameMode));
+                return mModeConfigs.get(gameMode);
+            }
+        }
+
         /**
          * GameModeConfiguration contains all the values for all the interventions associated with
          * a game mode.
@@ -657,17 +602,26 @@
             public static final String MODE_KEY = "mode";
             public static final String SCALING_KEY = "downscaleFactor";
             public static final String FPS_KEY = "fps";
-            public static final String DEFAULT_SCALING = "1.0";
-            public static final String DEFAULT_FPS = "";
             public static final String ANGLE_KEY = "useAngle";
             public static final String LOADING_BOOST_KEY = "loadingBoost";
 
+            public static final float DEFAULT_SCALING = -1f;
+            public static final String DEFAULT_FPS = "";
+            public static final boolean DEFAULT_USE_ANGLE = false;
+            public static final int DEFAULT_LOADING_BOOST_DURATION = -1;
+
             private final @GameMode int mGameMode;
-            private String mScaling;
-            private String mFps;
+            private float mScaling = DEFAULT_SCALING;
+            private String mFps = DEFAULT_FPS;
             private final boolean mUseAngle;
             private final int mLoadingBoostDuration;
 
+            GameModeConfiguration(int gameMode) {
+                mGameMode = gameMode;
+                mUseAngle = DEFAULT_USE_ANGLE;
+                mLoadingBoostDuration = DEFAULT_LOADING_BOOST_DURATION;
+            }
+
             GameModeConfiguration(KeyValueListParser parser) {
                 mGameMode = parser.getInt(MODE_KEY, GameManager.GAME_MODE_UNSUPPORTED);
                 // isGameModeOptedIn() returns if an app will handle all of the changes necessary
@@ -675,7 +629,7 @@
                 // GameManagerService) will not do anything for the app (like window scaling or
                 // using ANGLE).
                 mScaling = !mAllowDownscale || willGamePerformOptimizations(mGameMode)
-                        ? DEFAULT_SCALING : parser.getString(SCALING_KEY, DEFAULT_SCALING);
+                        ? DEFAULT_SCALING : parser.getFloat(SCALING_KEY, DEFAULT_SCALING);
 
                 mFps = mAllowFpsOverride && !willGamePerformOptimizations(mGameMode)
                         ? parser.getString(FPS_KEY, DEFAULT_FPS) : DEFAULT_FPS;
@@ -684,21 +638,22 @@
                 // - The app has not opted in to performing the work itself AND
                 // - The Phenotype config has enabled it.
                 mUseAngle = mAllowAngle && !willGamePerformOptimizations(mGameMode)
-                        && parser.getBoolean(ANGLE_KEY, false);
+                        && parser.getBoolean(ANGLE_KEY, DEFAULT_USE_ANGLE);
 
-                mLoadingBoostDuration = willGamePerformOptimizations(mGameMode) ? -1
-                        : parser.getInt(LOADING_BOOST_KEY, -1);
+                mLoadingBoostDuration = willGamePerformOptimizations(mGameMode)
+                        ? DEFAULT_LOADING_BOOST_DURATION
+                        : parser.getInt(LOADING_BOOST_KEY, DEFAULT_LOADING_BOOST_DURATION);
             }
 
             public int getGameMode() {
                 return mGameMode;
             }
 
-            public String getScaling() {
+            public synchronized float getScaling() {
                 return mScaling;
             }
 
-            public int getFps() {
+            public synchronized int getFps() {
                 return GameManagerService.getFpsInt(mFps);
             }
 
@@ -710,15 +665,15 @@
                 return mLoadingBoostDuration;
             }
 
-            public void setScaling(String scaling) {
+            public synchronized void setScaling(float scaling) {
                 mScaling = scaling;
             }
 
-            public void setFpsStr(String fpsStr) {
+            public synchronized void setFpsStr(String fpsStr) {
                 mFps = fpsStr;
             }
 
-            public boolean isValid() {
+            public boolean isActive() {
                 return (mGameMode == GameManager.GAME_MODE_STANDARD
                         || mGameMode == GameManager.GAME_MODE_PERFORMANCE
                         || mGameMode == GameManager.GAME_MODE_BATTERY)
@@ -733,13 +688,6 @@
                         + mUseAngle + ",Fps:" + mFps + ",Loading Boost Duration:"
                         + mLoadingBoostDuration + "]";
             }
-
-            /**
-             * Get the corresponding compat change id for the current scaling string.
-             */
-            public long getCompatChangeId() {
-                return GameManagerService.getCompatChangeId(mScaling);
-            }
         }
 
         public String getPackageName() {
@@ -761,8 +709,10 @@
 
         private int getAvailableGameModesBitfield() {
             int field = 0;
-            for (final int mode : mModeConfigs.keySet()) {
-                field |= modeToBitmask(mode);
+            synchronized (mModeConfigLock) {
+                for (final int mode : mModeConfigs.keySet()) {
+                    field |= modeToBitmask(mode);
+                }
             }
             if (mBatteryModeOptedIn) {
                 field |= modeToBitmask(GameManager.GAME_MODE_BATTERY);
@@ -803,27 +753,43 @@
          * @return The package's GameModeConfiguration for the provided mode or null if absent
          */
         public GameModeConfiguration getGameModeConfiguration(@GameMode int gameMode) {
-            return mModeConfigs.get(gameMode);
+            synchronized (mModeConfigLock) {
+                return mModeConfigs.get(gameMode);
+            }
         }
 
         /**
-         * Insert a new GameModeConfiguration
+         * Inserts a new GameModeConfiguration
          */
         public void addModeConfig(GameModeConfiguration config) {
-            if (config.isValid()) {
-                mModeConfigs.put(config.getGameMode(), config);
+            if (config.isActive()) {
+                synchronized (mModeConfigLock) {
+                    mModeConfigs.put(config.getGameMode(), config);
+                }
             } else {
-                Slog.w(TAG, "Invalid game mode config for "
+                Slog.w(TAG, "Attempt to add inactive game mode config for "
                         + mPackageName + ":" + config.toString());
             }
         }
 
-        public boolean isValid() {
-            return mModeConfigs.size() > 0 || mBatteryModeOptedIn || mPerfModeOptedIn;
+        public boolean isActive() {
+            synchronized (mModeConfigLock) {
+                return mModeConfigs.size() > 0 || mBatteryModeOptedIn || mPerfModeOptedIn;
+            }
         }
 
         public String toString() {
-            return "[Name:" + mPackageName + " Modes: " + mModeConfigs.toString() + "]";
+            synchronized (mModeConfigLock) {
+                return "[Name:" + mPackageName + " Modes: " + mModeConfigs.toString() + "]";
+            }
+        }
+    }
+
+    private final class LocalService extends GameManagerInternal {
+        @Override
+        public float getResolutionScalingFactor(String packageName, int userId) {
+            final int gameMode = getGameModeFromSettings(packageName, userId);
+            return getResolutionScalingFactorInternal(packageName, gameMode, userId);
         }
     }
 
@@ -837,13 +803,13 @@
 
         public Lifecycle(Context context) {
             super(context);
+            mService = new GameManagerService(context);
         }
 
         @Override
         public void onStart() {
-            final Context context = getContext();
-            mService = new GameManagerService(context);
             publishBinderService(Context.GAME_SERVICE, mService);
+            mService.publishLocalService();
             mService.registerDeviceConfigListener();
             mService.registerPackageReceiver();
         }
@@ -894,15 +860,7 @@
     }
 
     private @GameMode int[] getAvailableGameModesUnchecked(String packageName) {
-        GamePackageConfiguration config = null;
-        synchronized (mOverrideConfigLock) {
-            config = mOverrideConfigs.get(packageName);
-        }
-        if (config == null) {
-            synchronized (mDeviceConfigLock) {
-                config = mConfigs.get(packageName);
-            }
-        }
+        final GamePackageConfiguration config = getConfig(packageName);
         if (config == null) {
             return new int[]{};
         }
@@ -933,8 +891,8 @@
     private @GameMode int getGameModeFromSettings(String packageName, @UserIdInt int userId) {
         synchronized (mLock) {
             if (!mSettings.containsKey(userId)) {
-                Slog.w(TAG, "User ID '" + userId + "' does not have a Game Mode"
-                        + " selected for package: '" + packageName + "'");
+                Slog.d(TAG, "User ID '" + userId + "' does not have a Game Mode"
+                            + " selected for package: '" + packageName + "'");
                 return GameManager.GAME_MODE_UNSUPPORTED;
             }
 
@@ -1055,19 +1013,19 @@
         if (gameMode == GameManager.GAME_MODE_UNSUPPORTED) {
             return false;
         }
-
+        final GamePackageConfiguration config;
         synchronized (mDeviceConfigLock) {
-            final GamePackageConfiguration config = mConfigs.get(packageName);
+            config = mConfigs.get(packageName);
             if (config == null) {
                 return false;
             }
-            GamePackageConfiguration.GameModeConfiguration gameModeConfiguration =
-                    config.getGameModeConfiguration(gameMode);
-            if (gameModeConfiguration == null) {
-                return false;
-            }
-            return gameModeConfiguration.getUseAngle();
         }
+        GamePackageConfiguration.GameModeConfiguration gameModeConfiguration =
+                config.getGameModeConfiguration(gameMode);
+        if (gameModeConfiguration == null) {
+            return false;
+        }
+        return gameModeConfiguration.getUseAngle();
     }
 
     /**
@@ -1082,19 +1040,19 @@
         if (gameMode == GameManager.GAME_MODE_UNSUPPORTED) {
             return -1;
         }
-
+        final GamePackageConfiguration config;
         synchronized (mDeviceConfigLock) {
-            final GamePackageConfiguration config = mConfigs.get(packageName);
-            if (config == null) {
-                return -1;
-            }
-            GamePackageConfiguration.GameModeConfiguration gameModeConfiguration =
-                    config.getGameModeConfiguration(gameMode);
-            if (gameModeConfiguration == null) {
-                return -1;
-            }
-            return gameModeConfiguration.getLoadingBoostDuration();
+            config = mConfigs.get(packageName);
         }
+        if (config == null) {
+            return -1;
+        }
+        GamePackageConfiguration.GameModeConfiguration gameModeConfiguration =
+                config.getGameModeConfiguration(gameMode);
+        if (gameModeConfiguration == null) {
+            return -1;
+        }
+        return gameModeConfiguration.getLoadingBoostDuration();
     }
 
     /**
@@ -1159,6 +1117,66 @@
         mGameServiceController.setGameServiceProvider(packageName);
     }
 
+
+    /**
+     * Updates the resolution scaling factor for the package's target game mode and activates it.
+     *
+     * @param scalingFactor enable scaling override over any other compat scaling if positive,
+     *                      or disable the override otherwise
+     * @throws SecurityException        if caller doesn't have
+     *                                  {@link android.Manifest.permission#MANAGE_GAME_MODE}
+     *                                  permission.
+     * @throws IllegalArgumentException if the user ID provided doesn't exist.
+     */
+    @Override
+    @RequiresPermission(Manifest.permission.MANAGE_GAME_MODE)
+    public void updateResolutionScalingFactor(String packageName, int gameMode, float scalingFactor,
+            int userId) throws SecurityException, IllegalArgumentException {
+        checkPermission(Manifest.permission.MANAGE_GAME_MODE);
+        synchronized (mLock) {
+            if (!mSettings.containsKey(userId)) {
+                throw new IllegalArgumentException("User " + userId + " wasn't started");
+            }
+        }
+        setGameModeConfigOverride(packageName, userId, gameMode, null /*fpsStr*/,
+                Float.toString(scalingFactor));
+    }
+
+    /**
+     * Gets the resolution scaling factor for the package's target game mode.
+     *
+     * @return scaling factor for the game mode if exists or negative value otherwise.
+     * @throws SecurityException        if caller doesn't have
+     *                                  {@link android.Manifest.permission#MANAGE_GAME_MODE}
+     *                                  permission.
+     * @throws IllegalArgumentException if the user ID provided doesn't exist.
+     */
+    @Override
+    @RequiresPermission(Manifest.permission.MANAGE_GAME_MODE)
+    public float getResolutionScalingFactor(String packageName, int gameMode, int userId)
+            throws SecurityException, IllegalArgumentException {
+        checkPermission(Manifest.permission.MANAGE_GAME_MODE);
+        synchronized (mLock) {
+            if (!mSettings.containsKey(userId)) {
+                throw new IllegalArgumentException("User " + userId + " wasn't started");
+            }
+        }
+        return getResolutionScalingFactorInternal(packageName, gameMode, userId);
+    }
+
+    float getResolutionScalingFactorInternal(String packageName, int gameMode, int userId) {
+        final GamePackageConfiguration packageConfig = getConfig(packageName);
+        if (packageConfig == null) {
+            return GamePackageConfiguration.GameModeConfiguration.DEFAULT_SCALING;
+        }
+        final GamePackageConfiguration.GameModeConfiguration modeConfig =
+                packageConfig.getGameModeConfiguration(gameMode);
+        if (modeConfig != null) {
+            return modeConfig.getScaling();
+        }
+        return GamePackageConfiguration.GameModeConfiguration.DEFAULT_SCALING;
+    }
+
     /**
      * Notified when boot is completed.
      */
@@ -1235,28 +1253,6 @@
     }
 
     /**
-     * @hide
-     */
-    @VisibleForTesting
-    public void disableCompatScale(String packageName) {
-        final long uid = Binder.clearCallingIdentity();
-        try {
-            Slog.i(TAG, "Disabling downscale for " + packageName);
-            final ArrayMap<Long, PackageOverride> overrides = new ArrayMap<>();
-            overrides.put(DOWNSCALED, COMPAT_DISABLED);
-            final CompatibilityOverrideConfig changeConfig = new CompatibilityOverrideConfig(
-                    overrides);
-            try {
-                mPlatformCompat.putOverridesOnReleaseBuilds(changeConfig, packageName);
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Failed to call IPlatformCompat#putOverridesOnReleaseBuilds", e);
-            }
-        } finally {
-            Binder.restoreCallingIdentity(uid);
-        }
-    }
-
-    /**
      * Remove frame rate override due to mode switch
      */
     private void resetFps(String packageName, @UserIdInt int userId) {
@@ -1269,60 +1265,6 @@
         }
     }
 
-    private void enableCompatScale(String packageName, long scaleId) {
-        final long uid = Binder.clearCallingIdentity();
-        try {
-            Slog.i(TAG, "Enabling downscale: " + scaleId + " for " + packageName);
-            final ArrayMap<Long, PackageOverride> overrides = new ArrayMap<>();
-            overrides.put(DOWNSCALED, COMPAT_ENABLED);
-            overrides.put(DOWNSCALE_30, COMPAT_DISABLED);
-            overrides.put(DOWNSCALE_35, COMPAT_DISABLED);
-            overrides.put(DOWNSCALE_40, COMPAT_DISABLED);
-            overrides.put(DOWNSCALE_45, COMPAT_DISABLED);
-            overrides.put(DOWNSCALE_50, COMPAT_DISABLED);
-            overrides.put(DOWNSCALE_55, COMPAT_DISABLED);
-            overrides.put(DOWNSCALE_60, COMPAT_DISABLED);
-            overrides.put(DOWNSCALE_65, COMPAT_DISABLED);
-            overrides.put(DOWNSCALE_70, COMPAT_DISABLED);
-            overrides.put(DOWNSCALE_75, COMPAT_DISABLED);
-            overrides.put(DOWNSCALE_80, COMPAT_DISABLED);
-            overrides.put(DOWNSCALE_85, COMPAT_DISABLED);
-            overrides.put(DOWNSCALE_90, COMPAT_DISABLED);
-            overrides.put(scaleId, COMPAT_ENABLED);
-            final CompatibilityOverrideConfig changeConfig = new CompatibilityOverrideConfig(
-                    overrides);
-            try {
-                mPlatformCompat.putOverridesOnReleaseBuilds(changeConfig, packageName);
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Failed to call IPlatformCompat#putOverridesOnReleaseBuilds", e);
-            }
-        } finally {
-            Binder.restoreCallingIdentity(uid);
-        }
-    }
-
-    private void updateCompatModeDownscale(GamePackageConfiguration packageConfig,
-            String packageName, @GameMode int gameMode) {
-
-        if (DEBUG) {
-            Slog.v(TAG, dumpDeviceConfigs());
-        }
-        final GamePackageConfiguration.GameModeConfiguration modeConfig =
-                packageConfig.getGameModeConfiguration(gameMode);
-        if (modeConfig == null) {
-            Slog.i(TAG, "Game mode " + gameMode + " not found for " + packageName);
-            return;
-        }
-        long scaleId = modeConfig.getCompatChangeId();
-        if (scaleId == 0) {
-            Slog.w(TAG, "Invalid downscaling change id " + scaleId + " for "
-                    + packageName);
-            return;
-        }
-
-        enableCompatScale(packageName, scaleId);
-    }
-
     private int modeToBitmask(@GameMode int gameMode) {
         return (1 << gameMode);
     }
@@ -1360,31 +1302,17 @@
             @GameMode int gameMode, @UserIdInt int userId) {
         if (gameMode == GameManager.GAME_MODE_STANDARD
                 || gameMode == GameManager.GAME_MODE_UNSUPPORTED) {
-            disableCompatScale(packageName);
             resetFps(packageName, userId);
             return;
         }
-        GamePackageConfiguration packageConfig = null;
-
-        synchronized (mOverrideConfigLock) {
-            packageConfig = mOverrideConfigs.get(packageName);
-        }
-
+        final GamePackageConfiguration packageConfig = getConfig(packageName);
         if (packageConfig == null) {
-            synchronized (mDeviceConfigLock) {
-                packageConfig = mConfigs.get(packageName);
-            }
-        }
-
-        if (packageConfig == null) {
-            disableCompatScale(packageName);
             Slog.v(TAG, "Package configuration not found for " + packageName);
             return;
         }
         if (packageConfig.willGamePerformOptimizations(gameMode)) {
             return;
         }
-        updateCompatModeDownscale(packageConfig, packageName, gameMode);
         updateFps(packageConfig, packageName, gameMode, userId);
         updateUseAngle(packageName, gameMode);
     }
@@ -1404,34 +1332,31 @@
             }
         }
         // Adding override game mode configuration of the given package name
+        GamePackageConfiguration overrideConfig;
         synchronized (mOverrideConfigLock) {
             // look for the existing override GamePackageConfiguration
-            GamePackageConfiguration overrideConfig = mOverrideConfigs.get(packageName);
+            overrideConfig = mOverrideConfigs.get(packageName);
             if (overrideConfig == null) {
                 overrideConfig = new GamePackageConfiguration(packageName, userId);
                 mOverrideConfigs.put(packageName, overrideConfig);
             }
-
-            // modify GameModeConfiguration intervention settings
-            GamePackageConfiguration.GameModeConfiguration overrideModeConfig =
-                    overrideConfig.getGameModeConfiguration(gameMode);
-
-            if (fpsStr != null) {
-                overrideModeConfig.setFpsStr(fpsStr);
-            } else {
-                overrideModeConfig.setFpsStr(
-                        GamePackageConfiguration.GameModeConfiguration.DEFAULT_FPS);
-            }
-            if (scaling != null) {
-                overrideModeConfig.setScaling(scaling);
-            } else {
-                overrideModeConfig.setScaling(
-                        GamePackageConfiguration.GameModeConfiguration.DEFAULT_SCALING);
-            }
-            Slog.i(TAG, "Package Name: " + packageName
-                    + " FPS: " + String.valueOf(overrideModeConfig.getFps())
-                    + " Scaling: " + overrideModeConfig.getScaling());
         }
+        // modify GameModeConfiguration intervention settings
+        GamePackageConfiguration.GameModeConfiguration overrideModeConfig =
+                overrideConfig.getOrAddDefaultGameModeConfiguration(gameMode);
+
+        if (fpsStr != null) {
+            overrideModeConfig.setFpsStr(fpsStr);
+        } else {
+            overrideModeConfig.setFpsStr(
+                    GamePackageConfiguration.GameModeConfiguration.DEFAULT_FPS);
+        }
+        if (scaling != null) {
+            overrideModeConfig.setScaling(Float.parseFloat(scaling));
+        }
+        Slog.i(TAG, "Package Name: " + packageName
+                + " FPS: " + String.valueOf(overrideModeConfig.getFps())
+                + " Scaling: " + overrideModeConfig.getScaling());
         setGameMode(packageName, gameMode, userId);
     }
 
@@ -1477,7 +1402,7 @@
             }
 
             // If the game mode to reset is the only mode other than standard mode,
-            // The override config is removed.
+            // the override config is removed.
             if (modes.length <= 2) {
                 synchronized (mOverrideConfigLock) {
                     mOverrideConfigs.remove(packageName);
@@ -1497,15 +1422,7 @@
         // If not, set the game mode to standard
         int gameMode = getGameMode(packageName, userId);
 
-        GamePackageConfiguration config = null;
-        synchronized (mOverrideConfigLock) {
-            config = mOverrideConfigs.get(packageName);
-        }
-        if (config == null) {
-            synchronized (mDeviceConfigLock) {
-                config = mConfigs.get(packageName);
-            }
-        }
+        final GamePackageConfiguration config = getConfig(packageName);
         final int newGameMode = getNewGameMode(gameMode, config);
         if (gameMode != newGameMode) {
             setGameMode(packageName, GameManager.GAME_MODE_STANDARD, userId);
@@ -1544,18 +1461,8 @@
      * Returns the string listing all the interventions currently set to a game.
      */
     public String getInterventionList(String packageName) {
-        GamePackageConfiguration packageConfig = null;
-        synchronized (mOverrideConfigLock) {
-            packageConfig = mOverrideConfigs.get(packageName);
-        }
-
-        if (packageConfig == null) {
-            synchronized (mDeviceConfigLock) {
-                packageConfig = mConfigs.get(packageName);
-            }
-        }
-
-        StringBuilder listStrSb = new StringBuilder();
+        final GamePackageConfiguration packageConfig = getConfig(packageName);
+        final StringBuilder listStrSb = new StringBuilder();
         if (packageConfig == null) {
             listStrSb.append("\n No intervention found for package ")
                     .append(packageName);
@@ -1570,20 +1477,27 @@
      * @hide
      */
     @VisibleForTesting
-    void updateConfigsForUser(@UserIdInt int userId, String... packageNames) {
+    void updateConfigsForUser(@UserIdInt int userId, boolean checkGamePackage,
+            String... packageNames) {
+        if (checkGamePackage) {
+            packageNames = Arrays.stream(packageNames).filter(
+                    p -> isPackageGame(p, userId)).toArray(String[]::new);
+        }
         try {
             synchronized (mDeviceConfigLock) {
                 for (final String packageName : packageNames) {
                     final GamePackageConfiguration config =
                             new GamePackageConfiguration(packageName, userId);
-                    if (config.isValid()) {
+                    if (config.isActive()) {
                         if (DEBUG) {
                             Slog.i(TAG, "Adding config: " + config.toString());
                         }
                         mConfigs.put(packageName, config);
                     } else {
-                        Slog.w(TAG, "Invalid package config for "
-                                + config.getPackageName() + ":" + config.toString());
+                        if (DEBUG) {
+                            Slog.w(TAG, "Inactive package config for "
+                                    + config.getPackageName() + ":" + config.toString());
+                        }
                         mConfigs.remove(packageName);
                     }
                 }
@@ -1613,7 +1527,7 @@
                 }
             }
         } catch (Exception e) {
-            Slog.e(TAG, "Failed to update compat modes for user " + userId + ": " + e);
+            Slog.e(TAG, "Failed to update configs for user " + userId + ": " + e);
         }
 
         final Message msg = mHandler.obtainMessage(WRITE_GAME_MODE_INTERVENTION_LIST_FILE);
@@ -1664,7 +1578,7 @@
                     final int useAngle = gameModeConfiguration.getUseAngle() ? 1 : 0;
                     sb.append(TextUtils.formatSimple("angle=%d", useAngle));
                     sb.append(",");
-                    final String scaling = gameModeConfiguration.getScaling();
+                    final float scaling = gameModeConfiguration.getScaling();
                     sb.append("scaling=");
                     sb.append(scaling);
                     sb.append(",");
@@ -1761,17 +1675,9 @@
                     }
                     switch (intent.getAction()) {
                         case ACTION_PACKAGE_ADDED:
-                            updateConfigsForUser(userId, packageName);
+                            updateConfigsForUser(userId, true /*checkGamePackage*/, packageName);
                             break;
                         case ACTION_PACKAGE_REMOVED:
-                            disableCompatScale(packageName);
-                            // If EXTRA_REPLACING is true, it means there will be an
-                            // ACTION_PACKAGE_ADDED triggered after this because this
-                            // is an updated package that gets installed. Hence, disable
-                            // resolution downscaling effort but avoid removing the server
-                            // or commandline overriding configurations because those will
-                            // not change but the package game mode configurations may change
-                            // which may opt in and/or opt out some game mode configurations.
                             if (!intent.getBooleanExtra(EXTRA_REPLACING, false)) {
                                 synchronized (mOverrideConfigLock) {
                                     mOverrideConfigs.remove(packageName);
@@ -1803,6 +1709,10 @@
         mDeviceConfigListener = new DeviceConfigListener();
     }
 
+    private void publishLocalService() {
+        LocalServices.addService(GameManagerInternal.class, new LocalService());
+    }
+
     private String dumpDeviceConfigs() {
         StringBuilder out = new StringBuilder();
         for (String key : mConfigs.keySet()) {
diff --git a/services/core/java/com/android/server/app/GameManagerShellCommand.java b/services/core/java/com/android/server/app/GameManagerShellCommand.java
index 470c320..487d19a 100644
--- a/services/core/java/com/android/server/app/GameManagerShellCommand.java
+++ b/services/core/java/com/android/server/app/GameManagerShellCommand.java
@@ -16,21 +16,6 @@
 
 package com.android.server.app;
 
-import static com.android.server.wm.CompatModePackages.DOWNSCALED;
-import static com.android.server.wm.CompatModePackages.DOWNSCALE_30;
-import static com.android.server.wm.CompatModePackages.DOWNSCALE_35;
-import static com.android.server.wm.CompatModePackages.DOWNSCALE_40;
-import static com.android.server.wm.CompatModePackages.DOWNSCALE_45;
-import static com.android.server.wm.CompatModePackages.DOWNSCALE_50;
-import static com.android.server.wm.CompatModePackages.DOWNSCALE_55;
-import static com.android.server.wm.CompatModePackages.DOWNSCALE_60;
-import static com.android.server.wm.CompatModePackages.DOWNSCALE_65;
-import static com.android.server.wm.CompatModePackages.DOWNSCALE_70;
-import static com.android.server.wm.CompatModePackages.DOWNSCALE_75;
-import static com.android.server.wm.CompatModePackages.DOWNSCALE_80;
-import static com.android.server.wm.CompatModePackages.DOWNSCALE_85;
-import static com.android.server.wm.CompatModePackages.DOWNSCALE_90;
-
 import android.app.ActivityManager;
 import android.app.GameManager;
 import android.app.IGameManagerService;
@@ -39,7 +24,6 @@
 import android.os.ServiceManager;
 import android.os.ServiceManager.ServiceNotFoundException;
 import android.os.ShellCommand;
-import android.util.ArraySet;
 
 import java.io.PrintWriter;
 import java.util.Locale;
@@ -53,23 +37,6 @@
 
     public GameManagerShellCommand() {}
 
-    private static final ArraySet<Long> DOWNSCALE_CHANGE_IDS = new ArraySet<>(new Long[]{
-            DOWNSCALED,
-            DOWNSCALE_90,
-            DOWNSCALE_85,
-            DOWNSCALE_80,
-            DOWNSCALE_75,
-            DOWNSCALE_70,
-            DOWNSCALE_65,
-            DOWNSCALE_60,
-            DOWNSCALE_55,
-            DOWNSCALE_50,
-            DOWNSCALE_45,
-            DOWNSCALE_40,
-            DOWNSCALE_35,
-            DOWNSCALE_30,
-    });
-
     @Override
     public int onCommand(String cmd) {
         if (cmd == null) {
@@ -212,11 +179,15 @@
                 case "--downscale":
                     if (downscaleRatio == null) {
                         downscaleRatio = getNextArgRequired();
-                        if (downscaleRatio != null
-                                && GameManagerService.getCompatChangeId(downscaleRatio) == 0
-                                && !downscaleRatio.equals("disable")) {
-                            pw.println("Invalid scaling ratio '" + downscaleRatio + "'");
-                            return -1;
+                        if ("disable".equals(downscaleRatio)) {
+                            downscaleRatio = "-1";
+                        } else {
+                            try {
+                                Float.parseFloat(downscaleRatio);
+                            } catch (NumberFormatException e) {
+                                pw.println("Invalid scaling ratio '" + downscaleRatio + "'");
+                                return -1;
+                            }
                         }
                     } else {
                         pw.println("Duplicate option '" + option + "'");
@@ -249,40 +220,16 @@
         final GameManagerService gameManagerService = (GameManagerService)
                 ServiceManager.getService(Context.GAME_SERVICE);
 
-        boolean batteryModeSupported = false;
-        boolean perfModeSupported = false;
-        int [] modes = gameManagerService.getAvailableGameModes(packageName);
-
-        for (int mode : modes) {
-            if (mode == GameManager.GAME_MODE_PERFORMANCE) {
-                perfModeSupported = true;
-            } else if (mode == GameManager.GAME_MODE_BATTERY) {
-                batteryModeSupported = true;
-            }
-        }
-
         switch (gameMode.toLowerCase(Locale.getDefault())) {
             case "2":
             case "performance":
-                if (perfModeSupported) {
-                    gameManagerService.setGameModeConfigOverride(packageName, userId,
-                            GameManager.GAME_MODE_PERFORMANCE, fpsStr, downscaleRatio);
-                } else {
-                    pw.println("Game mode: " + gameMode + " not supported by "
-                            + packageName);
-                    return -1;
-                }
+                gameManagerService.setGameModeConfigOverride(packageName, userId,
+                        GameManager.GAME_MODE_PERFORMANCE, fpsStr, downscaleRatio);
                 break;
             case "3":
             case "battery":
-                if (batteryModeSupported) {
-                    gameManagerService.setGameModeConfigOverride(packageName, userId,
-                            GameManager.GAME_MODE_BATTERY, fpsStr, downscaleRatio);
-                } else {
-                    pw.println("Game mode: " + gameMode + " not supported by "
-                            + packageName);
-                    return -1;
-                }
+                gameManagerService.setGameModeConfigOverride(packageName, userId,
+                        GameManager.GAME_MODE_BATTERY, fpsStr, downscaleRatio);
                 break;
             default:
                 pw.println("Invalid game mode: " + gameMode);
diff --git a/services/core/java/com/android/server/app/GameTaskInfoProvider.java b/services/core/java/com/android/server/app/GameTaskInfoProvider.java
index f078d98..25a4f37 100644
--- a/services/core/java/com/android/server/app/GameTaskInfoProvider.java
+++ b/services/core/java/com/android/server/app/GameTaskInfoProvider.java
@@ -16,6 +16,8 @@
 
 package com.android.server.app;
 
+import static android.view.Display.INVALID_DISPLAY;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityManager.RunningTaskInfo;
@@ -93,7 +95,8 @@
             runningTaskInfos = mActivityTaskManager.getTasks(
                     /* maxNum= */ Integer.MAX_VALUE,
                     /* filterOnlyVisibleRecents= */ false,
-                    /* keepIntentExtra= */ false);
+                    /* keepIntentExtra= */ false,
+                    INVALID_DISPLAY);
         } catch (RemoteException ex) {
             Slog.w(TAG, "Failed to fetch running tasks");
             return null;
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 7bef6d5..a5bcb05 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -217,7 +217,7 @@
 import java.util.concurrent.ThreadLocalRandom;
 import java.util.function.Consumer;
 
-public class AppOpsService extends IAppOpsService.Stub {
+public class AppOpsService extends IAppOpsService.Stub implements PersistenceScheduler {
     static final String TAG = "AppOps";
     static final boolean DEBUG = false;
 
@@ -402,6 +402,9 @@
     /** Package Manager internal. Access via {@link #getPackageManagerInternal()} */
     private @Nullable PackageManagerInternal mPackageManagerInternal;
 
+    /** Interface for app-op modes.*/
+    @VisibleForTesting AppOpsServiceInterface mAppOpsServiceInterface;
+
     /**
      * An unsynchronized pool of {@link OpEventProxyInfo} objects.
      */
@@ -558,7 +561,6 @@
         public boolean pendingAppWidgetVisible;
 
         public ArrayMap<String, Ops> pkgOps;
-        public SparseIntArray opModes;
 
         // true indicates there is an interested observer, false there isn't but it has such an op
         public SparseBooleanArray foregroundOps;
@@ -569,15 +571,43 @@
         }
 
         public void clear() {
+            mAppOpsServiceInterface.removeUid(uid);
+            if (pkgOps != null) {
+                for (String packageName : pkgOps.keySet()) {
+                    mAppOpsServiceInterface.removePackage(packageName);
+                }
+            }
             pkgOps = null;
-            opModes = null;
         }
 
         public boolean isDefault() {
+            boolean areAllPackageModesDefault = true;
+            if (pkgOps != null) {
+                for (String packageName : pkgOps.keySet()) {
+                    if (!mAppOpsServiceInterface.arePackageModesDefault(packageName)) {
+                        areAllPackageModesDefault = false;
+                        break;
+                    }
+                }
+            }
             return (pkgOps == null || pkgOps.isEmpty())
-                    && (opModes == null || opModes.size() <= 0)
                     && (state == UID_STATE_CACHED
-                    && (pendingState == UID_STATE_CACHED));
+                    && (pendingState == UID_STATE_CACHED))
+                    && (mAppOpsServiceInterface.areUidModesDefault(uid)
+                    && areAllPackageModesDefault);
+        }
+
+        // Functions for uid mode access and manipulation.
+        public SparseIntArray getNonDefaultUidModes() {
+            return mAppOpsServiceInterface.getNonDefaultUidModes(uid);
+        }
+
+        public int getUidMode(int op) {
+            return mAppOpsServiceInterface.getUidMode(uid, op);
+        }
+
+        public boolean setUidMode(int op, int mode) {
+            return mAppOpsServiceInterface.setUidMode(uid, op, mode);
         }
 
         int evalMode(int op, int mode) {
@@ -647,21 +677,20 @@
         public void evalForegroundOps(SparseArray<ArraySet<ModeCallback>> watchers) {
             SparseBooleanArray which = null;
             hasForegroundWatchers = false;
-            if (opModes != null) {
-                for (int i = opModes.size() - 1; i >= 0; i--) {
-                    if (opModes.valueAt(i) == AppOpsManager.MODE_FOREGROUND) {
-                        if (which == null) {
-                            which = new SparseBooleanArray();
-                        }
-                        evalForegroundWatchers(opModes.keyAt(i), watchers, which);
+            final SparseIntArray opModes = getNonDefaultUidModes();
+            for (int i = opModes.size() - 1; i >= 0; i--) {
+                if (opModes.valueAt(i) == AppOpsManager.MODE_FOREGROUND) {
+                    if (which == null) {
+                        which = new SparseBooleanArray();
                     }
+                    evalForegroundWatchers(opModes.keyAt(i), watchers, which);
                 }
             }
             if (pkgOps != null) {
                 for (int i = pkgOps.size() - 1; i >= 0; i--) {
                     Ops ops = pkgOps.valueAt(i);
                     for (int j = ops.size() - 1; j >= 0; j--) {
-                        if (ops.valueAt(j).mode == AppOpsManager.MODE_FOREGROUND) {
+                        if (ops.valueAt(j).getMode() == AppOpsManager.MODE_FOREGROUND) {
                             if (which == null) {
                                 which = new SparseBooleanArray();
                             }
@@ -1451,8 +1480,6 @@
         final UidState uidState;
         final @NonNull String packageName;
 
-        private @Mode int mode;
-
         /** attributionTag -> AttributedOp */
         final ArrayMap<String, AttributedOp> mAttributions = new ArrayMap<>(1);
 
@@ -1461,15 +1488,17 @@
             this.uid = uid;
             this.uidState = uidState;
             this.packageName = packageName;
-            this.mode = AppOpsManager.opToDefaultMode(op);
         }
 
-        int getMode() {
-            return mode;
+        @Mode int getMode() {
+            return mAppOpsServiceInterface.getPackageMode(packageName, this.op);
+        }
+        void setMode(@Mode int mode) {
+            mAppOpsServiceInterface.setPackageMode(packageName, this.op, mode);
         }
 
         int evalMode() {
-            return uidState.evalMode(op, mode);
+            return uidState.evalMode(op, getMode());
         }
 
         void removeAttributionsWithNoTime() {
@@ -1503,7 +1532,7 @@
                         mAttributions.valueAt(i).createAttributedOpEntryLocked());
             }
 
-            return new OpEntry(op, mode, attributionEntries);
+            return new OpEntry(op, getMode(), attributionEntries);
         }
 
         @NonNull OpEntry createSingleAttributionEntryLocked(@Nullable String attributionTag) {
@@ -1518,7 +1547,7 @@
                 }
             }
 
-            return new OpEntry(op, mode, attributionEntries);
+            return new OpEntry(op, getMode(), attributionEntries);
         }
 
         boolean isRunning() {
@@ -1775,6 +1804,7 @@
 
     public AppOpsService(File storagePath, Handler handler, Context context) {
         mContext = context;
+        mAppOpsServiceInterface = new LegacyAppOpsServiceInterfaceImpl(this, this);
 
         LockGuard.installLock(this, LockGuard.INDEX_APP_OPS);
         mFile = new AtomicFile(storagePath, "appops");
@@ -1815,7 +1845,7 @@
                     if (uidState == null || uidState.pkgOps == null) {
                         return;
                     }
-
+                    mAppOpsServiceInterface.removePackage(pkgName);
                     Ops removedOps = uidState.pkgOps.remove(pkgName);
                     if (removedOps != null) {
                         scheduleFastWriteLocked();
@@ -2037,25 +2067,27 @@
                 return;
             }
 
-            Ops ops = null;
+            Ops removedOps = null;
 
             // Remove any package state if such.
             if (uidState.pkgOps != null) {
-                ops = uidState.pkgOps.remove(packageName);
+                removedOps = uidState.pkgOps.remove(packageName);
+                mAppOpsServiceInterface.removePackage(packageName);
             }
 
             // If we just nuked the last package state check if the UID is valid.
-            if (ops != null && uidState.pkgOps.isEmpty()
+            if (removedOps != null && uidState.pkgOps.isEmpty()
                     && getPackagesForUid(uid).length <= 0) {
+                uidState.clear();
                 mUidStates.remove(uid);
             }
 
-            if (ops != null) {
+            if (removedOps != null) {
                 scheduleFastWriteLocked();
 
-                final int numOps = ops.size();
+                final int numOps = removedOps.size();
                 for (int opNum = 0; opNum < numOps; opNum++) {
-                    final Op op = ops.valueAt(opNum);
+                    final Op op = removedOps.valueAt(opNum);
 
                     final int numAttributions = op.mAttributions.size();
                     for (int attributionNum = 0; attributionNum < numAttributions;
@@ -2080,6 +2112,7 @@
     public void uidRemoved(int uid) {
         synchronized (this) {
             if (mUidStates.indexOfKey(uid) >= 0) {
+                mUidStates.get(uid).clear();
                 mUidStates.remove(uid);
                 scheduleFastWriteLocked();
             }
@@ -2185,12 +2218,11 @@
 
     private ArrayList<AppOpsManager.OpEntry> collectOps(Ops pkgOps, int[] ops) {
         ArrayList<AppOpsManager.OpEntry> resOps = null;
-        final long elapsedNow = SystemClock.elapsedRealtime();
         if (ops == null) {
             resOps = new ArrayList<>();
             for (int j=0; j<pkgOps.size(); j++) {
                 Op curOp = pkgOps.valueAt(j);
-                resOps.add(getOpEntryForResult(curOp, elapsedNow));
+                resOps.add(getOpEntryForResult(curOp));
             }
         } else {
             for (int j=0; j<ops.length; j++) {
@@ -2199,7 +2231,7 @@
                     if (resOps == null) {
                         resOps = new ArrayList<>();
                     }
-                    resOps.add(getOpEntryForResult(curOp, elapsedNow));
+                    resOps.add(getOpEntryForResult(curOp));
                 }
             }
         }
@@ -2209,11 +2241,12 @@
     @Nullable
     private ArrayList<AppOpsManager.OpEntry> collectUidOps(@NonNull UidState uidState,
             @Nullable int[] ops) {
-        if (uidState.opModes == null) {
+        final SparseIntArray opModes = uidState.getNonDefaultUidModes();
+        if (opModes == null) {
             return null;
         }
 
-        int opModeCount = uidState.opModes.size();
+        int opModeCount = opModes.size();
         if (opModeCount == 0) {
             return null;
         }
@@ -2221,25 +2254,24 @@
         if (ops == null) {
             resOps = new ArrayList<>();
             for (int i = 0; i < opModeCount; i++) {
-                int code = uidState.opModes.keyAt(i);
-                resOps.add(new OpEntry(code, uidState.opModes.get(code), Collections.emptyMap()));
+                int code = opModes.keyAt(i);
+                resOps.add(new OpEntry(code, opModes.get(code), Collections.emptyMap()));
             }
         } else {
             for (int j=0; j<ops.length; j++) {
                 int code = ops[j];
-                if (uidState.opModes.indexOfKey(code) >= 0) {
+                if (opModes.indexOfKey(code) >= 0) {
                     if (resOps == null) {
                         resOps = new ArrayList<>();
                     }
-                    resOps.add(new OpEntry(code, uidState.opModes.get(code),
-                            Collections.emptyMap()));
+                    resOps.add(new OpEntry(code, opModes.get(code), Collections.emptyMap()));
                 }
             }
         }
         return resOps;
     }
 
-    private static @NonNull OpEntry getOpEntryForResult(@NonNull Op op, long elapsedNow) {
+    private static @NonNull OpEntry getOpEntryForResult(@NonNull Op op) {
         return op.createEntryLocked();
     }
 
@@ -2484,15 +2516,18 @@
             Ops ops = getOpsLocked(uid, packageName, null, false, null, /* edit */ false);
             if (ops != null) {
                 ops.remove(op.op);
+                op.setMode(AppOpsManager.opToDefaultMode(op.op));
                 if (ops.size() <= 0) {
                     UidState uidState = ops.uidState;
                     ArrayMap<String, Ops> pkgOps = uidState.pkgOps;
                     if (pkgOps != null) {
                         pkgOps.remove(ops.packageName);
+                        mAppOpsServiceInterface.removePackage(ops.packageName);
                         if (pkgOps.isEmpty()) {
                             uidState.pkgOps = null;
                         }
                         if (uidState.isDefault()) {
+                            uidState.clear();
                             mUidStates.remove(uid);
                         }
                     }
@@ -2548,33 +2583,18 @@
                 if (mode == defaultMode) {
                     return;
                 }
-                previousMode = MODE_DEFAULT;
                 uidState = new UidState(uid);
-                uidState.opModes = new SparseIntArray();
-                uidState.opModes.put(code, mode);
                 mUidStates.put(uid, uidState);
-                scheduleWriteLocked();
-            } else if (uidState.opModes == null) {
-                previousMode = MODE_DEFAULT;
-                if (mode != defaultMode) {
-                    uidState.opModes = new SparseIntArray();
-                    uidState.opModes.put(code, mode);
-                    scheduleWriteLocked();
-                }
+            }
+            if (uidState.getUidMode(code) != AppOpsManager.opToDefaultMode(code)) {
+                previousMode = uidState.getUidMode(code);
             } else {
-                if (uidState.opModes.indexOfKey(code) >= 0 && uidState.opModes.get(code) == mode) {
-                    return;
-                }
-                previousMode = uidState.opModes.get(code);
-                if (mode == defaultMode) {
-                    uidState.opModes.delete(code);
-                    if (uidState.opModes.size() <= 0) {
-                        uidState.opModes = null;
-                    }
-                } else {
-                    uidState.opModes.put(code, mode);
-                }
-                scheduleWriteLocked();
+                // doesn't look right but is legacy behavior.
+                previousMode = MODE_DEFAULT;
+            }
+
+            if (!uidState.setUidMode(code, mode)) {
+                return;
             }
             uidState.evalForegroundOps(mOpModeWatchers);
             if (mode != MODE_ERRORED && mode != previousMode) {
@@ -2806,9 +2826,10 @@
             UidState uidState = getUidStateLocked(uid, false);
             Op op = getOpLocked(code, uid, packageName, null, false, pvr.bypass, /* edit */ true);
             if (op != null) {
-                if (op.mode != mode) {
-                    previousMode = op.mode;
-                    op.mode = mode;
+                if (op.getMode() != mode) {
+                    previousMode = op.getMode();
+                    op.setMode(mode);
+
                     if (uidState != null) {
                         uidState.evalForegroundOps(mOpModeWatchers);
                     }
@@ -2976,17 +2997,14 @@
             for (int i = mUidStates.size() - 1; i >= 0; i--) {
                 UidState uidState = mUidStates.valueAt(i);
 
-                SparseIntArray opModes = uidState.opModes;
+                SparseIntArray opModes = uidState.getNonDefaultUidModes();
                 if (opModes != null && (uidState.uid == reqUid || reqUid == -1)) {
                     final int uidOpCount = opModes.size();
                     for (int j = uidOpCount - 1; j >= 0; j--) {
                         final int code = opModes.keyAt(j);
                         if (AppOpsManager.opAllowsReset(code)) {
                             int previousMode = opModes.valueAt(j);
-                            opModes.removeAt(j);
-                            if (opModes.size() <= 0) {
-                                uidState.opModes = null;
-                            }
+                            uidState.setUidMode(code, AppOpsManager.opToDefaultMode(code));
                             for (String packageName : getPackagesForUid(uidState.uid)) {
                                 callbacks = addCallbacks(callbacks, code, uidState.uid, packageName,
                                         previousMode, mOpModeWatchers.get(code));
@@ -3028,9 +3046,9 @@
                             continue;
                         }
                         if (AppOpsManager.opAllowsReset(curOp.op)
-                                && curOp.mode != AppOpsManager.opToDefaultMode(curOp.op)) {
-                            int previousMode = curOp.mode;
-                            curOp.mode = AppOpsManager.opToDefaultMode(curOp.op);
+                                && curOp.getMode() != AppOpsManager.opToDefaultMode(curOp.op)) {
+                            int previousMode = curOp.getMode();
+                            curOp.setMode(AppOpsManager.opToDefaultMode(curOp.op));
                             changed = true;
                             uidChanged = true;
                             final int uid = curOp.uidState.uid;
@@ -3049,9 +3067,11 @@
                     }
                     if (pkgOps.size() == 0) {
                         it.remove();
+                        mAppOpsServiceInterface.removePackage(packageName);
                     }
                 }
                 if (uidState.isDefault()) {
+                    uidState.clear();
                     mUidStates.remove(uidState.uid);
                 }
                 if (uidChanged) {
@@ -3267,16 +3287,16 @@
             }
             code = AppOpsManager.opToSwitch(code);
             UidState uidState = getUidStateLocked(uid, false);
-            if (uidState != null && uidState.opModes != null
-                    && uidState.opModes.indexOfKey(code) >= 0) {
-                final int rawMode = uidState.opModes.get(code);
+            if (uidState != null
+                    && uidState.getUidMode(code) != AppOpsManager.opToDefaultMode(code)) {
+                final int rawMode = uidState.getUidMode(code);
                 return raw ? rawMode : uidState.evalMode(code, rawMode);
             }
             Op op = getOpLocked(code, uid, packageName, null, false, pvr.bypass, /* edit */ false);
             if (op == null) {
                 return AppOpsManager.opToDefaultMode(code);
             }
-            return raw ? op.mode : op.evalMode();
+            return raw ? op.getMode() : op.evalMode();
         }
     }
 
@@ -3502,8 +3522,8 @@
             }
             // If there is a non-default per UID policy (we set UID op mode only if
             // non-default) it takes over, otherwise use the per package policy.
-            if (uidState.opModes != null && uidState.opModes.indexOfKey(switchCode) >= 0) {
-                final int uidMode = uidState.evalMode(code, uidState.opModes.get(switchCode));
+            if (uidState.getUidMode(switchCode) != AppOpsManager.opToDefaultMode(switchCode)) {
+                final int uidMode = uidState.evalMode(code, uidState.getUidMode(switchCode));
                 if (uidMode != AppOpsManager.MODE_ALLOWED) {
                     if (DEBUG) Slog.d(TAG, "noteOperation: uid reject #" + uidMode + " for code "
                             + switchCode + " (" + code + ") uid " + uid + " package "
@@ -4018,8 +4038,8 @@
             final int switchCode = AppOpsManager.opToSwitch(code);
             // If there is a non-default per UID policy (we set UID op mode only if
             // non-default) it takes over, otherwise use the per package policy.
-            if (uidState.opModes != null && uidState.opModes.indexOfKey(switchCode) >= 0) {
-                final int uidMode = uidState.evalMode(code, uidState.opModes.get(switchCode));
+            if (uidState.getUidMode(switchCode) != AppOpsManager.opToDefaultMode(switchCode)) {
+                final int uidMode = uidState.evalMode(code, uidState.getUidMode(switchCode));
                 if (!shouldStartForMode(uidMode, startIfModeDefault)) {
                     if (DEBUG) {
                         Slog.d(TAG, "startOperation: uid reject #" + uidMode + " for code "
@@ -4516,9 +4536,8 @@
                     continue;
                 }
 
-                if (uidState.opModes != null
-                        && uidState.opModes.indexOfKey(code) >= 0
-                        && uidState.opModes.get(code) == AppOpsManager.MODE_FOREGROUND) {
+                if (uidState.getUidMode(code) != AppOpsManager.opToDefaultMode(code)
+                        && uidState.getUidMode(code) == AppOpsManager.MODE_FOREGROUND) {
                     mHandler.sendMessage(PooledLambda.obtainMessage(
                             AppOpsService::notifyOpChangedForAllPkgsInUid,
                             this, code, uidState.uid, true, null));
@@ -4536,7 +4555,7 @@
                                 if (op == null) {
                                     continue;
                                 }
-                                if (op.mode == AppOpsManager.MODE_FOREGROUND) {
+                                if (op.getMode() == AppOpsManager.MODE_FOREGROUND) {
                                     mHandler.sendMessage(PooledLambda.obtainMessage(
                                             AppOpsService::notifyOpChanged,
                                             this, callback, code, uidState.uid,
@@ -4811,14 +4830,16 @@
         return ops;
     }
 
-    private void scheduleWriteLocked() {
+    @Override
+    public void scheduleWriteLocked() {
         if (!mWriteScheduled) {
             mWriteScheduled = true;
             mHandler.postDelayed(mWriteRunner, WRITE_DELAY);
         }
     }
 
-    private void scheduleFastWriteLocked() {
+    @Override
+    public void scheduleFastWriteLocked() {
         if (!mFastWriteScheduled) {
             mWriteScheduled = true;
             mFastWriteScheduled = true;
@@ -4929,6 +4950,7 @@
                 }
                 boolean success = false;
                 mUidStates.clear();
+                mAppOpsServiceInterface.clearAllModes();
                 try {
                     TypedXmlPullParser parser = Xml.resolvePullParser(stream);
                     int type;
@@ -4977,6 +4999,7 @@
                 } finally {
                     if (!success) {
                         mUidStates.clear();
+                        mAppOpsServiceInterface.clearAllModes();
                     }
                     try {
                         stream.close();
@@ -4996,11 +5019,12 @@
             if (uidState == null) {
                 continue;
             }
-            if (uidState.opModes != null) {
-                final int idx = uidState.opModes.indexOfKey(AppOpsManager.OP_RUN_IN_BACKGROUND);
+            SparseIntArray opModes = uidState.getNonDefaultUidModes();
+            if (opModes != null) {
+                final int idx = opModes.indexOfKey(AppOpsManager.OP_RUN_IN_BACKGROUND);
                 if (idx >= 0) {
-                    uidState.opModes.put(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND,
-                        uidState.opModes.valueAt(idx));
+                    uidState.setUidMode(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND,
+                            opModes.valueAt(idx));
                 }
             }
             if (uidState.pkgOps == null) {
@@ -5011,10 +5035,10 @@
                 Ops ops = uidState.pkgOps.valueAt(j);
                 if (ops != null) {
                     final Op op = ops.get(AppOpsManager.OP_RUN_IN_BACKGROUND);
-                    if (op != null && op.mode != AppOpsManager.opToDefaultMode(op.op)) {
+                    if (op != null && op.getMode() != AppOpsManager.opToDefaultMode(op.op)) {
                         final Op copy = new Op(op.uidState, op.packageName,
                                 AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, uidState.uid);
-                        copy.mode = op.mode;
+                        copy.setMode(op.getMode());
                         ops.put(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, copy);
                         changed = true;
                     }
@@ -5142,7 +5166,7 @@
         Op op = new Op(uidState, pkgName, opCode, uidState.uid);
 
         final int mode = parser.getAttributeInt(null, "m", AppOpsManager.opToDefaultMode(op.op));
-        op.mode = mode;
+        op.setMode(mode);
 
         int outerDepth = parser.getDepth();
         int type;
@@ -5199,16 +5223,9 @@
                         UidState uidState = mUidStates.valueAt(uidStateNum);
                         int uid = mUidStates.keyAt(uidStateNum);
 
-                        SparseIntArray opModes = uidState.opModes;
+                        SparseIntArray opModes = uidState.getNonDefaultUidModes();
                         if (opModes != null && opModes.size() > 0) {
-                            uidStatesClone.put(uid, new SparseIntArray(opModes.size()));
-
-                            final int opCount = opModes.size();
-                            for (int opCountNum = 0; opCountNum < opCount; opCountNum++) {
-                                uidStatesClone.get(uid).put(
-                                        opModes.keyAt(opCountNum),
-                                        opModes.valueAt(opCountNum));
-                            }
+                            uidStatesClone.put(uid, opModes);
                         }
                     }
                 }
@@ -6314,15 +6331,15 @@
             }
             for (int i=0; i<mUidStates.size(); i++) {
                 UidState uidState = mUidStates.valueAt(i);
-                final SparseIntArray opModes = uidState.opModes;
+                final SparseIntArray opModes = uidState.getNonDefaultUidModes();
                 final ArrayMap<String, Ops> pkgOps = uidState.pkgOps;
 
                 if (dumpWatchers || dumpHistory) {
                     continue;
                 }
                 if (dumpOp >= 0 || dumpPackage != null || dumpMode >= 0) {
-                    boolean hasOp = dumpOp < 0 || (uidState.opModes != null
-                            && uidState.opModes.indexOfKey(dumpOp) >= 0);
+                    boolean hasOp = dumpOp < 0 || (opModes != null
+                            && opModes.indexOfKey(dumpOp) >= 0);
                     boolean hasPackage = dumpPackage == null || dumpUid == mUidStates.keyAt(i);
                     boolean hasMode = dumpMode < 0;
                     if (!hasMode && opModes != null) {
@@ -6342,7 +6359,7 @@
                             }
                             if (!hasMode) {
                                 for (int opi = 0; !hasMode && opi < ops.size(); opi++) {
-                                    if (ops.valueAt(opi).mode == dumpMode) {
+                                    if (ops.valueAt(opi).getMode() == dumpMode) {
                                         hasMode = true;
                                     }
                                 }
@@ -6437,7 +6454,7 @@
                         if (dumpOp >= 0 && dumpOp != opCode) {
                             continue;
                         }
-                        if (dumpMode >= 0 && dumpMode != op.mode) {
+                        if (dumpMode >= 0 && dumpMode != op.getMode()) {
                             continue;
                         }
                         if (!printedPackage) {
@@ -6445,14 +6462,14 @@
                             printedPackage = true;
                         }
                         pw.print("      "); pw.print(AppOpsManager.opToName(opCode));
-                        pw.print(" ("); pw.print(AppOpsManager.modeToName(op.mode));
+                        pw.print(" ("); pw.print(AppOpsManager.modeToName(op.getMode()));
                         final int switchOp = AppOpsManager.opToSwitch(opCode);
                         if (switchOp != opCode) {
                             pw.print(" / switch ");
                             pw.print(AppOpsManager.opToName(switchOp));
                             final Op switchObj = ops.get(switchOp);
-                            int mode = switchObj != null ? switchObj.mode
-                                    : AppOpsManager.opToDefaultMode(switchOp);
+                            int mode = switchObj == null
+                                    ? AppOpsManager.opToDefaultMode(switchOp) : switchObj.getMode();
                             pw.print("="); pw.print(AppOpsManager.modeToName(mode));
                         }
                         pw.println("): ");
@@ -6696,7 +6713,7 @@
         for (int pkgNum = 0; pkgNum < numPkgOps; pkgNum++) {
             Ops ops = uidState.pkgOps.valueAt(pkgNum);
             Op op = ops != null ? ops.get(code) : null;
-            if (op == null || (op.mode != MODE_ALLOWED && op.mode != MODE_FOREGROUND)) {
+            if (op == null || (op.getMode() != MODE_ALLOWED && op.getMode() != MODE_FOREGROUND)) {
                 continue;
             }
             int numAttrTags = op.mAttributions.size();
@@ -6817,6 +6834,7 @@
                 return;
             }
             Ops removedOps = uidState.pkgOps.remove(packageName);
+            mAppOpsServiceInterface.removePackage(packageName);
             if (removedOps != null) {
                 scheduleFastWriteLocked();
             }
@@ -7136,10 +7154,12 @@
         return false;
     }
 
+    @GuardedBy("this")
     private void removeUidsForUserLocked(int userHandle) {
         for (int i = mUidStates.size() - 1; i >= 0; --i) {
             final int uid = mUidStates.keyAt(i);
             if (UserHandle.getUserId(uid) == userHandle) {
+                mUidStates.valueAt(i).clear();
                 mUidStates.removeAt(i);
             }
         }
diff --git a/services/core/java/com/android/server/appop/AppOpsServiceInterface.java b/services/core/java/com/android/server/appop/AppOpsServiceInterface.java
new file mode 100644
index 0000000..cd5ea12
--- /dev/null
+++ b/services/core/java/com/android/server/appop/AppOpsServiceInterface.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.appop;
+import android.annotation.NonNull;
+import android.app.AppOpsManager.Mode;
+import android.util.SparseIntArray;
+/**
+ * Interface for accessing and modifying modes for app-ops i.e. package and uid modes.
+ * In the future this interface will also include mode callbacks and op restrictions.
+ */
+public interface AppOpsServiceInterface {
+    /**
+     * Returns a copy of non-default app-ops with op as keys and their modes as values for a uid.
+     * Returns an empty SparseIntArray if nothing is set.
+     * @param uid for which we need the app-ops and their modes.
+     */
+    SparseIntArray getNonDefaultUidModes(int uid);
+
+    /**
+     * Returns the app-op mode for a particular app-op of a uid.
+     * Returns default op mode if the op mode for particular uid and op is not set.
+     * @param uid user id for which we need the mode.
+     * @param op app-op for which we need the mode.
+     * @return mode of the app-op.
+     */
+    int getUidMode(int uid, int op);
+
+    /**
+     * Set the app-op mode for a particular uid and op.
+     * The mode is not set if the mode is the same as the default mode for the op.
+     * @param uid user id for which we want to set the mode.
+     * @param op app-op for which we want to set the mode.
+     * @param mode mode for the app-op.
+     * @return true if op mode is changed.
+     */
+    boolean setUidMode(int uid, int op, @Mode int mode);
+
+    /**
+     * Gets the app-op mode for a particular package.
+     * Returns default op mode if the op mode for the particular package is not set.
+     * @param packageName package name for which we need the op mode.
+     * @param op app-op for which we need the mode.
+     * @return the mode of the app-op.
+     */
+    int getPackageMode(@NonNull String packageName, int op);
+
+    /**
+     * Sets the app-op mode for a particular package.
+     * @param packageName package name for which we need to set the op mode.
+     * @param op app-op for which we need to set the mode.
+     * @param mode the mode of the app-op.
+     */
+    void setPackageMode(@NonNull String packageName, int op, @Mode int mode);
+
+    /**
+     * Stop tracking any app-op modes for a package.
+     * @param packageName Name of the package for which we want to remove all mode tracking.
+     */
+    boolean removePackage(@NonNull String packageName);
+
+    /**
+     * Stop tracking any app-op modes for this uid.
+     * @param uid user id for which we want to remove all tracking.
+     */
+    void removeUid(int uid);
+
+    /**
+     * Returns true if all uid modes for this uid are
+     * in default state.
+     * @param uid user id
+     */
+    boolean areUidModesDefault(int uid);
+
+    /**
+     * Returns true if all package modes for this package name are
+     * in default state.
+     * @param packageName package name.
+     */
+    boolean arePackageModesDefault(String packageName);
+
+    /**
+     * Stop tracking app-op modes for all uid and packages.
+     */
+    void clearAllModes();
+}
diff --git a/services/core/java/com/android/server/appop/LegacyAppOpsServiceInterfaceImpl.java b/services/core/java/com/android/server/appop/LegacyAppOpsServiceInterfaceImpl.java
new file mode 100644
index 0000000..c27c0d3
--- /dev/null
+++ b/services/core/java/com/android/server/appop/LegacyAppOpsServiceInterfaceImpl.java
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.appop;
+
+import android.annotation.NonNull;
+import android.app.AppOpsManager;
+import android.app.AppOpsManager.Mode;
+import android.util.ArrayMap;
+import android.util.SparseArray;
+import android.util.SparseIntArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+
+
+/**
+ * Legacy implementation for App-ops service's app-op mode (uid and package) storage and access.
+ * In the future this class will also include mode callbacks and op restrictions.
+ */
+public class LegacyAppOpsServiceInterfaceImpl implements AppOpsServiceInterface {
+
+    // Should be the same object that the AppOpsService is using for locking.
+    final Object mLock;
+
+    @GuardedBy("mLock")
+    @VisibleForTesting
+    final SparseArray<SparseIntArray> mUidModes = new SparseArray<>();
+
+    @GuardedBy("mLock")
+    final ArrayMap<String, SparseIntArray> mPackageModes = new ArrayMap<>();
+
+    final PersistenceScheduler mPersistenceScheduler;
+
+
+    LegacyAppOpsServiceInterfaceImpl(PersistenceScheduler persistenceScheduler,
+            @NonNull Object lock) {
+        this.mPersistenceScheduler = persistenceScheduler;
+        this.mLock = lock;
+    }
+
+    @Override
+    public SparseIntArray getNonDefaultUidModes(int uid) {
+        synchronized (mLock) {
+            SparseIntArray opModes = mUidModes.get(uid, null);
+            if (opModes == null) {
+                return new SparseIntArray();
+            }
+            return opModes.clone();
+        }
+    }
+
+    @Override
+    public int getUidMode(int uid, int op) {
+        synchronized (mLock) {
+            SparseIntArray opModes = mUidModes.get(uid, null);
+            if (opModes == null) {
+                return AppOpsManager.opToDefaultMode(op);
+            }
+            return opModes.get(op, AppOpsManager.opToDefaultMode(op));
+        }
+    }
+
+    @Override
+    public boolean setUidMode(int uid, int op, int mode) {
+        final int defaultMode = AppOpsManager.opToDefaultMode(op);
+        synchronized (mLock) {
+            SparseIntArray opModes = mUidModes.get(uid, null);
+            if (opModes == null) {
+                if (mode != defaultMode) {
+                    opModes = new SparseIntArray();
+                    mUidModes.put(uid, opModes);
+                    opModes.put(op, mode);
+                    mPersistenceScheduler.scheduleWriteLocked();
+                }
+            } else {
+                if (opModes.indexOfKey(op) >= 0 && opModes.get(op) == mode) {
+                    return false;
+                }
+                if (mode == defaultMode) {
+                    opModes.delete(op);
+                    if (opModes.size() <= 0) {
+                        opModes = null;
+                        mUidModes.delete(uid);
+                    }
+                } else {
+                    opModes.put(op, mode);
+                }
+                mPersistenceScheduler.scheduleWriteLocked();
+            }
+        }
+        return true;
+    }
+
+    @Override
+    public int getPackageMode(String packageName, int op) {
+        synchronized (mLock) {
+            SparseIntArray opModes = mPackageModes.getOrDefault(packageName, null);
+            if (opModes == null) {
+                return AppOpsManager.opToDefaultMode(op);
+            }
+            return opModes.get(op, AppOpsManager.opToDefaultMode(op));
+        }
+    }
+
+    @Override
+    public void setPackageMode(String packageName, int op, @Mode int mode) {
+        final int defaultMode = AppOpsManager.opToDefaultMode(op);
+        synchronized (mLock) {
+            SparseIntArray opModes = mPackageModes.get(packageName);
+            if (opModes == null) {
+                if (mode != defaultMode) {
+                    opModes = new SparseIntArray();
+                    mPackageModes.put(packageName, opModes);
+                    opModes.put(op, mode);
+                    mPersistenceScheduler.scheduleWriteLocked();
+                }
+            } else {
+                if (opModes.indexOfKey(op) >= 0 && opModes.get(op) == mode) {
+                    return;
+                }
+                if (mode == defaultMode) {
+                    opModes.delete(op);
+                    if (opModes.size() <= 0) {
+                        opModes = null;
+                        mPackageModes.remove(packageName);
+                    }
+                } else {
+                    opModes.put(op, mode);
+                }
+                mPersistenceScheduler.scheduleWriteLocked();
+            }
+        }
+    }
+
+    @Override
+    public void removeUid(int uid) {
+        synchronized (mLock) {
+            SparseIntArray opModes = mUidModes.get(uid);
+            if (opModes == null) {
+                return;
+            }
+            mUidModes.remove(uid);
+            mPersistenceScheduler.scheduleFastWriteLocked();
+        }
+    }
+
+
+    @Override
+    public boolean areUidModesDefault(int uid) {
+        synchronized (mLock) {
+            SparseIntArray opModes = mUidModes.get(uid);
+            return (opModes == null || opModes.size() <= 0);
+        }
+    }
+
+    @Override
+    public boolean arePackageModesDefault(String packageMode) {
+        synchronized (mLock) {
+            SparseIntArray opModes = mPackageModes.get(packageMode);
+            return (opModes == null || opModes.size() <= 0);
+        }
+    }
+
+    @Override
+    public boolean removePackage(String packageName) {
+        synchronized (mLock) {
+            SparseIntArray ops = mPackageModes.remove(packageName);
+            if (ops != null) {
+                mPersistenceScheduler.scheduleFastWriteLocked();
+                return true;
+            }
+            return false;
+        }
+    }
+
+    @Override
+    public void clearAllModes() {
+        synchronized (mLock) {
+            mUidModes.clear();
+            mPackageModes.clear();
+        }
+    }
+
+}
diff --git a/services/core/java/com/android/server/appop/PersistenceScheduler.java b/services/core/java/com/android/server/appop/PersistenceScheduler.java
new file mode 100644
index 0000000..e50b658
--- /dev/null
+++ b/services/core/java/com/android/server/appop/PersistenceScheduler.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.appop;
+
+/**
+ * Interface that allows callers to persist AppOpsService's internal state
+ * to disk.
+ */
+public interface PersistenceScheduler {
+
+    /**
+     * Schedules disk writes for appOpsService and it's internal states.
+     */
+    void scheduleWriteLocked();
+
+    /**
+     * Schedules fast disk writes for appOpsService and it's internal states.
+     */
+    void scheduleFastWriteLocked();
+
+}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 298ee97..5a20db3 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -369,6 +369,7 @@
     private static final int MSG_DISPATCH_DEVICE_VOLUME_BEHAVIOR = 47;
     private static final int MSG_ROTATION_UPDATE = 48;
     private static final int MSG_FOLD_UPDATE = 49;
+    private static final int MSG_RESET_SPATIALIZER = 50;
 
     // start of messages handled under wakelock
     //   these messages can only be queued, i.e. sent with queueMsgUnderWakeLock(),
@@ -376,6 +377,7 @@
     private static final int MSG_DISABLE_AUDIO_FOR_UID = 100;
     private static final int MSG_INIT_STREAMS_VOLUMES = 101;
     private static final int MSG_INIT_SPATIALIZER = 102;
+
     // end of messages handled under wakelock
 
     // retry delay in case of failure to indicate system ready to AudioFlinger
@@ -8305,6 +8307,10 @@
                     onPersistSpatialAudioDeviceSettings();
                     break;
 
+                case MSG_RESET_SPATIALIZER:
+                    mSpatializerHelper.reset(/* featureEnabled */ mHasSpatializerEffect);
+                    break;
+
                 case MSG_CHECK_MUSIC_ACTIVE:
                     onCheckMusicActive((String) msg.obj);
                     break;
@@ -9283,6 +9289,16 @@
                 /*arg1*/ 0, /*arg2*/ 0, TAG, /*delay*/ 0);
     }
 
+    /**
+     * post a message to schedule a reset of the spatializer state
+     */
+    void postResetSpatializer() {
+        sendMsg(mAudioHandler,
+                MSG_RESET_SPATIALIZER,
+                SENDMSG_REPLACE,
+                /*arg1*/ 0, /*arg2*/ 0, TAG, /*delay*/ 0);
+    }
+
     void onInitSpatializer() {
         final String settings = mSettings.getSecureStringForUser(mContentResolver,
                 Settings.Secure.SPATIAL_AUDIO_ENABLED, UserHandle.USER_CURRENT);
diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java
index d9037fe..66a4527 100644
--- a/services/core/java/com/android/server/audio/BtHelper.java
+++ b/services/core/java/com/android/server/audio/BtHelper.java
@@ -53,6 +53,8 @@
 
     private static final String TAG = "AS.BtHelper";
 
+    private static final int SOURCE_CODEC_TYPE_OPUS = 6; // TODO remove in U
+
     private final @NonNull AudioDeviceBroker mDeviceBroker;
 
     BtHelper(@NonNull AudioDeviceBroker broker) {
@@ -911,6 +913,8 @@
                 return "ENCODING_APTX_HD";
             case BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC:
                 return "ENCODING_LDAC";
+            case SOURCE_CODEC_TYPE_OPUS: // TODO update in U
+                return "ENCODING_OPUS";
             default:
                 return "ENCODING_BT_CODEC_TYPE(" + btCodecType + ")";
         }
diff --git a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
index 9ff72d3..4559c56 100644
--- a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
+++ b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
@@ -17,10 +17,12 @@
 package com.android.server.audio;
 
 import static android.media.AudioPlaybackConfiguration.EXTRA_PLAYER_EVENT_MUTE;
+import static android.media.AudioPlaybackConfiguration.PLAYER_MUTE_CLIENT_VOLUME;
 import static android.media.AudioPlaybackConfiguration.PLAYER_MUTE_MASTER;
 import static android.media.AudioPlaybackConfiguration.PLAYER_MUTE_PLAYBACK_RESTRICTED;
 import static android.media.AudioPlaybackConfiguration.PLAYER_MUTE_STREAM_MUTED;
 import static android.media.AudioPlaybackConfiguration.PLAYER_MUTE_STREAM_VOLUME;
+import static android.media.AudioPlaybackConfiguration.PLAYER_MUTE_VOLUME_SHAPER;
 import static android.media.AudioPlaybackConfiguration.PLAYER_PIID_INVALID;
 import static android.media.AudioPlaybackConfiguration.PLAYER_UPDATE_MUTED;
 
@@ -1145,6 +1147,10 @@
                             (mEventValue & PLAYER_MUTE_STREAM_MUTED) != 0);
                     builder.append(" muteFromPlaybackRestricted:").append(
                             (mEventValue & PLAYER_MUTE_PLAYBACK_RESTRICTED) != 0);
+                    builder.append(" muteFromClientVolume:").append(
+                            (mEventValue & PLAYER_MUTE_CLIENT_VOLUME) != 0);
+                    builder.append(" muteFromVolumeShaper:").append(
+                            (mEventValue & PLAYER_MUTE_VOLUME_SHAPER) != 0);
                     return builder.toString();
                 default:
                     return builder.toString();
diff --git a/services/core/java/com/android/server/audio/SpatializerHelper.java b/services/core/java/com/android/server/audio/SpatializerHelper.java
index 1def72b..e27fb11 100644
--- a/services/core/java/com/android/server/audio/SpatializerHelper.java
+++ b/services/core/java/com/android/server/audio/SpatializerHelper.java
@@ -389,10 +389,10 @@
             try {
                 mSpat.setLevel(level);
             } catch (RemoteException e) {
-                Log.e(TAG, "Can't set spatializer level", e);
-                mState = STATE_NOT_SUPPORTED;
-                mCapableSpatLevel = Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE;
-                enabled = false;
+                Log.e(TAG, "onRoutingUpdated() Can't set spatializer level", e);
+                // try to recover by resetting the native spatializer state
+                postReset();
+                return;
             }
         }
 
@@ -404,6 +404,10 @@
         }
     }
 
+    private void postReset() {
+        mAudioService.postResetSpatializer();
+    }
+
     //------------------------------------------------------
     // spatializer callback from native
     private final class SpatializerCallback extends INativeSpatializerCallback.Stub {
@@ -751,33 +755,29 @@
                 if (enabled) {
                     throw (new IllegalStateException("Can't enable when uninitialized"));
                 }
-                return;
+                break;
             case STATE_NOT_SUPPORTED:
                 if (enabled) {
                     Log.e(TAG, "Can't enable when unsupported");
                 }
-                return;
+                break;
             case STATE_DISABLED_UNAVAILABLE:
             case STATE_DISABLED_AVAILABLE:
                 if (enabled) {
                     createSpat();
                     onRoutingUpdated();
-                    break;
-                } else {
-                    // already in disabled state
-                    return;
-                }
+                    // onRoutingUpdated() can update the "enabled" state based on context
+                    // and will call setDispatchFeatureEnabledState().
+                } // else { nothing to do as already disabled }
+                break;
             case STATE_ENABLED_UNAVAILABLE:
             case STATE_ENABLED_AVAILABLE:
                 if (!enabled) {
                     releaseSpat();
-                    break;
-                } else {
-                    // already in enabled state
-                    return;
-                }
+                    setDispatchFeatureEnabledState(false, "setSpatializerEnabledInt");
+                } // else { nothing to do as already enabled }
+                break;
         }
-        setDispatchFeatureEnabledState(enabled, "setSpatializerEnabledInt");
     }
 
     synchronized int getCapableImmersiveAudioLevel() {
@@ -1161,8 +1161,11 @@
             case STATE_DISABLED_AVAILABLE:
             case STATE_ENABLED_AVAILABLE:
                 if (mSpat == null) {
-                    throw (new IllegalStateException(
-                            "null Spatializer when calling " + funcName));
+                    // try to recover by resetting the native spatializer state
+                    Log.e(TAG, "checkSpatForHeadTracking(): "
+                            + "native spatializer should not be null in state: " + mState);
+                    postReset();
+                    return false;
                 }
                 break;
         }
@@ -1252,8 +1255,8 @@
             case STATE_DISABLED_AVAILABLE:
             case STATE_ENABLED_AVAILABLE:
                 if (mSpat == null) {
-                    throw (new IllegalStateException(
-                            "null Spatializer for setParameter for key:" + key));
+                    Log.e(TAG, "setParameter(" + key + "): null spatializer in state: " + mState);
+                    return;
                 }
                 break;
         }
@@ -1276,8 +1279,8 @@
             case STATE_DISABLED_AVAILABLE:
             case STATE_ENABLED_AVAILABLE:
                 if (mSpat == null) {
-                    throw (new IllegalStateException(
-                            "null Spatializer for getParameter for key:" + key));
+                    Log.e(TAG, "getParameter(" + key + "): null spatializer in state: " + mState);
+                    return;
                 }
                 break;
         }
diff --git a/services/core/java/com/android/server/broadcastradio/BroadcastRadioService.java b/services/core/java/com/android/server/broadcastradio/BroadcastRadioService.java
index 351a1e9..23183f9 100644
--- a/services/core/java/com/android/server/broadcastradio/BroadcastRadioService.java
+++ b/services/core/java/com/android/server/broadcastradio/BroadcastRadioService.java
@@ -26,11 +26,14 @@
 import android.hardware.radio.ITunerCallback;
 import android.hardware.radio.RadioManager;
 import android.os.RemoteException;
+import android.util.IndentingPrintWriter;
 import android.util.Slog;
 
 import com.android.server.SystemService;
 import com.android.server.broadcastradio.hal2.AnnouncementAggregator;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
@@ -46,7 +49,7 @@
     private final com.android.server.broadcastradio.hal2.BroadcastRadioService mHal2;
 
     private final Object mLock = new Object();
-    private List<RadioManager.ModuleProperties> mV1Modules = null;
+    private final List<RadioManager.ModuleProperties> mV1Modules;
 
     public BroadcastRadioService(Context context) {
         super(context);
@@ -115,5 +118,21 @@
                 return mHal2.addAnnouncementListener(enabledTypes, listener);
             }
         }
+
+        @Override
+        protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+            IndentingPrintWriter radioPw = new IndentingPrintWriter(pw);
+            radioPw.printf("BroadcastRadioService\n");
+            radioPw.increaseIndent();
+            radioPw.printf("HAL1: %s\n", mHal1);
+            radioPw.increaseIndent();
+            radioPw.printf("Modules of HAL1: %s\n", mV1Modules);
+            radioPw.decreaseIndent();
+            radioPw.printf("HAL2:\n");
+            radioPw.increaseIndent();
+            mHal2.dumpInfo(radioPw);
+            radioPw.decreaseIndent();
+            radioPw.decreaseIndent();
+        }
     }
 }
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java b/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java
index 534e828..5605737 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java
@@ -29,6 +29,7 @@
 import android.hidl.manager.V1_0.IServiceNotification;
 import android.os.IHwBinder.DeathRecipient;
 import android.os.RemoteException;
+import android.util.IndentingPrintWriter;
 import android.util.Slog;
 
 import com.android.internal.annotations.GuardedBy;
@@ -195,4 +196,30 @@
         }
         return aggregator;
     }
+
+    /**
+     * Dump state of broadcastradio service for HIDL HAL 2.0.
+     *
+     * @param pw The file to which BroadcastRadioService state is dumped.
+     */
+    public void dumpInfo(IndentingPrintWriter pw) {
+        synchronized (mLock) {
+            pw.printf("Next module id available: %d\n", mNextModuleId);
+            pw.printf("ServiceName to module id map:\n");
+            pw.increaseIndent();
+            for (Map.Entry<String, Integer> entry : mServiceNameToModuleIdMap.entrySet()) {
+                pw.printf("Service name: %s, module id: %d\n", entry.getKey(), entry.getValue());
+            }
+            pw.decreaseIndent();
+            pw.printf("Radio modules:\n");
+            pw.increaseIndent();
+            for (Map.Entry<Integer, RadioModule> moduleEntry : mModules.entrySet()) {
+                pw.printf("Module id=%d:\n", moduleEntry.getKey());
+                pw.increaseIndent();
+                moduleEntry.getValue().dumpInfo(pw);
+                pw.decreaseIndent();
+            }
+            pw.decreaseIndent();
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java b/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java
index aeaa678..852aa66 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java
@@ -38,6 +38,7 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.RemoteException;
+import android.util.IndentingPrintWriter;
 import android.util.MutableInt;
 import android.util.Slog;
 
@@ -424,4 +425,30 @@
 
         return BitmapFactory.decodeByteArray(rawImage, 0, rawImage.length);
     }
+
+    void dumpInfo(IndentingPrintWriter pw) {
+        pw.printf("RadioModule\n");
+        pw.increaseIndent();
+        synchronized (mLock) {
+            pw.printf("BroadcastRadioService: %s\n", mService);
+            pw.printf("Properties: %s\n", mProperties);
+            pw.printf("HIDL2.0 HAL TunerSession: %s\n", mHalTunerSession);
+            pw.printf("Is antenna connected? ");
+            if (mAntennaConnected == null) {
+                pw.printf("null\n");
+            } else {
+                pw.printf("%s\n", mAntennaConnected ? "Yes" : "No");
+            }
+            pw.printf("current ProgramInfo: %s\n", mCurrentProgramInfo);
+            pw.printf("ProgramInfoCache: %s\n", mProgramInfoCache);
+            pw.printf("Union of AIDL ProgramFilters: %s\n", mUnionOfAidlProgramFilters);
+            pw.printf("AIDL TunerSessions:\n");
+            pw.increaseIndent();
+            for (TunerSession aidlTunerSession : mAidlTunerSessions) {
+                aidlTunerSession.dumpInfo(pw);
+            }
+            pw.decreaseIndent();
+        }
+        pw.decreaseIndent();
+    }
 }
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java b/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java
index c13216b..41f753c 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java
@@ -27,6 +27,7 @@
 import android.hardware.radio.ProgramSelector;
 import android.hardware.radio.RadioManager;
 import android.os.RemoteException;
+import android.util.IndentingPrintWriter;
 import android.util.Log;
 import android.util.MutableBoolean;
 import android.util.MutableInt;
@@ -339,4 +340,17 @@
                     () -> mHwSession.getParameters(Convert.listToArrayList(keys))));
         }
     }
+
+    void dumpInfo(IndentingPrintWriter pw) {
+        pw.printf("TunerSession\n");
+        pw.increaseIndent();
+        synchronized (mLock) {
+            pw.printf("HIDL HAL Session: %s\n", mHwSession);
+            pw.printf("Is session closed? %s\n", mIsClosed ? "Yes" : "No");
+            pw.printf("Is muted? %s\n", mIsMuted ? "Yes" : "No");
+            pw.printf("ProgramInfoCache: %s\n", mProgramInfoCache);
+            pw.printf("Config: %s\n", mDummyConfig);
+        }
+        pw.decreaseIndent();
+    }
 }
diff --git a/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java b/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java
index f2b4d42..230bfc5 100644
--- a/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java
+++ b/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java
@@ -16,6 +16,7 @@
 
 package com.android.server.companion.virtual;
 
+import android.annotation.NonNull;
 import android.companion.virtual.IVirtualDevice;
 
 /**
@@ -24,16 +25,41 @@
  */
 public abstract class VirtualDeviceManagerInternal {
 
+    /** Interface to listen to the creation and destruction of virtual displays. */
+    public interface VirtualDisplayListener {
+        /** Notifies that a virtual display was created. */
+        void onVirtualDisplayCreated(int displayId);
+
+        /** Notifies that a virtual display was removed. */
+        void onVirtualDisplayRemoved(int displayId);
+    }
+
+    /** Register a listener for the creation and destruction of virtual displays. */
+    public abstract void registerVirtualDisplayListener(
+            @NonNull VirtualDisplayListener listener);
+
+    /** Unregister a listener for the creation and  destruction of virtual displays. */
+    public abstract void unregisterVirtualDisplayListener(
+            @NonNull VirtualDisplayListener listener);
+
+
     /**
      * Validate the virtual device.
      */
     public abstract boolean isValidVirtualDevice(IVirtualDevice virtualDevice);
 
     /**
-     * Notify a virtual display is removed.
+     * Notifies that a virtual display is created.
+     *
+     * @param displayId The display id of the created virtual display.
+     */
+    public abstract void onVirtualDisplayCreated(int displayId);
+
+    /**
+     * Notifies that a virtual display is removed.
      *
      * @param virtualDevice The virtual device where the virtual display located.
-     * @param displayId The display id of the removed virtual display.
+     * @param displayId     The display id of the removed virtual display.
      */
     public abstract void onVirtualDisplayRemoved(IVirtualDevice virtualDevice, int displayId);
 
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index b343480..2dd3864 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -2583,8 +2583,8 @@
         final BrightnessSetting brightnessSetting = new BrightnessSetting(mPersistentDataStore,
                 display, mSyncRoot);
         final DisplayPowerController displayPowerController = new DisplayPowerController(
-                mContext, mDisplayPowerCallbacks, mPowerHandler, mSensorManager,
-                mDisplayBlanker, display, mBrightnessTracker, brightnessSetting,
+                mContext, /* injector= */ null, mDisplayPowerCallbacks, mPowerHandler,
+                mSensorManager, mDisplayBlanker, display, mBrightnessTracker, brightnessSetting,
                 () -> handleBrightnessChange(display));
         mDisplayPowerControllers.append(display.getDisplayIdLocked(), displayPowerController);
     }
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index c01a228..e71e29b 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -48,6 +48,7 @@
 import android.os.Trace;
 import android.os.UserHandle;
 import android.provider.Settings;
+import android.util.FloatProperty;
 import android.util.Log;
 import android.util.MathUtils;
 import android.util.MutableFloat;
@@ -58,6 +59,7 @@
 
 import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.IBatteryStats;
 import com.android.internal.display.BrightnessSynchronizer;
 import com.android.internal.logging.MetricsLogger;
@@ -97,7 +99,7 @@
  * blocker as long as the display is not ready.  So most of the work done here
  * does not need to worry about holding a suspend blocker unless it happens
  * independently of the display ready signal.
-   *
+ *
  * For debugging, you can make the color fade and brightness animations run
  * slower by changing the "animator duration scale" option in Development Settings.
  */
@@ -160,7 +162,7 @@
 
     private static final int RINGBUFFER_MAX = 100;
 
-    private final String TAG;
+    private final String mTag;
 
     private final Object mLock = new Object();
 
@@ -256,6 +258,9 @@
     // to reach the final state.
     private final boolean mBrightnessBucketsInDozeConfig;
 
+    private final Clock mClock;
+    private final Injector mInjector;
+
     //  Maximum time a ramp animation can take.
     private long mBrightnessRampIncreaseMaxTimeMillis;
     private long mBrightnessRampDecreaseMaxTimeMillis;
@@ -495,20 +500,22 @@
     /**
      * Creates the display power controller.
      */
-    public DisplayPowerController(Context context,
+    DisplayPowerController(Context context, Injector injector,
             DisplayPowerCallbacks callbacks, Handler handler,
             SensorManager sensorManager, DisplayBlanker blanker, LogicalDisplay logicalDisplay,
             BrightnessTracker brightnessTracker, BrightnessSetting brightnessSetting,
             Runnable onBrightnessChangeRunnable) {
+
+        mInjector = injector != null ? injector : new Injector();
+        mClock = mInjector.getClock();
         mLogicalDisplay = logicalDisplay;
         mDisplayId = mLogicalDisplay.getDisplayIdLocked();
-        final String displayIdStr = "[" + mDisplayId + "]";
-        TAG = "DisplayPowerController" + displayIdStr;
-        mSuspendBlockerIdUnfinishedBusiness = displayIdStr + "unfinished business";
-        mSuspendBlockerIdOnStateChanged = displayIdStr + "on state changed";
-        mSuspendBlockerIdProxPositive = displayIdStr + "prox positive";
-        mSuspendBlockerIdProxNegative = displayIdStr + "prox negative";
-        mSuspendBlockerIdProxDebounce = displayIdStr + "prox debounce";
+        mTag = "DisplayPowerController[" + mDisplayId + "]";
+        mSuspendBlockerIdUnfinishedBusiness = getSuspendBlockerUnfinishedBusinessId(mDisplayId);
+        mSuspendBlockerIdOnStateChanged = getSuspendBlockerOnStateChangedId(mDisplayId);
+        mSuspendBlockerIdProxPositive = getSuspendBlockerProxPositiveId(mDisplayId);
+        mSuspendBlockerIdProxNegative = getSuspendBlockerProxNegativeId(mDisplayId);
+        mSuspendBlockerIdProxDebounce = getSuspendBlockerProxDebounceId(mDisplayId);
 
         mDisplayDevice = mLogicalDisplay.getPrimaryDisplayDeviceLocked();
         mUniqueDisplayId = logicalDisplay.getPrimaryDisplayDeviceLocked().getUniqueId();
@@ -593,7 +600,7 @@
                 displayWhiteBalanceSettings.setCallbacks(this);
                 displayWhiteBalanceController.setCallbacks(this);
             } catch (Exception e) {
-                Slog.e(TAG, "failed to set up display white-balance: " + e);
+                Slog.e(mTag, "failed to set up display white-balance: " + e);
             }
         }
         mDisplayWhiteBalanceSettings = displayWhiteBalanceSettings;
@@ -660,7 +667,7 @@
                 && mInteractiveModeBrightnessMapper == null)
                 || (mAutomaticBrightnessController.isInIdleMode()
                 && mIdleModeBrightnessMapper == null)) {
-            Log.w(TAG, "No brightness mapping available to recalculate splines for this mode");
+            Log.w(mTag, "No brightness mapping available to recalculate splines for this mode");
             return;
         }
 
@@ -681,7 +688,8 @@
 
     /**
      * Get the {@link BrightnessChangeEvent}s for the specified user.
-     * @param userId userId to fetch data for
+     *
+     * @param userId         userId to fetch data for
      * @param includePackage if false will null out the package name in events
      */
     @Nullable
@@ -723,10 +731,11 @@
      * The controller makes a copy of the provided object and then
      * begins adjusting the power state to match what was requested.
      *
-     * @param request The requested power state.
+     * @param request                  The requested power state.
      * @param waitForNegativeProximity If true, issues a request to wait for
-     * negative proximity before turning the screen back on, assuming the screen
-     * was turned off by the proximity sensor.
+     *                                 negative proximity before turning the screen back on,
+     *                                 assuming the screen
+     *                                 was turned off by the proximity sensor.
      * @return True if display is ready, false if there are important changes that must
      * be made asynchronously (such as turning the screen on), in which case the caller
      * should grab a wake lock, watch for {@link DisplayPowerCallbacks#onStateChanged()}
@@ -735,7 +744,7 @@
     public boolean requestPowerState(DisplayPowerRequest request,
             boolean waitForNegativeProximity) {
         if (DEBUG) {
-            Slog.d(TAG, "requestPowerState: "
+            Slog.d(mTag, "requestPowerState: "
                     + request + ", waitForNegativeProximity=" + waitForNegativeProximity);
         }
 
@@ -788,7 +797,7 @@
     public void onDisplayChanged() {
         final DisplayDevice device = mLogicalDisplay.getPrimaryDisplayDeviceLocked();
         if (device == null) {
-            Slog.wtf(TAG, "Display Device is null in DisplayPowerController for display: "
+            Slog.wtf(mTag, "Display Device is null in DisplayPowerController for display: "
                     + mLogicalDisplay.getDisplayIdLocked());
             return;
         }
@@ -834,7 +843,7 @@
         synchronized (mLock) {
             mStopped = true;
             Message msg = mHandler.obtainMessage(MSG_STOP);
-            mHandler.sendMessage(msg);
+            mHandler.sendMessageAtTime(msg, mClock.uptimeMillis());
 
             if (mDisplayWhiteBalanceController != null) {
                 mDisplayWhiteBalanceController.setEnabled(false);
@@ -888,12 +897,12 @@
         if (!mStopped && !mPendingUpdatePowerStateLocked) {
             mPendingUpdatePowerStateLocked = true;
             Message msg = mHandler.obtainMessage(MSG_UPDATE_POWER_STATE);
-            mHandler.sendMessage(msg);
+            mHandler.sendMessageAtTime(msg, mClock.uptimeMillis());
         }
     }
 
     private void initialize(int displayState) {
-        mPowerState = new DisplayPowerState(mBlanker,
+        mPowerState = mInjector.getDisplayPowerState(mBlanker,
                 mColorFadeEnabled ? new ColorFade(mDisplayId) : null, mDisplayId, displayState);
 
         if (mColorFadeEnabled) {
@@ -908,7 +917,7 @@
             mColorFadeOffAnimator.addListener(mAnimatorListener);
         }
 
-        mScreenBrightnessRampAnimator = new DualRampAnimator<>(mPowerState,
+        mScreenBrightnessRampAnimator = mInjector.getDualRampAnimator(mPowerState,
                 DisplayPowerState.SCREEN_BRIGHTNESS_FLOAT,
                 DisplayPowerState.SCREEN_SDR_BRIGHTNESS_FLOAT);
         mScreenBrightnessRampAnimator.setAnimationTimeLimits(
@@ -1002,7 +1011,7 @@
             if (initialLightSensorRate == -1) {
                 initialLightSensorRate = lightSensorRate;
             } else if (initialLightSensorRate > lightSensorRate) {
-                Slog.w(TAG, "Expected config_autoBrightnessInitialLightSensorRate ("
+                Slog.w(mTag, "Expected config_autoBrightnessInitialLightSensorRate ("
                         + initialLightSensorRate + ") to be less than or equal to "
                         + "config_autoBrightnessLightSensorRate (" + lightSensorRate + ").");
             }
@@ -1048,7 +1057,7 @@
         if (mDisplayDeviceConfig != null && mDisplayDeviceConfig.getNits() != null) {
             mNitsRange = mDisplayDeviceConfig.getNits();
         } else {
-            Slog.w(TAG, "Screen brightness nits configuration is unavailable; falling back");
+            Slog.w(mTag, "Screen brightness nits configuration is unavailable; falling back");
             mNitsRange = BrightnessMappingStrategy.getFloatArray(resources
                     .obtainTypedArray(com.android.internal.R.array.config_screenBrightnessNits));
         }
@@ -1077,13 +1086,16 @@
         @Override
         public void onAnimationStart(Animator animation) {
         }
+
         @Override
         public void onAnimationEnd(Animator animation) {
             sendUpdatePowerState();
         }
+
         @Override
         public void onAnimationRepeat(Animator animation) {
         }
+
         @Override
         public void onAnimationCancel(Animator animation) {
         }
@@ -1124,8 +1136,8 @@
         mOnProximityNegativeMessages = 0;
 
         final float brightness = mPowerState != null
-            ? mPowerState.getScreenBrightness()
-            : PowerManager.BRIGHTNESS_MIN;
+                ? mPowerState.getScreenBrightness()
+                : PowerManager.BRIGHTNESS_MIN;
         reportStats(brightness);
 
         if (mPowerState != null) {
@@ -1203,7 +1215,7 @@
                 state = Display.STATE_ON;
                 break;
         }
-        assert(state != Display.STATE_UNKNOWN);
+        assert (state != Display.STATE_UNKNOWN);
 
         // Apply the proximity sensor.
         if (mProximitySensor != null) {
@@ -1290,16 +1302,16 @@
         final boolean autoBrightnessEnabledInDoze =
                 mAllowAutoBrightnessWhileDozingConfig && Display.isDozeState(state);
         final boolean autoBrightnessEnabled = mPowerRequest.useAutoBrightness
-                    && (state == Display.STATE_ON || autoBrightnessEnabledInDoze)
-                    && Float.isNaN(brightnessState)
-                    && mAutomaticBrightnessController != null;
+                && (state == Display.STATE_ON || autoBrightnessEnabledInDoze)
+                && Float.isNaN(brightnessState)
+                && mAutomaticBrightnessController != null;
         final boolean autoBrightnessDisabledDueToDisplayOff = mPowerRequest.useAutoBrightness
-                    && !(state == Display.STATE_ON || autoBrightnessEnabledInDoze);
+                && !(state == Display.STATE_ON || autoBrightnessEnabledInDoze);
         final int autoBrightnessState = autoBrightnessEnabled
                 ? AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED
                 : autoBrightnessDisabledDueToDisplayOff
-                ? AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE
-                : AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED;
+                        ? AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE
+                        : AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED;
 
         final boolean userSetBrightnessChanged = updateUserSetScreenBrightness();
 
@@ -1589,9 +1601,9 @@
             // AND if we are not in idle screen brightness mode.
             if (!brightnessIsTemporary
                     && (mAutomaticBrightnessController != null
-                            && !mAutomaticBrightnessController.isInIdleMode())) {
+                    && !mAutomaticBrightnessController.isInIdleMode())) {
                 if (userInitiatedChange && (mAutomaticBrightnessController == null
-                            || !mAutomaticBrightnessController.hasValidAmbientLux())) {
+                        || !mAutomaticBrightnessController.hasValidAmbientLux())) {
                     // If we don't have a valid lux reading we can't report a valid
                     // slider event so notify as if the system changed the brightness.
                     userInitiatedChange = false;
@@ -1614,13 +1626,13 @@
 
         // Log any changes to what is currently driving the brightness setting.
         if (!mBrightnessReasonTemp.equals(mBrightnessReason) || brightnessAdjustmentFlags != 0) {
-            Slog.v(TAG, "Brightness [" + brightnessState + "] reason changing to: '"
+            Slog.v(mTag, "Brightness [" + brightnessState + "] reason changing to: '"
                     + mBrightnessReasonTemp.toString(brightnessAdjustmentFlags)
                     + "', previous reason: '" + mBrightnessReason + "'.");
             mBrightnessReason.set(mBrightnessReasonTemp);
         } else if (mBrightnessReasonTemp.getReason() == BrightnessReason.REASON_MANUAL
                 && userSetBrightnessChanged) {
-            Slog.v(TAG, "Brightness [" + brightnessState + "] manual adjustment.");
+            Slog.v(mTag, "Brightness [" + brightnessState + "] manual adjustment.");
         }
 
 
@@ -1638,7 +1650,7 @@
         // we don't spam logcat when the slider is being used.
         boolean tempToTempTransition =
                 mTempBrightnessEvent.getReason().getReason() == BrightnessReason.REASON_TEMPORARY
-                && mLastBrightnessEvent.getReason().getReason()
+                        && mLastBrightnessEvent.getReason().getReason()
                         == BrightnessReason.REASON_TEMPORARY;
         if ((!mTempBrightnessEvent.equalsMainData(mLastBrightnessEvent) && !tempToTempTransition)
                 || brightnessAdjustmentFlags != 0) {
@@ -1650,7 +1662,7 @@
             newEvent.setAdjustmentFlags(brightnessAdjustmentFlags);
             newEvent.setFlags(newEvent.getFlags() | (userSetBrightnessChanged
                     ? BrightnessEvent.FLAG_USER_SET : 0));
-            Slog.i(TAG, newEvent.toString(/* includeTime= */ false));
+            Slog.i(mTag, newEvent.toString(/* includeTime= */ false));
 
             if (mBrightnessEventRingBuffer != null) {
                 mBrightnessEventRingBuffer.append(newEvent);
@@ -1671,9 +1683,9 @@
         // Note that we do not wait for the brightness ramp animation to complete before
         // reporting the display is ready because we only need to ensure the screen is in the
         // right power state even as it continues to converge on the desired brightness.
-        final boolean ready = mPendingScreenOnUnblocker == null &&
-                (!mColorFadeEnabled ||
-                        (!mColorFadeOnAnimator.isStarted() && !mColorFadeOffAnimator.isStarted()))
+        final boolean ready = mPendingScreenOnUnblocker == null
+                && (!mColorFadeEnabled || (!mColorFadeOnAnimator.isStarted()
+                        && !mColorFadeOffAnimator.isStarted()))
                 && mPowerState.waitUntilClean(mCleanListener);
         final boolean finished = ready
                 && !mScreenBrightnessRampAnimator.isAnimating();
@@ -1688,7 +1700,7 @@
         // Grab a wake lock if we have unfinished business.
         if (!finished && !mUnfinishedBusiness) {
             if (DEBUG) {
-                Slog.d(TAG, "Unfinished business...");
+                Slog.d(mTag, "Unfinished business...");
             }
             mCallbacks.acquireSuspendBlocker(mSuspendBlockerIdUnfinishedBusiness);
             mUnfinishedBusiness = true;
@@ -1702,7 +1714,7 @@
                     mDisplayReadyLocked = true;
 
                     if (DEBUG) {
-                        Slog.d(TAG, "Display ready!");
+                        Slog.d(mTag, "Display ready!");
                     }
                 }
             }
@@ -1712,7 +1724,7 @@
         // Release the wake lock when we have no unfinished business.
         if (finished && mUnfinishedBusiness) {
             if (DEBUG) {
-                Slog.d(TAG, "Finished business...");
+                Slog.d(mTag, "Finished business...");
             }
             mUnfinishedBusiness = false;
             mCallbacks.releaseSuspendBlocker(mSuspendBlockerIdUnfinishedBusiness);
@@ -1782,26 +1794,26 @@
             boolean changed = false;
 
             changed |=
-                mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.brightness,
-                        brightness);
+                    mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.brightness,
+                            brightness);
             changed |=
-                mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.adjustedBrightness,
-                        adjustedBrightness);
+                    mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.adjustedBrightness,
+                            adjustedBrightness);
             changed |=
-                mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.brightnessMin,
-                        minBrightness);
+                    mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.brightnessMin,
+                            minBrightness);
             changed |=
-                mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.brightnessMax,
-                        maxBrightness);
+                    mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.brightnessMax,
+                            maxBrightness);
             changed |=
-                mCachedBrightnessInfo.checkAndSetInt(mCachedBrightnessInfo.hbmMode,
-                        mHbmController.getHighBrightnessMode());
+                    mCachedBrightnessInfo.checkAndSetInt(mCachedBrightnessInfo.hbmMode,
+                            mHbmController.getHighBrightnessMode());
             changed |=
-                mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.hbmTransitionPoint,
-                        mHbmController.getTransitionPoint());
+                    mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.hbmTransitionPoint,
+                            mHbmController.getTransitionPoint());
             changed |=
-                mCachedBrightnessInfo.checkAndSetInt(mCachedBrightnessInfo.brightnessMaxReason,
-                        mBrightnessThrottler.getBrightnessMaxReason());
+                    mCachedBrightnessInfo.checkAndSetInt(mCachedBrightnessInfo.brightnessMaxReason,
+                            mBrightnessThrottler.getBrightnessMaxReason());
 
             return changed;
         }
@@ -1856,7 +1868,7 @@
             Trace.asyncTraceBegin(Trace.TRACE_TAG_POWER, SCREEN_ON_BLOCKED_TRACE_NAME, 0);
             mPendingScreenOnUnblocker = new ScreenOnUnblocker();
             mScreenOnBlockStartRealTime = SystemClock.elapsedRealtime();
-            Slog.i(TAG, "Blocking screen on until initial contents have been drawn.");
+            Slog.i(mTag, "Blocking screen on until initial contents have been drawn.");
         }
     }
 
@@ -1864,7 +1876,7 @@
         if (mPendingScreenOnUnblocker != null) {
             mPendingScreenOnUnblocker = null;
             long delay = SystemClock.elapsedRealtime() - mScreenOnBlockStartRealTime;
-            Slog.i(TAG, "Unblocked screen on after " + delay + " ms");
+            Slog.i(mTag, "Unblocked screen on after " + delay + " ms");
             Trace.asyncTraceEnd(Trace.TRACE_TAG_POWER, SCREEN_ON_BLOCKED_TRACE_NAME, 0);
         }
     }
@@ -1874,7 +1886,7 @@
             Trace.asyncTraceBegin(Trace.TRACE_TAG_POWER, SCREEN_OFF_BLOCKED_TRACE_NAME, 0);
             mPendingScreenOffUnblocker = new ScreenOffUnblocker();
             mScreenOffBlockStartRealTime = SystemClock.elapsedRealtime();
-            Slog.i(TAG, "Blocking screen off");
+            Slog.i(mTag, "Blocking screen off");
         }
     }
 
@@ -1882,7 +1894,7 @@
         if (mPendingScreenOffUnblocker != null) {
             mPendingScreenOffUnblocker = null;
             long delay = SystemClock.elapsedRealtime() - mScreenOffBlockStartRealTime;
-            Slog.i(TAG, "Unblocked screen off after " + delay + " ms");
+            Slog.i(mTag, "Unblocked screen off after " + delay + " ms");
             Trace.asyncTraceEnd(Trace.TRACE_TAG_POWER, SCREEN_OFF_BLOCKED_TRACE_NAME, 0);
         }
     }
@@ -1943,7 +1955,7 @@
         }
         if (!isOff
                 && (mReportedScreenStateToPolicy == REPORTED_TO_POLICY_SCREEN_OFF
-                        || mReportedScreenStateToPolicy == REPORTED_TO_POLICY_UNREPORTED)) {
+                || mReportedScreenStateToPolicy == REPORTED_TO_POLICY_UNREPORTED)) {
             setReportedScreenState(REPORTED_TO_POLICY_SCREEN_TURNING_ON);
             if (mPowerState.getColorFadeLevel() == 0.0f) {
                 blockScreenOn();
@@ -2008,7 +2020,7 @@
 
     private void animateScreenBrightness(float target, float sdrTarget, float rate) {
         if (DEBUG) {
-            Slog.d(TAG, "Animating brightness: target=" + target + ", sdrTarget=" + sdrTarget
+            Slog.d(mTag, "Animating brightness: target=" + target + ", sdrTarget=" + sdrTarget
                     + ", rate=" + rate);
         }
         if (mScreenBrightnessRampAnimator.animateTo(target, sdrTarget, rate)) {
@@ -2021,8 +2033,8 @@
 
     private void animateScreenStateChange(int target, boolean performScreenOffTransition) {
         // If there is already an animation in progress, don't interfere with it.
-        if (mColorFadeEnabled &&
-                (mColorFadeOnAnimator.isStarted() || mColorFadeOffAnimator.isStarted())) {
+        if (mColorFadeEnabled
+                && (mColorFadeOnAnimator.isStarted() || mColorFadeOffAnimator.isStarted())) {
             if (target != Display.STATE_ON) {
                 return;
             }
@@ -2069,9 +2081,8 @@
                 if (mPowerState.getColorFadeLevel() == 1.0f) {
                     mPowerState.dismissColorFade();
                 } else if (mPowerState.prepareColorFade(mContext,
-                        mColorFadeFadesConfig ?
-                                ColorFade.MODE_FADE :
-                                        ColorFade.MODE_WARM_UP)) {
+                        mColorFadeFadesConfig
+                                ? ColorFade.MODE_FADE : ColorFade.MODE_WARM_UP)) {
                     mColorFadeOnAnimator.start();
                 } else {
                     mColorFadeOnAnimator.end();
@@ -2173,8 +2184,8 @@
                 mPowerState.dismissColorFadeResources();
             } else if (performScreenOffTransition
                     && mPowerState.prepareColorFade(mContext,
-                            mColorFadeFadesConfig ?
-                                    ColorFade.MODE_FADE : ColorFade.MODE_COOL_DOWN)
+                    mColorFadeFadesConfig
+                            ? ColorFade.MODE_FADE : ColorFade.MODE_COOL_DOWN)
                     && mPowerState.getScreenState() != Display.STATE_OFF) {
                 // Perform the screen off animation.
                 mColorFadeOffAnimator.start();
@@ -2245,12 +2256,12 @@
         if (mProximitySensorEnabled
                 && mPendingProximity != PROXIMITY_UNKNOWN
                 && mPendingProximityDebounceTime >= 0) {
-            final long now = SystemClock.uptimeMillis();
+            final long now = mClock.uptimeMillis();
             if (mPendingProximityDebounceTime <= now) {
                 if (mProximity != mPendingProximity) {
                     // if the status of the sensor changed, stop ignoring.
                     mIgnoreProximityUntilChanged = false;
-                    Slog.i(TAG, "No longer ignoring proximity [" + mPendingProximity + "]");
+                    Slog.i(mTag, "No longer ignoring proximity [" + mPendingProximity + "]");
                 }
                 // Sensor reading accepted.  Apply the change then release the wake lock.
                 mProximity = mPendingProximity;
@@ -2435,7 +2446,7 @@
                 && mProximity == PROXIMITY_POSITIVE) {
             // Only ignore if it is still reporting positive (near)
             mIgnoreProximityUntilChanged = true;
-            Slog.i(TAG, "Ignoring proximity");
+            Slog.i(mTag, "Ignoring proximity");
             updatePowerState();
         }
     }
@@ -2505,25 +2516,25 @@
         pw.println("  mScreenBrightnessForVrRangeMaximum=" + mScreenBrightnessForVrRangeMaximum);
         pw.println("  mScreenBrightnessForVrDefault=" + mScreenBrightnessForVrDefault);
         pw.println("  mUseSoftwareAutoBrightnessConfig=" + mUseSoftwareAutoBrightnessConfig);
-        pw.println("  mAllowAutoBrightnessWhileDozingConfig=" +
-                mAllowAutoBrightnessWhileDozingConfig);
+        pw.println("  mAllowAutoBrightnessWhileDozingConfig="
+                + mAllowAutoBrightnessWhileDozingConfig);
         pw.println("  mSkipScreenOnBrightnessRamp=" + mSkipScreenOnBrightnessRamp);
         pw.println("  mColorFadeFadesConfig=" + mColorFadeFadesConfig);
         pw.println("  mColorFadeEnabled=" + mColorFadeEnabled);
         synchronized (mCachedBrightnessInfo) {
-            pw.println("  mCachedBrightnessInfo.brightness=" +
-                    mCachedBrightnessInfo.brightness.value);
-            pw.println("  mCachedBrightnessInfo.adjustedBrightness=" +
-                    mCachedBrightnessInfo.adjustedBrightness.value);
-            pw.println("  mCachedBrightnessInfo.brightnessMin=" +
-                    mCachedBrightnessInfo.brightnessMin.value);
-            pw.println("  mCachedBrightnessInfo.brightnessMax=" +
-                    mCachedBrightnessInfo.brightnessMax.value);
+            pw.println("  mCachedBrightnessInfo.brightness="
+                    + mCachedBrightnessInfo.brightness.value);
+            pw.println("  mCachedBrightnessInfo.adjustedBrightness="
+                    + mCachedBrightnessInfo.adjustedBrightness.value);
+            pw.println("  mCachedBrightnessInfo.brightnessMin="
+                    + mCachedBrightnessInfo.brightnessMin.value);
+            pw.println("  mCachedBrightnessInfo.brightnessMax="
+                    + mCachedBrightnessInfo.brightnessMax.value);
             pw.println("  mCachedBrightnessInfo.hbmMode=" + mCachedBrightnessInfo.hbmMode.value);
-            pw.println("  mCachedBrightnessInfo.hbmTransitionPoint=" +
-                    mCachedBrightnessInfo.hbmTransitionPoint.value);
-            pw.println("  mCachedBrightnessInfo.brightnessMaxReason =" +
-                    mCachedBrightnessInfo.brightnessMaxReason .value);
+            pw.println("  mCachedBrightnessInfo.hbmTransitionPoint="
+                    + mCachedBrightnessInfo.hbmTransitionPoint.value);
+            pw.println("  mCachedBrightnessInfo.brightnessMaxReason ="
+                    + mCachedBrightnessInfo.brightnessMaxReason.value);
         }
         pw.println("  mDisplayBlanksAfterDozeConfig=" + mDisplayBlanksAfterDozeConfig);
         pw.println("  mBrightnessBucketsInDozeConfig=" + mBrightnessBucketsInDozeConfig);
@@ -2706,7 +2717,7 @@
         }
 
         float hbmTransitionPoint = PowerManager.BRIGHTNESS_MAX;
-        synchronized(mCachedBrightnessInfo) {
+        synchronized (mCachedBrightnessInfo) {
             if (mCachedBrightnessInfo.hbmTransitionPoint == null) {
                 return;
             }
@@ -2740,10 +2751,8 @@
         }
     }
 
-
-
     private final class DisplayControllerHandler extends Handler {
-        public DisplayControllerHandler(Looper looper) {
+        DisplayControllerHandler(Looper looper) {
             super(looper, null, true /*async*/);
         }
 
@@ -2806,7 +2815,7 @@
                     break;
 
                 case MSG_BRIGHTNESS_RAMP_DONE:
-                    if (mPowerState != null)  {
+                    if (mPowerState != null) {
                         final float brightness = mPowerState.getScreenBrightness();
                         reportStats(brightness);
                     }
@@ -2823,7 +2832,7 @@
         @Override
         public void onSensorChanged(SensorEvent event) {
             if (mProximitySensorEnabled) {
-                final long time = SystemClock.uptimeMillis();
+                final long time = mClock.uptimeMillis();
                 final float distance = event.values[0];
                 boolean positive = distance >= 0.0f && distance < mProximityThreshold;
                 handleProximitySensorEvent(time, positive);
@@ -2838,7 +2847,7 @@
 
 
     private final class SettingsObserver extends ContentObserver {
-        public SettingsObserver(Handler handler) {
+        SettingsObserver(Handler handler) {
             super(handler);
         }
 
@@ -2892,19 +2901,68 @@
         }
     }
 
+    @VisibleForTesting
+    String getSuspendBlockerUnfinishedBusinessId(int displayId) {
+        return "[" + displayId + "]unfinished business";
+    }
+
+    String getSuspendBlockerOnStateChangedId(int displayId) {
+        return "[" + displayId + "]on state changed";
+    }
+
+    String getSuspendBlockerProxPositiveId(int displayId) {
+        return "[" + displayId + "]prox positive";
+    }
+
+    String getSuspendBlockerProxNegativeId(int displayId) {
+        return "[" + displayId + "]prox negative";
+    }
+
+    @VisibleForTesting
+    String getSuspendBlockerProxDebounceId(int displayId) {
+        return "[" + displayId + "]prox debounce";
+    }
+
+    /** Functional interface for providing time. */
+    @VisibleForTesting
+    interface Clock {
+        /**
+         * Returns current time in milliseconds since boot, not counting time spent in deep sleep.
+         */
+        long uptimeMillis();
+    }
+
+    @VisibleForTesting
+    static class Injector {
+        Clock getClock() {
+            return SystemClock::uptimeMillis;
+        }
+
+        DisplayPowerState getDisplayPowerState(DisplayBlanker blanker, ColorFade colorFade,
+                int displayId, int displayState) {
+            return new DisplayPowerState(blanker, colorFade, displayId, displayState);
+        }
+
+        DualRampAnimator<DisplayPowerState> getDualRampAnimator(DisplayPowerState dps,
+                FloatProperty<DisplayPowerState> firstProperty,
+                FloatProperty<DisplayPowerState> secondProperty) {
+            return new DualRampAnimator(dps, firstProperty, secondProperty);
+        }
+    }
+
     static class CachedBrightnessInfo {
         public MutableFloat brightness = new MutableFloat(PowerManager.BRIGHTNESS_INVALID_FLOAT);
         public MutableFloat adjustedBrightness =
-            new MutableFloat(PowerManager.BRIGHTNESS_INVALID_FLOAT);
+                new MutableFloat(PowerManager.BRIGHTNESS_INVALID_FLOAT);
         public MutableFloat brightnessMin =
-            new MutableFloat(PowerManager.BRIGHTNESS_INVALID_FLOAT);
+                new MutableFloat(PowerManager.BRIGHTNESS_INVALID_FLOAT);
         public MutableFloat brightnessMax =
-            new MutableFloat(PowerManager.BRIGHTNESS_INVALID_FLOAT);
+                new MutableFloat(PowerManager.BRIGHTNESS_INVALID_FLOAT);
         public MutableInt hbmMode = new MutableInt(BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF);
         public MutableFloat hbmTransitionPoint =
-            new MutableFloat(HighBrightnessModeController.HBM_TRANSITION_POINT_INVALID);
+                new MutableFloat(HighBrightnessModeController.HBM_TRANSITION_POINT_INVALID);
         public MutableInt brightnessMaxReason =
-            new MutableInt(BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE);
+                new MutableInt(BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE);
 
         public boolean checkAndSetFloat(MutableFloat mf, float f) {
             if (mf.value != f) {
diff --git a/services/core/java/com/android/server/display/RampAnimator.java b/services/core/java/com/android/server/display/RampAnimator.java
index 690ec3f..52b92c4 100644
--- a/services/core/java/com/android/server/display/RampAnimator.java
+++ b/services/core/java/com/android/server/display/RampAnimator.java
@@ -29,7 +29,6 @@
 class RampAnimator<T> {
     private final T mObject;
     private final FloatProperty<T> mProperty;
-    private final Choreographer mChoreographer;
 
     private float mCurrentValue;
     private float mTargetValue;
@@ -43,18 +42,16 @@
 
     private boolean mFirstTime = true;
 
-    private Listener mListener;
 
     RampAnimator(T object, FloatProperty<T> property) {
         mObject = object;
         mProperty = property;
-        mChoreographer = Choreographer.getInstance();
     }
 
     /**
      * Sets the maximum time that a brightness animation can take.
      */
-    public void setAnimationTimeLimits(long animationRampIncreaseMaxTimeMillis,
+    void setAnimationTimeLimits(long animationRampIncreaseMaxTimeMillis,
             long animationRampDecreaseMaxTimeMillis) {
         mAnimationIncreaseMaxTimeSecs = (animationRampIncreaseMaxTimeMillis > 0)
                 ? (animationRampIncreaseMaxTimeMillis / 1000.0f) : 0.0f;
@@ -63,7 +60,7 @@
     }
 
     /**
-     * Starts animating towards the specified value.
+     * Sets the animation target and the rate of this ramp animator.
      *
      * If this is the first time the property is being set or if the rate is 0,
      * the value jumps directly to the target.
@@ -72,7 +69,7 @@
      * @param rate The convergence rate in units per second, or 0 to set the value immediately.
      * @return True if the target differs from the previous target.
      */
-    public boolean animateTo(float targetLinear, float rate) {
+    boolean setAnimationTarget(float targetLinear, float rate) {
         // Convert the target from the linear into the HLG space.
         final float target = BrightnessUtils.convertLinearToGamma(targetLinear);
 
@@ -84,13 +81,7 @@
                 mTargetValue = target;
                 mCurrentValue = target;
                 setPropertyValue(target);
-                if (mAnimating) {
-                    mAnimating = false;
-                    cancelAnimationCallback();
-                }
-                if (mListener != null) {
-                    mListener.onAnimationEnd();
-                }
+                mAnimating = false;
                 return true;
             }
             return false;
@@ -127,7 +118,6 @@
             mAnimating = true;
             mAnimatedValue = mCurrentValue;
             mLastFrameTimeNanos = System.nanoTime();
-            postAnimationCallback();
         }
 
         return changed;
@@ -136,18 +126,11 @@
     /**
      * Returns true if the animation is running.
      */
-    public boolean isAnimating() {
+    boolean isAnimating() {
         return mAnimating;
     }
 
     /**
-     * Sets a listener to watch for animation events.
-     */
-    public void setListener(Listener listener) {
-        mListener = listener;
-    }
-
-    /**
      * Sets the brightness property by converting the given value from HLG space
      * into linear space.
      */
@@ -156,78 +139,53 @@
         mProperty.setValue(mObject, linearVal);
     }
 
-    private void postAnimationCallback() {
-        mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, mAnimationCallback, null);
-    }
+    void performNextAnimationStep(long frameTimeNanos) {
+        final float timeDelta = (frameTimeNanos - mLastFrameTimeNanos) * 0.000000001f;
+        mLastFrameTimeNanos = frameTimeNanos;
 
-    private void cancelAnimationCallback() {
-        mChoreographer.removeCallbacks(Choreographer.CALLBACK_ANIMATION, mAnimationCallback, null);
-    }
-
-    private final Runnable mAnimationCallback = new Runnable() {
-        @Override // Choreographer callback
-        public void run() {
-            final long frameTimeNanos = mChoreographer.getFrameTimeNanos();
-            final float timeDelta = (frameTimeNanos - mLastFrameTimeNanos)
-                    * 0.000000001f;
-            mLastFrameTimeNanos = frameTimeNanos;
-
-            // Advance the animated value towards the target at the specified rate
-            // and clamp to the target. This gives us the new current value but
-            // we keep the animated value around to allow for fractional increments
-            // towards the target.
-            final float scale = ValueAnimator.getDurationScale();
-            if (scale == 0) {
-                // Animation off.
-                mAnimatedValue = mTargetValue;
+        // Advance the animated value towards the target at the specified rate
+        // and clamp to the target. This gives us the new current value but
+        // we keep the animated value around to allow for fractional increments
+        // towards the target.
+        final float scale = ValueAnimator.getDurationScale();
+        if (scale == 0) {
+            // Animation off.
+            mAnimatedValue = mTargetValue;
+        } else {
+            final float amount = timeDelta * mRate / scale;
+            if (mTargetValue > mCurrentValue) {
+                mAnimatedValue = Math.min(mAnimatedValue + amount, mTargetValue);
             } else {
-                final float amount = timeDelta * mRate / scale;
-                if (mTargetValue > mCurrentValue) {
-                    mAnimatedValue = Math.min(mAnimatedValue + amount, mTargetValue);
-                } else {
-                    mAnimatedValue = Math.max(mAnimatedValue - amount, mTargetValue);
-                }
-            }
-            final float oldCurrentValue = mCurrentValue;
-            mCurrentValue = mAnimatedValue;
-            if (oldCurrentValue != mCurrentValue) {
-                setPropertyValue(mCurrentValue);
-            }
-            if (mTargetValue != mCurrentValue) {
-                postAnimationCallback();
-            } else {
-                mAnimating = false;
-                if (mListener != null) {
-                    mListener.onAnimationEnd();
-                }
+                mAnimatedValue = Math.max(mAnimatedValue - amount, mTargetValue);
             }
         }
-    };
+        final float oldCurrentValue = mCurrentValue;
+        mCurrentValue = mAnimatedValue;
+        if (oldCurrentValue != mCurrentValue) {
+            setPropertyValue(mCurrentValue);
+        }
+        if (mTargetValue == mCurrentValue) {
+            mAnimating = false;
+        }
+    }
 
     public interface Listener {
         void onAnimationEnd();
     }
 
     static class DualRampAnimator<T> {
+        private final Choreographer mChoreographer;
         private final RampAnimator<T> mFirst;
         private final RampAnimator<T> mSecond;
-        private final Listener mInternalListener = new Listener() {
-            @Override
-            public void onAnimationEnd() {
-                if (mListener != null && !isAnimating()) {
-                    mListener.onAnimationEnd();
-                }
-            }
-        };
 
         private Listener mListener;
+        private boolean mAwaitingCallback;
 
         DualRampAnimator(T object, FloatProperty<T> firstProperty,
                 FloatProperty<T> secondProperty) {
-            mFirst = new RampAnimator(object, firstProperty);
-            mFirst.setListener(mInternalListener);
-            mSecond = new RampAnimator(object, secondProperty);
-            mSecond.setListener(mInternalListener);
+            mChoreographer = Choreographer.getInstance();
+            mFirst = new RampAnimator<>(object, firstProperty);
+            mSecond = new RampAnimator<>(object, secondProperty);
         }
 
         /**
@@ -253,17 +211,56 @@
          * @return True if either target differs from the previous target.
          */
         public boolean animateTo(float linearFirstTarget, float linearSecondTarget, float rate) {
-            final boolean firstRetval = mFirst.animateTo(linearFirstTarget, rate);
-            final boolean secondRetval = mSecond.animateTo(linearSecondTarget, rate);
-            return firstRetval && secondRetval;
+            boolean animationTargetChanged = mFirst.setAnimationTarget(linearFirstTarget, rate);
+            animationTargetChanged |= mSecond.setAnimationTarget(linearSecondTarget, rate);
+            boolean shouldBeAnimating = isAnimating();
+
+            if (shouldBeAnimating != mAwaitingCallback) {
+                if (shouldBeAnimating) {
+                    mAwaitingCallback = true;
+                    postAnimationCallback();
+                } else if (mAwaitingCallback) {
+                    mChoreographer.removeCallbacks(Choreographer.CALLBACK_ANIMATION,
+                            mAnimationCallback, null);
+                    mAwaitingCallback = false;
+                }
+            }
+            return animationTargetChanged;
         }
 
+        /**
+        * Sets a listener to watch for animation events.
+        */
         public void setListener(Listener listener) {
             mListener = listener;
         }
 
+        /**
+        * Returns true if the animation is running.
+        */
         public boolean isAnimating() {
-            return mFirst.isAnimating() && mSecond.isAnimating();
+            return mFirst.isAnimating() || mSecond.isAnimating();
         }
+
+        private void postAnimationCallback() {
+            mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, mAnimationCallback, null);
+        }
+
+        private final Runnable mAnimationCallback = new Runnable() {
+            @Override // Choreographer callback
+            public void run() {
+                long frameTimeNanos = mChoreographer.getFrameTimeNanos();
+                mFirst.performNextAnimationStep(frameTimeNanos);
+                mSecond.performNextAnimationStep(frameTimeNanos);
+                if (isAnimating()) {
+                    postAnimationCallback();
+                } else {
+                    if (mListener != null) {
+                        mListener.onAnimationEnd();
+                    }
+                    mAwaitingCallback = false;
+                }
+            }
+        };
     }
 }
diff --git a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
index 9f44765..479629e 100644
--- a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
@@ -35,6 +35,7 @@
 import static com.android.server.display.DisplayDeviceInfo.FLAG_TOUCH_FEEDBACK_DISABLED;
 import static com.android.server.display.DisplayDeviceInfo.FLAG_TRUSTED;
 
+import android.annotation.Nullable;
 import android.content.Context;
 import android.graphics.Point;
 import android.hardware.display.IVirtualDisplayCallback;
@@ -110,15 +111,20 @@
         } else {
             uniqueId = UNIQUE_ID_PREFIX + ownerPackageName + ":" + uniqueId;
         }
+        MediaProjectionCallback mediaProjectionCallback =  null;
+        if (projection != null) {
+            mediaProjectionCallback = new MediaProjectionCallback(appToken);
+        }
         VirtualDisplayDevice device = new VirtualDisplayDevice(displayToken, appToken,
-                ownerUid, ownerPackageName, surface, flags, new Callback(callback, mHandler),
+                ownerUid, ownerPackageName, surface, flags,
+                new Callback(callback, mHandler), projection, mediaProjectionCallback,
                 uniqueId, uniqueIndex, virtualDisplayConfig);
 
         mVirtualDisplayDevices.put(appToken, device);
 
         try {
             if (projection != null) {
-                projection.registerCallback(new MediaProjectionCallback(appToken));
+                projection.registerCallback(mediaProjectionCallback);
             }
             appToken.linkToDeath(device, 0);
         } catch (RemoteException ex) {
@@ -223,6 +229,8 @@
         final String mName;
         private final int mFlags;
         private final Callback mCallback;
+        @Nullable private final IMediaProjection mProjection;
+        @Nullable private final IMediaProjectionCallback mMediaProjectionCallback;
 
         private int mWidth;
         private int mHeight;
@@ -240,7 +248,8 @@
 
         public VirtualDisplayDevice(IBinder displayToken, IBinder appToken,
                 int ownerUid, String ownerPackageName, Surface surface, int flags,
-                Callback callback, String uniqueId, int uniqueIndex,
+                Callback callback, IMediaProjection projection,
+                IMediaProjectionCallback mediaProjectionCallback, String uniqueId, int uniqueIndex,
                 VirtualDisplayConfig virtualDisplayConfig) {
             super(VirtualDisplayAdapter.this, displayToken, uniqueId, getContext());
             mAppToken = appToken;
@@ -254,6 +263,8 @@
             mSurface = surface;
             mFlags = flags;
             mCallback = callback;
+            mProjection = projection;
+            mMediaProjectionCallback = mediaProjectionCallback;
             mDisplayState = Display.STATE_UNKNOWN;
             mPendingChanges |= PENDING_SURFACE_CHANGE;
             mUniqueIndex = uniqueIndex;
@@ -269,6 +280,13 @@
                 Slog.i(TAG, "Virtual display device released because application token died: "
                     + mOwnerPackageName);
                 destroyLocked(false);
+                if (mProjection != null && mMediaProjectionCallback != null) {
+                    try {
+                        mProjection.unregisterCallback(mMediaProjectionCallback);
+                    } catch (RemoteException e) {
+                        Slog.w(TAG, "Failed to unregister callback in binderDied", e);
+                    }
+                }
                 sendDisplayDeviceEventLocked(this, DISPLAY_DEVICE_EVENT_REMOVED);
             }
         }
@@ -279,6 +297,13 @@
                 mSurface = null;
             }
             SurfaceControl.destroyDisplay(getDisplayTokenLocked());
+            if (mProjection != null && mMediaProjectionCallback != null) {
+                try {
+                    mProjection.unregisterCallback(mMediaProjectionCallback);
+                } catch (RemoteException e) {
+                    Slog.w(TAG, "Failed to unregister callback in destroy", e);
+                }
+            }
             if (binderAlive) {
                 mCallback.dispatchDisplayStopped();
             }
diff --git a/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java b/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java
index 8180e66..0c889c2 100644
--- a/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java
+++ b/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java
@@ -32,11 +32,10 @@
     public static final String TAG = HandwritingEventReceiverSurface.class.getSimpleName();
     static final boolean DEBUG = HandwritingModeController.DEBUG;
 
-    // Place the layer below the highest layer to place it under gesture monitors. If the surface
-    // is above gesture monitors, then edge-back and swipe-up gestures won't work when this surface
-    // is intercepting.
+    // Place the layer at the highest layer so stylus cannot trigger gesture monitors
+    // (e.g. navigation bar, edge-back, etc) while handwriting is ongoing.
     // TODO(b/217538817): Specify the ordering in WM by usage.
-    private static final int HANDWRITING_SURFACE_LAYER = Integer.MAX_VALUE - 1;
+    private static final int HANDWRITING_SURFACE_LAYER = Integer.MAX_VALUE;
 
     private final InputWindowHandle mWindowHandle;
     private final InputChannel mClientChannel;
diff --git a/services/core/java/com/android/server/locksettings/BiometricDeferredQueue.java b/services/core/java/com/android/server/locksettings/BiometricDeferredQueue.java
index 2bdeab4..f144cf8 100644
--- a/services/core/java/com/android/server/locksettings/BiometricDeferredQueue.java
+++ b/services/core/java/com/android/server/locksettings/BiometricDeferredQueue.java
@@ -44,7 +44,6 @@
 public class BiometricDeferredQueue {
     private static final String TAG = "BiometricDeferredQueue";
 
-    @NonNull private final Context mContext;
     @NonNull private final SyntheticPasswordManager mSpManager;
     @NonNull private final Handler mHandler;
     @Nullable private FingerprintManager mFingerprintManager;
@@ -131,9 +130,7 @@
         mFaceResetLockoutTask = null;
     };
 
-    BiometricDeferredQueue(@NonNull Context context, @NonNull SyntheticPasswordManager spManager,
-            @NonNull Handler handler) {
-        mContext = context;
+    BiometricDeferredQueue(@NonNull SyntheticPasswordManager spManager, @NonNull Handler handler) {
         mSpManager = spManager;
         mHandler = handler;
         mPendingResetLockoutsForFingerprint = new ArrayList<>();
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index b86fa7a..c5f73625 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -67,7 +67,6 @@
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
 import android.content.pm.UserInfo;
-import android.content.res.Resources;
 import android.database.ContentObserver;
 import android.database.sqlite.SQLiteDatabase;
 import android.hardware.authsecret.V1_0.IAuthSecret;
@@ -238,7 +237,6 @@
     @VisibleForTesting
     protected final SyntheticPasswordManager mSpManager;
 
-    private final KeyStore mKeyStore;
     private final java.security.KeyStore mJavaKeyStore;
     private final RecoverableKeyStoreManager mRecoverableKeyStoreManager;
     private ManagedProfilePasswordCache mManagedProfilePasswordCache;
@@ -570,7 +568,6 @@
     protected LockSettingsService(Injector injector) {
         mInjector = injector;
         mContext = injector.getContext();
-        mKeyStore = injector.getKeyStore();
         mJavaKeyStore = injector.getJavaKeyStore();
         mRecoverableKeyStoreManager = injector.getRecoverableKeyStoreManager();
         mHandler = injector.getHandler(injector.getServiceThread());
@@ -595,7 +592,7 @@
 
         mSpManager = injector.getSyntheticPasswordManager(mStorage);
         mManagedProfilePasswordCache = injector.getManagedProfilePasswordCache(mJavaKeyStore);
-        mBiometricDeferredQueue = new BiometricDeferredQueue(mContext, mSpManager, mHandler);
+        mBiometricDeferredQueue = new BiometricDeferredQueue(mSpManager, mHandler);
 
         mRebootEscrowManager = injector.getRebootEscrowManager(new RebootEscrowCallbacks(),
                 mStorage);
@@ -637,7 +634,6 @@
     }
 
     private void showEncryptionNotificationForProfile(UserHandle user, String reason) {
-        Resources r = mContext.getResources();
         CharSequence title = getEncryptionNotificationTitle();
         CharSequence message = getEncryptionNotificationMessage();
         CharSequence detail = getEncryptionNotificationDetail();
@@ -657,7 +653,7 @@
         PendingIntent intent = PendingIntent.getActivity(mContext, 0, unlockIntent,
                 PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE_UNAUDITED);
 
-        Slog.d(TAG, String.format("showing encryption notification, user: %d; reason: %s",
+        Slog.d(TAG, TextUtils.formatSimple("showing encryption notification, user: %d; reason: %s",
                 user.getIdentifier(), reason));
 
         showEncryptionNotification(user, title, message, detail, intent);
@@ -839,7 +835,7 @@
         if (mContext.checkCallingOrSelfPermission(PERMISSION) != PERMISSION_GRANTED) {
             EventLog.writeEvent(0x534e4554, "28251513", getCallingUid(), "");  // SafetyNet
         }
-        checkWritePermission(UserHandle.USER_SYSTEM);
+        checkWritePermission();
 
         mHasSecureLockScreen = mContext.getPackageManager()
                 .hasSystemFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN);
@@ -979,7 +975,7 @@
         }
     }
 
-    private final void checkWritePermission(int userId) {
+    private final void checkWritePermission() {
         mContext.enforceCallingOrSelfPermission(PERMISSION, "LockSettingsWrite");
     }
 
@@ -987,7 +983,7 @@
         mContext.enforceCallingOrSelfPermission(PERMISSION, "LockSettingsRead");
     }
 
-    private final void checkPasswordHavePermission(int userId) {
+    private final void checkPasswordHavePermission() {
         if (mContext.checkCallingOrSelfPermission(PERMISSION) != PERMISSION_GRANTED) {
             EventLog.writeEvent(0x534e4554, "28251513", getCallingUid(), "");  // SafetyNet
         }
@@ -1056,7 +1052,7 @@
     @Override
     public void setSeparateProfileChallengeEnabled(int userId, boolean enabled,
             LockscreenCredential profileUserPassword) {
-        checkWritePermission(userId);
+        checkWritePermission();
         if (!mHasSecureLockScreen
                 && profileUserPassword != null
                 && profileUserPassword.getType() != CREDENTIAL_TYPE_NONE) {
@@ -1103,19 +1099,19 @@
 
     @Override
     public void setBoolean(String key, boolean value, int userId) {
-        checkWritePermission(userId);
+        checkWritePermission();
         mStorage.setBoolean(key, value, userId);
     }
 
     @Override
     public void setLong(String key, long value, int userId) {
-        checkWritePermission(userId);
+        checkWritePermission();
         mStorage.setLong(key, value, userId);
     }
 
     @Override
     public void setString(String key, String value, int userId) {
-        checkWritePermission(userId);
+        checkWritePermission();
         mStorage.setString(key, value, userId);
     }
 
@@ -1154,7 +1150,7 @@
      */
     @Override
     public int getCredentialType(int userId) {
-        checkPasswordHavePermission(userId);
+        checkPasswordHavePermission();
         return getCredentialTypeInternal(userId);
     }
 
@@ -1627,7 +1623,7 @@
             }
 
             onSyntheticPasswordKnown(userId, sp);
-            setLockCredentialWithSpLocked(credential, sp, userId, isLockTiedToParent);
+            setLockCredentialWithSpLocked(credential, sp, userId);
             sendCredentialsOnChangeIfRequired(credential, userId, isLockTiedToParent);
             return true;
         }
@@ -1641,15 +1637,18 @@
         if (newCredential.isPattern()) {
             setBoolean(LockPatternUtils.PATTERN_EVER_CHOSEN_KEY, true, userHandle);
         }
+        updatePasswordHistory(newCredential, userHandle);
         mContext.getSystemService(TrustManager.class).reportEnabledTrustAgentsChanged(userHandle);
     }
 
     /**
      * Store the hash of the new password in the password history list, if device policy enforces
      * a password history requirement.
+     *
+     * This must not be called while the mSpManager lock is held, as this calls into
+     * DevicePolicyManagerService to get the requested password history length.
      */
-    private void updatePasswordHistory(SyntheticPassword sp, LockscreenCredential password,
-            int userHandle, boolean isLockTiedToParent) {
+    private void updatePasswordHistory(LockscreenCredential password, int userHandle) {
         if (password.isNone()) {
             return;
         }
@@ -1657,10 +1656,6 @@
             // Do not keep track of historical patterns
             return;
         }
-        if (isLockTiedToParent) {
-            // Do not keep track of historical auto-generated profile passwords
-            return;
-        }
         // Add the password to the password history.
         String passwordHistory = getString(
                 LockPatternUtils.PASSWORD_HISTORY_KEY, /* defaultValue= */ null, userHandle);
@@ -1671,9 +1666,16 @@
         if (passwordHistoryLength == 0) {
             passwordHistory = "";
         } else {
-            final byte[] hashFactor = sp.derivePasswordHashFactor();
+            final byte[] hashFactor = getHashFactor(password, userHandle);
             final byte[] salt = getSalt(userHandle).getBytes();
             String hash = password.passwordToHistoryHash(salt, hashFactor);
+            if (hash == null) {
+                // This should never happen, as all information needed to compute the hash should be
+                // available.  In particular, unwrapping the SP in getHashFactor() should always
+                // succeed, as we're using the LSKF that was just set.
+                Slog.e(TAG, "Failed to compute password hash; password history won't be updated");
+                return;
+            }
             if (TextUtils.isEmpty(passwordHistory)) {
                 passwordHistory = hash;
             } else {
@@ -1961,7 +1963,7 @@
 
     @Override
     public void resetKeyStore(int userId) {
-        checkWritePermission(userId);
+        checkWritePermission();
         if (DEBUG) Slog.v(TAG, "Reset keystore for user: " + userId);
         List<Integer> profileUserIds = new ArrayList<>();
         List<LockscreenCredential> profileUserDecryptedPasswords = new ArrayList<>();
@@ -2269,7 +2271,7 @@
 
     @Override
     public void requireStrongAuth(int strongAuthReason, int userId) {
-        checkWritePermission(userId);
+        checkWritePermission();
         mStrongAuth.requireStrongAuth(strongAuthReason, userId);
     }
 
@@ -2287,7 +2289,7 @@
 
     @Override
     public void userPresent(int userId) {
-        checkWritePermission(userId);
+        checkWritePermission();
         mStrongAuth.reportUnlock(userId);
     }
 
@@ -2315,9 +2317,9 @@
         final int origPid = Binder.getCallingPid();
         final int origUid = Binder.getCallingUid();
 
+        Slog.e(TAG, "Caller pid " + origPid + " Caller uid " + origUid);
         // The original identity is an opaque integer.
         final long origId = Binder.clearCallingIdentity();
-        Slog.e(TAG, "Caller pid " + origPid + " Caller uid " + origUid);
         try {
             final LockSettingsShellCommand command =
                     new LockSettingsShellCommand(new LockPatternUtils(mContext), mContext, origPid,
@@ -2644,7 +2646,7 @@
      */
     @GuardedBy("mSpManager")
     private long setLockCredentialWithSpLocked(LockscreenCredential credential,
-            SyntheticPassword sp, int userId, boolean isLockTiedToParent) {
+            SyntheticPassword sp, int userId) {
         if (DEBUG) Slog.d(TAG, "setLockCredentialWithSpLocked: user=" + userId);
         final int savedCredentialType = getCredentialTypeInternal(userId);
         final long oldProtectorId = getCurrentLskfBasedProtectorId(userId);
@@ -2682,7 +2684,6 @@
         LockPatternUtils.invalidateCredentialTypeCache();
         synchronizeUnifiedWorkChallengeForProfiles(userId, profilePasswords);
 
-        updatePasswordHistory(sp, credential, userId, isLockTiedToParent);
         setUserPasswordMetrics(credential, userId);
         mManagedProfilePasswordCache.removePassword(userId);
         if (savedCredentialType != CREDENTIAL_TYPE_NONE) {
@@ -2842,7 +2843,7 @@
         synchronized (mSpManager) {
             disableEscrowTokenOnNonManagedDevicesIfNeeded(userId);
             for (long handle : mSpManager.getPendingTokensForUser(userId)) {
-                Slog.i(TAG, String.format("activateEscrowTokens: %x %d ", handle, userId));
+                Slog.i(TAG, TextUtils.formatSimple("activateEscrowTokens: %x %d ", handle, userId));
                 mSpManager.createTokenBasedProtector(handle, sp, userId);
             }
         }
@@ -2928,8 +2929,7 @@
             return false;
         }
         onSyntheticPasswordKnown(userId, result.syntheticPassword);
-        setLockCredentialWithSpLocked(credential, result.syntheticPassword, userId,
-                /* isLockTiedToParent= */ false);
+        setLockCredentialWithSpLocked(credential, result.syntheticPassword, userId);
         return true;
     }
 
@@ -3004,14 +3004,14 @@
             pw.println("User " + userId);
             pw.increaseIndent();
             synchronized (mSpManager) {
-                pw.println(String.format("LSKF-based SP protector ID: %x",
+                pw.println(TextUtils.formatSimple("LSKF-based SP protector ID: %x",
                         getCurrentLskfBasedProtectorId(userId)));
-                pw.println(String.format("LSKF last changed: %s (previous protector: %x)",
+                pw.println(TextUtils.formatSimple("LSKF last changed: %s (previous protector: %x)",
                         timestampToString(getLong(LSKF_LAST_CHANGED_TIME_KEY, 0, userId)),
                         getLong(PREV_LSKF_BASED_PROTECTOR_ID_KEY, 0, userId)));
             }
             try {
-                pw.println(String.format("SID: %x",
+                pw.println(TextUtils.formatSimple("SID: %x",
                         getGateKeeperService().getSecureUserId(userId)));
             } catch (RemoteException e) {
                 // ignore.
@@ -3022,7 +3022,7 @@
             pw.println("CredentialType: " + credentialTypeToString(
                     getCredentialTypeInternal(userId)));
             pw.println("SeparateChallenge: " + getSeparateProfileChallengeEnabledInternal(userId));
-            pw.println(String.format("Metrics: %s",
+            pw.println(TextUtils.formatSimple("Metrics: %s",
                     getUserPasswordMetrics(userId) != null ? "known" : "unknown"));
             pw.decreaseIndent();
         }
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
index e5b50362..db036b0 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
@@ -17,7 +17,6 @@
 package com.android.server.locksettings;
 
 import static android.content.Context.USER_SERVICE;
-import static android.text.TextUtils.formatSimple;
 
 import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
 import static com.android.internal.widget.LockPatternUtils.USER_FRP;
@@ -437,7 +436,7 @@
     }
 
     private File getSyntheticPasswordStateFileForUser(int userId, long protectorId, String name) {
-        String fileName = formatSimple("%016x.%s", protectorId, name);
+        String fileName = TextUtils.formatSimple("%016x.%s", protectorId, name);
         return new File(getSyntheticPasswordDirectoryForUser(userId), fileName);
     }
 
@@ -643,13 +642,13 @@
         final UserManager um = UserManager.get(mContext);
         for (UserInfo user : um.getUsers()) {
             File userPath = getSyntheticPasswordDirectoryForUser(user.id);
-            pw.println(String.format("User %d [%s]:", user.id, userPath));
+            pw.println(TextUtils.formatSimple("User %d [%s]:", user.id, userPath));
             pw.increaseIndent();
             File[] files = userPath.listFiles();
             if (files != null) {
                 Arrays.sort(files);
                 for (File file : files) {
-                    pw.println(String.format("%6d %s %s", file.length(),
+                    pw.println(TextUtils.formatSimple("%6d %s %s", file.length(),
                             LockSettingsService.timestampToString(file.lastModified()),
                             file.getName()));
                 }
diff --git a/services/core/java/com/android/server/locksettings/PasswordSlotManager.java b/services/core/java/com/android/server/locksettings/PasswordSlotManager.java
index 17aca15..21fb403 100644
--- a/services/core/java/com/android/server/locksettings/PasswordSlotManager.java
+++ b/services/core/java/com/android/server/locksettings/PasswordSlotManager.java
@@ -72,8 +72,6 @@
 
     /**
      * Notify the manager of which slots are definitively in use by the current OS image.
-     *
-     * @throws RuntimeException
      */
     public void refreshActiveSlots(Set<Integer> activeSlots) throws RuntimeException {
         if (mSlotMap == null) {
@@ -103,8 +101,6 @@
 
     /**
      * Mark the given slot as in use by the current OS image.
-     *
-     * @throws RuntimeException
      */
     public void markSlotInUse(int slot) throws RuntimeException {
         ensureSlotMapLoaded();
@@ -117,8 +113,6 @@
 
     /**
      * Mark the given slot as no longer in use by the current OS image.
-     *
-     * @throws RuntimeException
      */
     public void markSlotDeleted(int slot) throws RuntimeException {
         ensureSlotMapLoaded();
diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowKeyStoreManager.java b/services/core/java/com/android/server/locksettings/RebootEscrowKeyStoreManager.java
index da29368..41cdb42 100644
--- a/services/core/java/com/android/server/locksettings/RebootEscrowKeyStoreManager.java
+++ b/services/core/java/com/android/server/locksettings/RebootEscrowKeyStoreManager.java
@@ -64,9 +64,9 @@
     private SecretKey getKeyStoreEncryptionKeyLocked() {
         try {
             KeyStore keyStore = KeyStore.getInstance(ANDROID_KEY_STORE_PROVIDER);
-            KeyStore.LoadStoreParameter loadStoreParameter = null;
             // Load from the specific namespace if keystore2 is enabled.
-            loadStoreParameter = new AndroidKeyStoreLoadStoreParameter(KEY_STORE_NAMESPACE);
+            KeyStore.LoadStoreParameter loadStoreParameter =
+                new AndroidKeyStoreLoadStoreParameter(KEY_STORE_NAMESPACE);
             keyStore.load(loadStoreParameter);
             return (SecretKey) keyStore.getKey(REBOOT_ESCROW_KEY_STORE_ENCRYPTION_KEY_NAME,
                     null);
@@ -86,9 +86,9 @@
         synchronized (mKeyStoreLock) {
             try {
                 KeyStore keyStore = KeyStore.getInstance(ANDROID_KEY_STORE_PROVIDER);
-                KeyStore.LoadStoreParameter loadStoreParameter = null;
                 // Load from the specific namespace if keystore2 is enabled.
-                loadStoreParameter = new AndroidKeyStoreLoadStoreParameter(KEY_STORE_NAMESPACE);
+                KeyStore.LoadStoreParameter loadStoreParameter =
+                    new AndroidKeyStoreLoadStoreParameter(KEY_STORE_NAMESPACE);
                 keyStore.load(loadStoreParameter);
                 keyStore.deleteEntry(REBOOT_ESCROW_KEY_STORE_ENCRYPTION_KEY_NAME);
             } catch (IOException | GeneralSecurityException e) {
diff --git a/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java b/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java
index a931844..2a6ae44 100644
--- a/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java
+++ b/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java
@@ -23,6 +23,7 @@
 import android.security.keystore2.AndroidKeyStoreLoadStoreParameter;
 import android.system.keystore2.Domain;
 import android.system.keystore2.KeyDescriptor;
+import android.text.TextUtils;
 import android.util.Slog;
 
 import com.android.internal.util.ArrayUtils;
@@ -301,7 +302,7 @@
             // Treat this as a success so we don't migrate again.
             return true;
         } else {
-            Slog.e(TAG, String.format("Failed to migrate key: %d", err));
+            Slog.e(TAG, TextUtils.formatSimple("Failed to migrate key: %d", err));
             return false;
         }
     }
diff --git a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
index 5b75b6a..f1afb96 100644
--- a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
+++ b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
@@ -36,6 +36,7 @@
 import android.security.Scrypt;
 import android.service.gatekeeper.GateKeeperResponse;
 import android.service.gatekeeper.IGateKeeperService;
+import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Slog;
@@ -590,7 +591,7 @@
         // Remove potential persistent state (in RPMB), to prevent them from accumulating and
         // causing problems.
         try {
-            gatekeeper.clearSecureUserId(fakeUid(userId));
+            gatekeeper.clearSecureUserId(fakeUserId(userId));
         } catch (RemoteException ignore) {
             Slog.w(TAG, "Failed to clear SID from gatekeeper");
         }
@@ -800,13 +801,13 @@
             // In case GK enrollment leaves persistent state around (in RPMB), this will nuke them
             // to prevent them from accumulating and causing problems.
             try {
-                gatekeeper.clearSecureUserId(fakeUid(userId));
+                gatekeeper.clearSecureUserId(fakeUserId(userId));
             } catch (RemoteException ignore) {
                 Slog.w(TAG, "Failed to clear SID from gatekeeper");
             }
             GateKeeperResponse response;
             try {
-                response = gatekeeper.enroll(fakeUid(userId), null, null,
+                response = gatekeeper.enroll(fakeUserId(userId), null, null,
                         stretchedLskfToGkPassword(stretchedLskf));
             } catch (RemoteException e) {
                 throw new IllegalStateException("Failed to enroll LSKF for new SP protector for "
@@ -840,7 +841,7 @@
 
             GateKeeperResponse response;
             try {
-                response = gatekeeper.verifyChallenge(fakeUid(persistentData.userId),
+                response = gatekeeper.verifyChallenge(fakeUserId(persistentData.userId),
                         0 /* challenge */, pwd.passwordHandle,
                         stretchedLskfToGkPassword(stretchedLskf));
             } catch (RemoteException e) {
@@ -1029,7 +1030,7 @@
                     userId));
 
         if (!credential.checkAgainstStoredType(pwd.credentialType)) {
-            Slog.e(TAG, String.format("Credential type mismatch: expected %d actual %d",
+            Slog.e(TAG, TextUtils.formatSimple("Credential type mismatch: expected %d actual %d",
                     pwd.credentialType, credential.getType()));
             result.gkResponse = VerifyCredentialResponse.ERROR;
             return result;
@@ -1059,7 +1060,7 @@
             byte[] gkPassword = stretchedLskfToGkPassword(stretchedLskf);
             GateKeeperResponse response;
             try {
-                response = gatekeeper.verifyChallenge(fakeUid(userId), 0L,
+                response = gatekeeper.verifyChallenge(fakeUserId(userId), 0L,
                         pwd.passwordHandle, gkPassword);
             } catch (RemoteException e) {
                 Slog.e(TAG, "gatekeeper verify failed", e);
@@ -1072,7 +1073,7 @@
                 if (response.getShouldReEnroll()) {
                     GateKeeperResponse reenrollResponse;
                     try {
-                        reenrollResponse = gatekeeper.enroll(fakeUid(userId),
+                        reenrollResponse = gatekeeper.enroll(fakeUserId(userId),
                                 pwd.passwordHandle, gkPassword, gkPassword);
                     } catch (RemoteException e) {
                         Slog.w(TAG, "Fail to invoke gatekeeper.enroll", e);
@@ -1452,8 +1453,8 @@
         return result;
     }
 
-    private int fakeUid(int uid) {
-        return 100000 + uid;
+    private int fakeUserId(int userId) {
+        return 100000 + userId;
     }
 
     protected static byte[] secureRandom(int length) {
@@ -1466,7 +1467,7 @@
     }
 
     private String getProtectorKeyAlias(long protectorId) {
-        return String.format("%s%x", PROTECTOR_KEY_ALIAS_PREFIX, protectorId);
+        return TextUtils.formatSimple("%s%x", PROTECTOR_KEY_ALIAS_PREFIX, protectorId);
     }
 
     private byte[] stretchLskf(LockscreenCredential credential, PasswordData data) {
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java
index f32af54..7009a41 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java
@@ -75,7 +75,6 @@
             "com.android.server.locksettings.recoverablekeystore/platform/";
     private static final String ENCRYPT_KEY_ALIAS_SUFFIX = "encrypt";
     private static final String DECRYPT_KEY_ALIAS_SUFFIX = "decrypt";
-    private static final int USER_AUTHENTICATION_VALIDITY_DURATION_SECONDS = 15;
     private static final String KEY_WRAP_CIPHER_ALGORITHM = "AES/GCM/NoPadding";
     private static final int GCM_TAG_LENGTH_BITS = 128;
     // Only used for checking if a key is usable
@@ -184,7 +183,6 @@
             invalidatePlatformKey(userId, generationId);
             nextId = generationId + 1;
         }
-        generationId = Math.max(generationId, MIN_GENERATION_ID_FOR_UNLOCKED_DEVICE_REQUIRED);
         generateAndLoadKey(userId, nextId);
     }
 
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/CleanupManager.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/CleanupManager.java
index be35b50..7d5fd65 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/CleanupManager.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/CleanupManager.java
@@ -35,7 +35,6 @@
 public class CleanupManager {
     private static final String TAG = "CleanupManager";
 
-    private final Context mContext;
     private final UserManager mUserManager;
     private final RecoverableKeyStoreDb mDatabase;
     private final RecoverySnapshotStorage mSnapshotStorage;
@@ -54,7 +53,6 @@
             RecoverableKeyStoreDb recoverableKeyStoreDb,
             ApplicationKeyStorage applicationKeyStorage) {
         return new CleanupManager(
-                context,
                 snapshotStorage,
                 recoverableKeyStoreDb,
                 UserManager.get(context),
@@ -63,12 +61,10 @@
 
     @VisibleForTesting
     CleanupManager(
-            Context context,
             RecoverySnapshotStorage snapshotStorage,
             RecoverableKeyStoreDb recoverableKeyStoreDb,
             UserManager userManager,
             ApplicationKeyStorage applicationKeyStorage) {
-        mContext = context;
         mSnapshotStorage = snapshotStorage;
         mDatabase = recoverableKeyStoreDb;
         mUserManager = userManager;
diff --git a/services/core/java/com/android/server/pm/CrossProfileIntentFilter.java b/services/core/java/com/android/server/pm/CrossProfileIntentFilter.java
index f5910fa..718756f 100644
--- a/services/core/java/com/android/server/pm/CrossProfileIntentFilter.java
+++ b/services/core/java/com/android/server/pm/CrossProfileIntentFilter.java
@@ -16,6 +16,7 @@
 
 package com.android.server.pm;
 
+import android.annotation.IntDef;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.os.UserHandle;
@@ -30,6 +31,8 @@
 import org.xmlpull.v1.XmlPullParserException;
 
 import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 
 /**
  * The {@link PackageManagerService} maintains some {@link CrossProfileIntentFilter}s for each user.
@@ -41,13 +44,52 @@
     private static final String ATTR_FLAGS = "flags";
     private static final String ATTR_OWNER_PACKAGE = "ownerPackage";
     private static final String ATTR_FILTER = "filter";
+    private static final String ATTR_ACCESS_CONTROL = "accessControl";
 
     private static final String TAG = "CrossProfileIntentFilter";
 
+    /**
+     * AccessControlLevel provides level of access for user to create/modify
+     * {@link CrossProfileIntentFilter} in {@link com.android.server.pm.Settings}.
+     * Each AccessControlLevel have value assigned, the higher the value
+     * implies higher restriction for creation/modification.
+     * AccessControlLevel allows us to protect against malicious changes in user's
+     * {@link CrossProfileIntentFilter}s, which might add/remove {@link CrossProfileIntentFilter}
+     * leading to unprecedented results.
+     */
+    @IntDef(prefix = {"ACCESS_LEVEL_"}, value = {
+            ACCESS_LEVEL_ALL,
+            ACCESS_LEVEL_SYSTEM,
+            ACCESS_LEVEL_SYSTEM_ADD_ONLY,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface AccessControlLevel {
+    }
+
+    /**
+     * ACCESS_LEVEL_ALL signifies that irrespective of user we would allow
+     * access(addition/modification/removal) for CrossProfileIntentFilter.
+     * This is the default access control level.
+     */
+    public static final int ACCESS_LEVEL_ALL = 0;
+
+    /**
+     * ACCESS_LEVEL_SYSTEM signifies that only system/root user would be able to
+     * access(addition/modification/removal) CrossProfileIntentFilter.
+     */
+    public static final int ACCESS_LEVEL_SYSTEM = 10;
+
+    /**
+     * ACCESS_LEVEL_SYSTEM_ADD_ONLY signifies that only system/root user would be able to add
+     * CrossProfileIntentFilter but not modify/remove. Once added, it cannot be modified or removed.
+     */
+    public static final int ACCESS_LEVEL_SYSTEM_ADD_ONLY = 20;
+
     // If the intent matches the IntentFilter, then it can be forwarded to this userId.
     final int mTargetUserId;
     final String mOwnerPackage; // packageName of the app.
     final int mFlags;
+    final int mAccessControlLevel;
 
     // The cache for snapshots, so they are not rebuilt if the base object has not
     // changed.
@@ -65,10 +107,16 @@
 
     CrossProfileIntentFilter(IntentFilter filter, String ownerPackage, int targetUserId,
             int flags) {
+        this(filter, ownerPackage, targetUserId, flags, ACCESS_LEVEL_ALL);
+    }
+
+    CrossProfileIntentFilter(IntentFilter filter, String ownerPackage, int targetUserId,
+            int flags, @AccessControlLevel int accessControlLevel) {
         super(filter);
         mTargetUserId = targetUserId;
         mOwnerPackage = ownerPackage;
         mFlags = flags;
+        mAccessControlLevel = accessControlLevel;
         mSnapshot = makeCache();
     }
 
@@ -77,12 +125,18 @@
         this(filter.mFilter, ownerPackage, targetUserId, flags);
     }
 
+    CrossProfileIntentFilter(WatchedIntentFilter filter, String ownerPackage, int targetUserId,
+            int flags, @AccessControlLevel int accessControlLevel) {
+        this(filter.mFilter, ownerPackage, targetUserId, flags, accessControlLevel);
+    }
+
     // Copy constructor used only to create a snapshot.
     private CrossProfileIntentFilter(CrossProfileIntentFilter f) {
         super(f);
         mTargetUserId = f.mTargetUserId;
         mOwnerPackage = f.mOwnerPackage;
         mFlags = f.mFlags;
+        mAccessControlLevel = f.mAccessControlLevel;
         mSnapshot = new SnapshotCache.Sealed();
     }
 
@@ -98,9 +152,14 @@
         return mOwnerPackage;
     }
 
+    public @AccessControlLevel int getAccessControlLevel() {
+        return mAccessControlLevel;
+    }
+
     CrossProfileIntentFilter(TypedXmlPullParser parser) throws XmlPullParserException, IOException {
         mTargetUserId = parser.getAttributeInt(null, ATTR_TARGET_USER_ID, UserHandle.USER_NULL);
         mOwnerPackage = getStringFromXml(parser, ATTR_OWNER_PACKAGE, "");
+        mAccessControlLevel = parser.getAttributeInt(null, ATTR_ACCESS_CONTROL, ACCESS_LEVEL_ALL);
         mFlags = parser.getAttributeInt(null, ATTR_FLAGS, 0);
         mSnapshot = makeCache();
 
@@ -151,6 +210,7 @@
         serializer.attributeInt(null, ATTR_TARGET_USER_ID, mTargetUserId);
         serializer.attributeInt(null, ATTR_FLAGS, mFlags);
         serializer.attribute(null, ATTR_OWNER_PACKAGE, mOwnerPackage);
+        serializer.attributeInt(null, ATTR_ACCESS_CONTROL, mAccessControlLevel);
         serializer.startTag(null, ATTR_FILTER);
         mFilter.writeToXml(serializer);
         serializer.endTag(null, ATTR_FILTER);
@@ -165,7 +225,8 @@
     boolean equalsIgnoreFilter(CrossProfileIntentFilter other) {
         return mTargetUserId == other.mTargetUserId
                 && mOwnerPackage.equals(other.mOwnerPackage)
-                && mFlags == other.mFlags;
+                && mFlags == other.mFlags
+                && mAccessControlLevel == other.mAccessControlLevel;
     }
 
     public CrossProfileIntentFilter snapshot() {
diff --git a/services/core/java/com/android/server/pm/DeletePackageHelper.java b/services/core/java/com/android/server/pm/DeletePackageHelper.java
index 48ec436..bda7b82 100644
--- a/services/core/java/com/android/server/pm/DeletePackageHelper.java
+++ b/services/core/java/com/android/server/pm/DeletePackageHelper.java
@@ -816,9 +816,10 @@
         }
 
         // Allow package verifier to silently uninstall.
-        if (mPm.mRequiredVerifierPackage != null && callingUid == snapshot
-                .getPackageUid(mPm.mRequiredVerifierPackage, 0, callingUserId)) {
-            return true;
+        for (String verifierPackageName : mPm.mRequiredVerifierPackages) {
+            if (callingUid == snapshot.getPackageUid(verifierPackageName, 0, callingUserId)) {
+                return true;
+            }
         }
 
         // Allow package uninstaller to silently uninstall.
diff --git a/services/core/java/com/android/server/pm/DumpHelper.java b/services/core/java/com/android/server/pm/DumpHelper.java
index 7352018..d2a5e32 100644
--- a/services/core/java/com/android/server/pm/DumpHelper.java
+++ b/services/core/java/com/android/server/pm/DumpHelper.java
@@ -54,7 +54,7 @@
     private final StorageEventHelper mStorageEventHelper;
     private final DomainVerificationManagerInternal mDomainVerificationManager;
     private final PackageInstallerService mInstallerService;
-    private final String mRequiredVerifierPackage;
+    private final String[] mRequiredVerifierPackages;
     private final KnownPackages mKnownPackages;
     private final ChangedPackagesTracker mChangedPackagesTracker;
     private final ArrayMap<String, FeatureInfo> mAvailableFeatures;
@@ -65,7 +65,7 @@
             PermissionManagerServiceInternal permissionManager,
             StorageEventHelper storageEventHelper,
             DomainVerificationManagerInternal domainVerificationManager,
-            PackageInstallerService installerService, String requiredVerifierPackage,
+            PackageInstallerService installerService, String[] requiredVerifierPackages,
             KnownPackages knownPackages,
             ChangedPackagesTracker changedPackagesTracker,
             ArrayMap<String, FeatureInfo> availableFeatures,
@@ -75,7 +75,7 @@
         mStorageEventHelper = storageEventHelper;
         mDomainVerificationManager = domainVerificationManager;
         mInstallerService = installerService;
-        mRequiredVerifierPackage = requiredVerifierPackage;
+        mRequiredVerifierPackages = requiredVerifierPackages;
         mKnownPackages = knownPackages;
         mChangedPackagesTracker = changedPackagesTracker;
         mAvailableFeatures = availableFeatures;
@@ -313,26 +313,28 @@
             ipw.decreaseIndent();
         }
 
-        if (dumpState.isDumping(DumpState.DUMP_VERIFIERS)
-                && packageName == null) {
-            final String requiredVerifierPackage = mRequiredVerifierPackage;
-            if (!checkin) {
+        if (dumpState.isDumping(DumpState.DUMP_VERIFIERS) && packageName == null) {
+            if (!checkin && mRequiredVerifierPackages.length > 0) {
                 if (dumpState.onTitlePrinted()) {
                     pw.println();
                 }
                 pw.println("Verifiers:");
-                pw.print("  Required: ");
-                pw.print(requiredVerifierPackage);
-                pw.print(" (uid=");
-                pw.print(snapshot.getPackageUid(requiredVerifierPackage,
-                        MATCH_DEBUG_TRIAGED_MISSING, UserHandle.USER_SYSTEM));
-                pw.println(")");
-            } else if (requiredVerifierPackage != null) {
-                pw.print("vrfy,");
-                pw.print(requiredVerifierPackage);
-                pw.print(",");
-                pw.println(snapshot.getPackageUid(requiredVerifierPackage,
-                        MATCH_DEBUG_TRIAGED_MISSING, UserHandle.USER_SYSTEM));
+            }
+            for (String requiredVerifierPackage : mRequiredVerifierPackages) {
+                if (!checkin) {
+                    pw.print("  Required: ");
+                    pw.print(requiredVerifierPackage);
+                    pw.print(" (uid=");
+                    pw.print(snapshot.getPackageUid(requiredVerifierPackage,
+                            MATCH_DEBUG_TRIAGED_MISSING, UserHandle.USER_SYSTEM));
+                    pw.println(")");
+                } else {
+                    pw.print("vrfy,");
+                    pw.print(requiredVerifierPackage);
+                    pw.print(",");
+                    pw.println(snapshot.getPackageUid(requiredVerifierPackage,
+                            MATCH_DEBUG_TRIAGED_MISSING, UserHandle.USER_SYSTEM));
+                }
             }
         }
 
@@ -642,17 +644,19 @@
     private void dumpProto(Computer snapshot, FileDescriptor fd) {
         final ProtoOutputStream proto = new ProtoOutputStream(fd);
 
-        final long requiredVerifierPackageToken =
-                proto.start(PackageServiceDumpProto.REQUIRED_VERIFIER_PACKAGE);
-        proto.write(PackageServiceDumpProto.PackageShortProto.NAME,
-                mRequiredVerifierPackage);
-        proto.write(
-                PackageServiceDumpProto.PackageShortProto.UID,
-                snapshot.getPackageUid(
-                        mRequiredVerifierPackage,
-                        MATCH_DEBUG_TRIAGED_MISSING,
-                        UserHandle.USER_SYSTEM));
-        proto.end(requiredVerifierPackageToken);
+        for (String requiredVerifierPackage : mRequiredVerifierPackages) {
+            final long requiredVerifierPackageToken =
+                    proto.start(PackageServiceDumpProto.REQUIRED_VERIFIER_PACKAGE);
+            proto.write(PackageServiceDumpProto.PackageShortProto.NAME,
+                    requiredVerifierPackage);
+            proto.write(
+                    PackageServiceDumpProto.PackageShortProto.UID,
+                    snapshot.getPackageUid(
+                            requiredVerifierPackage,
+                            MATCH_DEBUG_TRIAGED_MISSING,
+                            UserHandle.USER_SYSTEM));
+            proto.end(requiredVerifierPackageToken);
+        }
 
         DomainVerificationProxy proxy = mDomainVerificationManager.getProxy();
         ComponentName verifierComponent = proxy.getComponentName();
diff --git a/services/core/java/com/android/server/pm/InitAppsHelper.java b/services/core/java/com/android/server/pm/InitAppsHelper.java
index 122ab10..52e3fd9 100644
--- a/services/core/java/com/android/server/pm/InitAppsHelper.java
+++ b/services/core/java/com/android/server/pm/InitAppsHelper.java
@@ -46,6 +46,7 @@
 import android.util.Slog;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.content.om.OverlayConfig;
 import com.android.internal.util.FrameworkStatsLog;
 import com.android.server.EventLogTags;
@@ -401,4 +402,9 @@
     public List<ScanPartition> getDirsToScanAsSystem() {
         return mDirsToScanAsSystem;
     }
+
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+    public int getSystemScanFlags() {
+        return mSystemScanFlags;
+    }
 }
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 8bb9b84..728d2d1 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -2719,15 +2719,16 @@
                             installerPackageName, null /*finishedReceiver*/,
                             updateUserIds, instantUserIds, null /* broadcastAllowList */, null);
                 }
-                // if the required verifier is defined, but, is not the installer of record
-                // for the package, it gets notified
-                final boolean notifyVerifier = mPm.mRequiredVerifierPackage != null
-                        && !mPm.mRequiredVerifierPackage.equals(installerPackageName);
-                if (notifyVerifier) {
-                    mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
-                            extras, 0 /*flags*/,
-                            mPm.mRequiredVerifierPackage, null /*finishedReceiver*/,
-                            updateUserIds, instantUserIds, null /* broadcastAllowList */, null);
+                // Notify required verifier(s) that are not the installer of record for the package.
+                for (String verifierPackageName : mPm.mRequiredVerifierPackages) {
+                    if (verifierPackageName != null && !verifierPackageName.equals(
+                            installerPackageName)) {
+                        mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
+                                extras, 0 /*flags*/,
+                                verifierPackageName, null /*finishedReceiver*/,
+                                updateUserIds, instantUserIds, null /* broadcastAllowList */,
+                                null);
+                    }
                 }
                 // If package installer is defined, notify package installer about new
                 // app installed
@@ -2752,12 +2753,14 @@
                                 updateUserIds, instantUserIds, null /*broadcastAllowList*/,
                                 null);
                     }
-                    if (notifyVerifier) {
-                        mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName,
-                                extras, 0 /*flags*/,
-                                mPm.mRequiredVerifierPackage, null /*finishedReceiver*/,
-                                updateUserIds, instantUserIds, null /*broadcastAllowList*/,
-                                null);
+                    for (String verifierPackageName : mPm.mRequiredVerifierPackages) {
+                        if (verifierPackageName != null && !verifierPackageName.equals(
+                                installerPackageName)) {
+                            mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED,
+                                    packageName, extras, 0 /*flags*/, verifierPackageName,
+                                    null /*finishedReceiver*/, updateUserIds, instantUserIds,
+                                    null /*broadcastAllowList*/, null);
+                        }
                     }
                     mPm.sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED,
                             null /*package*/, null /*extras*/, 0 /*flags*/,
diff --git a/services/core/java/com/android/server/pm/KnownPackages.java b/services/core/java/com/android/server/pm/KnownPackages.java
index 04376b4..dcf7152 100644
--- a/services/core/java/com/android/server/pm/KnownPackages.java
+++ b/services/core/java/com/android/server/pm/KnownPackages.java
@@ -79,7 +79,7 @@
     private final String mRequiredInstallerPackage;
     private final String mRequiredUninstallerPackage;
     private final String mSetupWizardPackage;
-    private final String mRequiredVerifierPackage;
+    private final String[] mRequiredVerifierPackages;
     private final String mDefaultTextClassifierPackage;
     private final String mSystemTextClassifierPackageName;
     private final String mRequiredPermissionControllerPackage;
@@ -94,7 +94,7 @@
 
     KnownPackages(DefaultAppProvider defaultAppProvider, String requiredInstallerPackage,
             String requiredUninstallerPackage, String setupWizardPackage,
-            String requiredVerifierPackage, String defaultTextClassifierPackage,
+            String[] requiredVerifierPackages, String defaultTextClassifierPackage,
             String systemTextClassifierPackageName, String requiredPermissionControllerPackage,
             String configuratorPackage, String incidentReportApproverPackage,
             String ambientContextDetectionPackage, String appPredictionServicePackage,
@@ -104,7 +104,7 @@
         mRequiredInstallerPackage = requiredInstallerPackage;
         mRequiredUninstallerPackage = requiredUninstallerPackage;
         mSetupWizardPackage = setupWizardPackage;
-        mRequiredVerifierPackage = requiredVerifierPackage;
+        mRequiredVerifierPackages = requiredVerifierPackages;
         mDefaultTextClassifierPackage = defaultTextClassifierPackage;
         mSystemTextClassifierPackageName = systemTextClassifierPackageName;
         mRequiredPermissionControllerPackage = requiredPermissionControllerPackage;
@@ -182,7 +182,7 @@
             case PACKAGE_SYSTEM:
                 return new String[]{"android"};
             case PACKAGE_VERIFIER:
-                return snapshot.filterOnlySystemPackages(mRequiredVerifierPackage);
+                return snapshot.filterOnlySystemPackages(mRequiredVerifierPackages);
             case PACKAGE_SYSTEM_TEXT_CLASSIFIER:
                 return snapshot.filterOnlySystemPackages(
                         mDefaultTextClassifierPackage, mSystemTextClassifierPackageName);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 32b3e6a..e798adf 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -238,6 +238,7 @@
 
 import dalvik.system.VMRuntime;
 
+import libcore.util.EmptyArray;
 import libcore.util.HexEncoding;
 
 import java.io.ByteArrayInputStream;
@@ -521,6 +522,9 @@
 
     private static final String COMPANION_PACKAGE_NAME = "com.android.companiondevicemanager";
 
+    // How many required verifiers can be on the system.
+    private static final int REQUIRED_VERIFIERS_MAX_COUNT = 2;
+
     // Compilation reasons.
     public static final int REASON_FIRST_BOOT = 0;
     public static final int REASON_BOOT_AFTER_OTA = 1;
@@ -906,7 +910,7 @@
     final SparseArray<PostInstallData> mRunningInstalls = new SparseArray<>();
     int mNextInstallToken = 1;  // nonzero; will be wrapped back to 1 when ++ overflows
 
-    final @Nullable String mRequiredVerifierPackage;
+    final @NonNull String[] mRequiredVerifierPackages;
     final @NonNull String mRequiredInstallerPackage;
     final @NonNull String mRequiredUninstallerPackage;
     final @NonNull String mRequiredPermissionControllerPackage;
@@ -1642,7 +1646,7 @@
         mProtectedPackages = testParams.protectedPackages;
         mSeparateProcesses = testParams.separateProcesses;
         mViewCompiler = testParams.viewCompiler;
-        mRequiredVerifierPackage = testParams.requiredVerifierPackage;
+        mRequiredVerifierPackages = testParams.requiredVerifierPackages;
         mRequiredInstallerPackage = testParams.requiredInstallerPackage;
         mRequiredUninstallerPackage = testParams.requiredUninstallerPackage;
         mRequiredPermissionControllerPackage = testParams.requiredPermissionControllerPackage;
@@ -2126,7 +2130,7 @@
             EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_READY,
                     SystemClock.uptimeMillis());
 
-            mRequiredVerifierPackage = getRequiredButNotReallyRequiredVerifierLPr(computer);
+            mRequiredVerifierPackages = getRequiredButNotReallyRequiredVerifiersLPr(computer);
             mRequiredInstallerPackage = getRequiredInstallerLPr(computer);
             mRequiredUninstallerPackage = getRequiredUninstallerLPr(computer);
             ComponentName intentFilterVerifierComponent =
@@ -2265,8 +2269,8 @@
                 "persist.pm.mock-upgrade", false /* default */);
     }
 
-    @Nullable
-    private String getRequiredButNotReallyRequiredVerifierLPr(@NonNull Computer computer) {
+    @NonNull
+    private String[] getRequiredButNotReallyRequiredVerifiersLPr(@NonNull Computer computer) {
         final Intent intent = new Intent(Intent.ACTION_PACKAGE_NEEDS_VERIFICATION);
 
         final List<ResolveInfo> matches =
@@ -2274,13 +2278,23 @@
                         PACKAGE_MIME_TYPE,
                         MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE,
                         UserHandle.USER_SYSTEM, Binder.getCallingUid());
-        if (matches.size() == 1) {
-            return matches.get(0).getComponentInfo().packageName;
-        } else if (matches.size() == 0) {
+        final int size = matches.size();
+        if (size == 0) {
             Log.w(TAG, "There should probably be a verifier, but, none were found");
-            return null;
+            return EmptyArray.STRING;
+        } else if (size <= REQUIRED_VERIFIERS_MAX_COUNT) {
+            String[] verifiers = new String[size];
+            for (int i = 0; i < size; ++i) {
+                verifiers[i] = matches.get(i).getComponentInfo().packageName;
+                if (TextUtils.isEmpty(verifiers[i])) {
+                    throw new RuntimeException("Invalid verifier: " + matches);
+                }
+            }
+            return verifiers;
         }
-        throw new RuntimeException("There must be exactly one verifier; found " + matches);
+        throw new RuntimeException(
+                "There must be no more than " + REQUIRED_VERIFIERS_MAX_COUNT + " verifiers; found "
+                        + matches);
     }
 
     @NonNull
@@ -3153,8 +3167,12 @@
 
     boolean isCallerVerifier(@NonNull Computer snapshot, int callingUid) {
         final int callingUserId = UserHandle.getUserId(callingUid);
-        return mRequiredVerifierPackage != null && callingUid == snapshot.getPackageUid(
-                mRequiredVerifierPackage, 0, callingUserId);
+        for (String requiredVerifierPackage : mRequiredVerifierPackages) {
+            if (callingUid == snapshot.getPackageUid(requiredVerifierPackage, 0, callingUserId)) {
+                return true;
+            }
+        }
+        return false;
     }
 
     public boolean isPackageDeviceAdminOnAnyUser(@NonNull Computer snapshot, String packageName) {
@@ -3332,6 +3350,12 @@
                         android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
         int callingUid = Binder.getCallingUid();
         enforceOwnerRights(snapshot, ownerPackage, callingUid);
+
+        // Verifying that current calling uid should be able to add {@link CrossProfileIntentFilter}
+        // for source and target user
+        mUserManager.enforceCrossProfileIntentFilterAccess(sourceUserId, targetUserId, callingUid,
+                /* addCrossProfileIntentFilter */ true);
+
         PackageManagerServiceUtils.enforceShellRestriction(mInjector.getUserManagerInternal(),
                 UserManager.DISALLOW_DEBUGGING_FEATURES, callingUid, sourceUserId);
         if (intentFilter.countActions() == 0) {
@@ -3340,7 +3364,8 @@
         }
         synchronized (mLock) {
             CrossProfileIntentFilter newFilter = new CrossProfileIntentFilter(intentFilter,
-                    ownerPackage, targetUserId, flags);
+                    ownerPackage, targetUserId, flags, mUserManager
+                    .getCrossProfileIntentFilterAccessControl(sourceUserId, targetUserId));
             CrossProfileIntentResolver resolver =
                     mSettings.editCrossProfileIntentResolverLPw(sourceUserId);
             ArrayList<CrossProfileIntentFilter> existing = resolver.findFilters(intentFilter);
@@ -4554,7 +4579,11 @@
                 ArraySet<CrossProfileIntentFilter> set =
                         new ArraySet<>(resolver.filterSet());
                 for (CrossProfileIntentFilter filter : set) {
-                    if (filter.getOwnerPackage().equals(ownerPackage)) {
+                    //Only remove if calling user is allowed based on access control of
+                    // {@link CrossProfileIntentFilter}
+                    if (filter.getOwnerPackage().equals(ownerPackage)
+                            && mUserManager.isCrossProfileIntentFilterAccessible(sourceUserId,
+                            filter.mTargetUserId, /* addCrossProfileIntentFilter */ false)) {
                         resolver.removeFilter(filter);
                     }
                 }
@@ -4665,15 +4694,28 @@
         }
 
         @Override
-        public void extendVerificationTimeout(int id, int verificationCodeAtTimeout,
+        public void extendVerificationTimeout(int verificationId, int verificationCodeAtTimeout,
                 long millisecondsToDelay) {
-            mContext.enforceCallingOrSelfPermission(
-                    Manifest.permission.PACKAGE_VERIFICATION_AGENT,
-                    "Only package verification agents can extend verification timeouts");
+            // Negative ids correspond to testing verifiers and will be silently enforced in
+            // the handler thread.
+            if (verificationId >= 0) {
+                mContext.enforceCallingOrSelfPermission(
+                        Manifest.permission.PACKAGE_VERIFICATION_AGENT,
+                        "Only package verification agents can extend verification timeouts");
+            }
             final int callingUid = Binder.getCallingUid();
 
             mHandler.post(() -> {
+                final int id = verificationId >= 0 ? verificationId : -verificationId;
                 final PackageVerificationState state = mPendingVerification.get(id);
+                if (state == null || state.timeoutExtended() || !state.checkRequiredVerifierUid(
+                        callingUid)) {
+                    // Only allow calls from required verifiers.
+                    return;
+                }
+
+                state.extendTimeout();
+
                 final PackageVerificationResponse response = new PackageVerificationResponse(
                         verificationCodeAtTimeout, callingUid);
 
@@ -4685,14 +4727,10 @@
                     delay = 0;
                 }
 
-                if ((state != null) && !state.timeoutExtended()) {
-                    state.extendTimeout();
-
-                    final Message msg = mHandler.obtainMessage(PackageManagerService.PACKAGE_VERIFIED);
-                    msg.arg1 = id;
-                    msg.obj = response;
-                    mHandler.sendMessageDelayed(msg, delay);
-                }
+                final Message msg = mHandler.obtainMessage(PackageManagerService.PACKAGE_VERIFIED);
+                msg.arg1 = id;
+                msg.obj = response;
+                mHandler.sendMessageDelayed(msg, delay);
             });
         }
 
@@ -5434,13 +5472,19 @@
 
             final long callingId = Binder.clearCallingIdentity();
             try {
-                final PackageStateInternal packageState =
-                        snapshot.getPackageStateForInstalledAndFiltered(
-                                packageName, callingUid, userId);
+                final PackageStateInternal packageState = snapshot.getPackageStateInternal(
+                        packageName);
                 if (packageState == null) {
                     return false;
                 }
 
+                final PackageUserStateInternal userState = packageState.getUserStateOrDefault(
+                        userId);
+                if (userState.isHidden() == hidden || !userState.isInstalled()
+                        || snapshot.shouldFilterApplication(packageState, callingUid, userId)) {
+                    return false;
+                }
+
                 // Cannot hide static shared libs as they are considered
                 // a part of the using app (emulating static linking). Also
                 // static libs are installed always on internal storage.
@@ -5470,10 +5514,6 @@
                     return false;
                 }
 
-                if (packageState.getUserStateOrDefault(userId).isHidden() == hidden) {
-                    return false;
-                }
-
                 commitPackageStateMutation(null, packageName, packageState1 ->
                         packageState1.userState(userId).setHidden(hidden));
 
@@ -5862,18 +5902,32 @@
         }
 
         @Override
-        public void verifyPendingInstall(int id, int verificationCode) throws RemoteException {
-            mContext.enforceCallingOrSelfPermission(
-                    Manifest.permission.PACKAGE_VERIFICATION_AGENT,
-                    "Only package verification agents can verify applications");
+        public void verifyPendingInstall(int verificationId, int verificationCode)
+                throws RemoteException {
+            // Negative ids correspond to testing verifiers and will be silently enforced in
+            // the handler thread.
+            if (verificationId >= 0) {
+                mContext.enforceCallingOrSelfPermission(
+                        Manifest.permission.PACKAGE_VERIFICATION_AGENT,
+                        "Only package verification agents can verify applications");
+            }
             final int callingUid = Binder.getCallingUid();
 
-            final Message msg = mHandler.obtainMessage(PackageManagerService.PACKAGE_VERIFIED);
-            final PackageVerificationResponse response = new PackageVerificationResponse(
-                    verificationCode, callingUid);
-            msg.arg1 = id;
-            msg.obj = response;
-            mHandler.sendMessage(msg);
+            mHandler.post(() -> {
+                final int id = verificationId >= 0 ? verificationId : -verificationId;
+                final PackageVerificationState state = mPendingVerification.get(id);
+                if (state == null || !state.checkRequiredVerifierUid(callingUid)) {
+                    // Only allow calls from required verifiers.
+                    return;
+                }
+
+                final Message msg = mHandler.obtainMessage(PackageManagerService.PACKAGE_VERIFIED);
+                final PackageVerificationResponse response = new PackageVerificationResponse(
+                        verificationCode, callingUid);
+                msg.arg1 = id;
+                msg.obj = response;
+                mHandler.sendMessage(msg);
+            });
         }
 
         @Override
@@ -5928,7 +5982,7 @@
                     mRequiredInstallerPackage,
                     mRequiredUninstallerPackage,
                     mSetupWizardPackage,
-                    mRequiredVerifierPackage,
+                    mRequiredVerifierPackages,
                     mDefaultTextClassifierPackage,
                     mSystemTextClassifierPackageName,
                     mRequiredPermissionControllerPackage,
@@ -5949,7 +6003,7 @@
                 protectedBroadcasts = new ArraySet<>(mProtectedBroadcasts);
             }
             new DumpHelper(mPermissionManager, mStorageEventHelper,
-                    mDomainVerificationManager, mInstallerService, mRequiredVerifierPackage,
+                    mDomainVerificationManager, mInstallerService, mRequiredVerifierPackages,
                     knownPackages, mChangedPackagesTracker, availableFeatures, protectedBroadcasts,
                     getPerUidReadTimeouts(snapshot)
             ).doDump(snapshot, fd, pw, args);
@@ -6956,7 +7010,7 @@
                 mRequiredInstallerPackage,
                 mRequiredUninstallerPackage,
                 mSetupWizardPackage,
-                mRequiredVerifierPackage,
+                mRequiredVerifierPackages,
                 mDefaultTextClassifierPackage,
                 mSystemTextClassifierPackageName,
                 mRequiredPermissionControllerPackage,
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java b/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
index f44d922..ec1b29e 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
@@ -80,7 +80,7 @@
     public @NonNull String requiredInstallerPackage;
     public @NonNull String requiredPermissionControllerPackage;
     public @NonNull String requiredUninstallerPackage;
-    public @Nullable String requiredVerifierPackage;
+    public @NonNull String[] requiredVerifierPackages;
     public String[] separateProcesses;
     public @NonNull String servicesExtensionPackageName;
     public @Nullable String setupWizardPackage;
diff --git a/services/core/java/com/android/server/pm/PackageVerificationState.java b/services/core/java/com/android/server/pm/PackageVerificationState.java
index d105fc4..a574fc9 100644
--- a/services/core/java/com/android/server/pm/PackageVerificationState.java
+++ b/services/core/java/com/android/server/pm/PackageVerificationState.java
@@ -30,7 +30,7 @@
 
     private final SparseBooleanArray mSufficientVerifierUids;
 
-    private int mRequiredVerifierUid;
+    private final SparseBooleanArray mRequiredVerifierUids;
 
     private boolean mSufficientVerificationComplete;
 
@@ -51,6 +51,9 @@
     PackageVerificationState(VerifyingSession verifyingSession) {
         mVerifyingSession = verifyingSession;
         mSufficientVerifierUids = new SparseBooleanArray();
+        mRequiredVerifierUids = new SparseBooleanArray();
+        mRequiredVerificationComplete = false;
+        mRequiredVerificationPassed = true;
         mExtendedTimeout = false;
     }
 
@@ -58,9 +61,14 @@
         return mVerifyingSession;
     }
 
-    /** Sets the user ID of the required package verifier. */
-    void setRequiredVerifierUid(int uid) {
-        mRequiredVerifierUid = uid;
+    /** Add the user ID of the required package verifier. */
+    void addRequiredVerifierUid(int uid) {
+        mRequiredVerifierUids.put(uid, true);
+    }
+
+    /** Returns true if the uid a required verifier. */
+    boolean checkRequiredVerifierUid(int uid) {
+        return mRequiredVerifierUids.get(uid, false);
     }
 
     /**
@@ -80,39 +88,55 @@
      * @return {@code true} if the verifying agent actually exists in our list
      */
     boolean setVerifierResponse(int uid, int code) {
-        if (uid == mRequiredVerifierUid) {
-            mRequiredVerificationComplete = true;
+        if (mRequiredVerifierUids.get(uid)) {
             switch (code) {
                 case PackageManager.VERIFICATION_ALLOW_WITHOUT_SUFFICIENT:
                     mSufficientVerifierUids.clear();
                     // fall through
                 case PackageManager.VERIFICATION_ALLOW:
-                    mRequiredVerificationPassed = true;
+                    // Two possible options:
+                    // - verification result is true,
+                    // - another verifier set it to false.
+                    // In both cases we don't need to assign anything, just exit.
                     break;
                 default:
                     mRequiredVerificationPassed = false;
             }
-            return true;
-        } else {
-            if (mSufficientVerifierUids.get(uid)) {
-                if (code == PackageManager.VERIFICATION_ALLOW) {
-                    mSufficientVerificationComplete = true;
-                    mSufficientVerificationPassed = true;
-                }
 
-                mSufficientVerifierUids.delete(uid);
-                if (mSufficientVerifierUids.size() == 0) {
-                    mSufficientVerificationComplete = true;
-                }
-
-                return true;
+            mRequiredVerifierUids.delete(uid);
+            if (mRequiredVerifierUids.size() == 0) {
+                mRequiredVerificationComplete = true;
             }
+            return true;
+        } else if (mSufficientVerifierUids.get(uid)) {
+            if (code == PackageManager.VERIFICATION_ALLOW) {
+                mSufficientVerificationPassed = true;
+                mSufficientVerificationComplete = true;
+            }
+
+            mSufficientVerifierUids.delete(uid);
+            if (mSufficientVerifierUids.size() == 0) {
+                mSufficientVerificationComplete = true;
+            }
+
+            return true;
         }
 
         return false;
     }
 
     /**
+     * Mark the session as passed required verification.
+     */
+    void passRequiredVerification() {
+        if (mRequiredVerifierUids.size() > 0) {
+            throw new RuntimeException("Required verifiers still present.");
+        }
+        mRequiredVerificationPassed = true;
+        mRequiredVerificationComplete = true;
+    }
+
+    /**
      * Returns whether verification is considered complete. This means that the required verifier
      * and at least one of the sufficient verifiers has returned a positive verification.
      *
@@ -137,15 +161,15 @@
      * @return {@code true} if installation should be allowed
      */
     boolean isInstallAllowed() {
-        if (!mRequiredVerificationComplete) {
+        if (!mRequiredVerificationComplete || !mRequiredVerificationPassed) {
             return false;
         }
 
         if (mSufficientVerificationComplete) {
-            return mRequiredVerificationPassed && mSufficientVerificationPassed;
+            return mSufficientVerificationPassed;
         }
 
-        return mRequiredVerificationPassed;
+        return true;
     }
 
     /** Extend the timeout for this Package to be verified. */
diff --git a/services/core/java/com/android/server/pm/SuspendPackageHelper.java b/services/core/java/com/android/server/pm/SuspendPackageHelper.java
index f931ba8..01aecd9 100644
--- a/services/core/java/com/android/server/pm/SuspendPackageHelper.java
+++ b/services/core/java/com/android/server/pm/SuspendPackageHelper.java
@@ -126,10 +126,9 @@
                 unmodifiablePackages.add(packageName);
                 continue;
             }
-            final PackageStateInternal packageState =
-                    snapshot.getPackageStateForInstalledAndFiltered(
-                            packageName, callingUid, userId);
-            if (packageState == null) {
+            final PackageStateInternal packageState = snapshot.getPackageStateInternal(packageName);
+            if (packageState == null || !packageState.getUserStateOrDefault(userId).isInstalled()
+                    || snapshot.shouldFilterApplication(packageState, callingUid, userId)) {
                 Slog.w(TAG, "Could not find package setting for package: " + packageName
                         + ". Skipping suspending/un-suspending.");
                 unmodifiablePackages.add(packageName);
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index e022f15..a0335e8 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -1650,11 +1650,40 @@
         }
 
         int currentUser = Binder.withCleanCallingIdentity(() -> ActivityManager.getCurrentUser());
-        // TODO(b/179163496): should return true for profile users of the current user as well
         return currentUser == userId;
     }
 
     @Override
+    public boolean isUserVisible(@UserIdInt int userId) {
+        int callingUserId = UserHandle.getCallingUserId();
+        if (callingUserId != userId
+                && !hasManageUsersOrPermission(android.Manifest.permission.INTERACT_ACROSS_USERS)) {
+            throw new SecurityException("Caller from user " + callingUserId + " needs MANAGE_USERS "
+                    + "or INTERACT_ACROSS_USERS permission to check if another user (" + userId
+                    + ") is visible");
+        }
+
+        // First check current foreground user (on main display)
+        int currentUserId = Binder.withCleanCallingIdentity(() -> ActivityManager.getCurrentUser());
+
+        if (currentUserId == userId) {
+            return true;
+        }
+
+        // Then profiles of current user
+        // TODO(b/239824814): consider using AMInternal.isCurrentProfile() instead
+        if (isProfile(userId)) {
+            int parentId = Binder.withCleanCallingIdentity(() -> getProfileParentId(userId));
+            if (parentId == currentUserId) {
+                return isUserRunning(userId);
+            }
+        }
+
+        // TODO(b/239824814): support background users on secondary display (and their profiles)
+        return false;
+    }
+
+    @Override
     public @NonNull String getUserName() {
         final int callingUid = Binder.getCallingUid();
         if (!hasQueryOrCreateUsersPermission()
@@ -1866,6 +1895,103 @@
         return mLocalService.exists(userId);
     }
 
+    /**
+     * Returns user's {@link  CrossProfileIntentFilter.AccessControlLevel}, which is derived from
+     * {@link UserTypeDetails}. If user does not have defined their access control level,
+     * returns default {@link CrossProfileIntentFilter#ACCESS_LEVEL_ALL}
+     */
+    private @CrossProfileIntentFilter.AccessControlLevel int
+                getCrossProfileIntentFilterAccessControl(@UserIdInt int userId) {
+        final UserTypeDetails userTypeDetails = getUserTypeDetailsNoChecks(userId);
+        return userTypeDetails != null ? userTypeDetails.getCrossProfileIntentFilterAccessControl()
+                : CrossProfileIntentFilter.ACCESS_LEVEL_ALL;
+    }
+
+    /**
+     * Verifies if calling user is allowed to access {@link CrossProfileIntentFilter} between given
+     * source and target user.
+     * @param sourceUserId userId for which CrossProfileIntentFilter would be configured
+     * @param targetUserId target user where we can resolve given intent filter
+     * @param callingUid user accessing api
+     * @param addCrossProfileIntentFilter if the operation is addition or not.
+     * @throws SecurityException is calling user is not allowed to access.
+     */
+    public void enforceCrossProfileIntentFilterAccess(
+            int sourceUserId, int targetUserId,
+            int callingUid, boolean addCrossProfileIntentFilter) {
+        if (!isCrossProfileIntentFilterAccessible(sourceUserId, targetUserId,
+                addCrossProfileIntentFilter)) {
+            throw new SecurityException("CrossProfileIntentFilter cannot be accessed by user "
+                    + callingUid);
+        }
+    }
+
+    /**
+     * Checks if {@link CrossProfileIntentFilter} can be accessed by calling user for given source
+     * and target user. There are following rules of access
+     * 1. For {@link CrossProfileIntentFilter#ACCESS_LEVEL_ALL},
+     *  irrespective of user we would allow access(addition/modification/removal)
+     * 2. For {@link CrossProfileIntentFilter#ACCESS_LEVEL_SYSTEM},
+     *  only system/root user would be able to access(addition/modification/removal)
+     * 3. For {@link CrossProfileIntentFilter#ACCESS_LEVEL_SYSTEM_ADD_ONLY},
+     *  only system/root user would be able to add but not modify/remove. Once added, it cannot be
+     *  modified or removed
+     * @param sourceUserId userId for which CrossProfileIntentFilter would be configured
+     * @param targetUserId target user where we can resolve given intent filter
+     * @param addCrossProfileIntentFilter if the operation is addition or not.
+     * @return true if {@link CrossProfileIntentFilter} can be accessed by calling user
+     */
+    public boolean isCrossProfileIntentFilterAccessible(int sourceUserId, int targetUserId,
+            boolean addCrossProfileIntentFilter) {
+        int effectiveAccessControl =
+                getCrossProfileIntentFilterAccessControl(sourceUserId, targetUserId);
+
+        /*
+        For {@link CrossProfileIntentFilter#ACCESS_LEVEL_SYSTEM}, if accessing user is not
+        system or root disallowing access to {@link CrossProfileIntentFilter}
+         */
+        if (CrossProfileIntentFilter.ACCESS_LEVEL_SYSTEM == effectiveAccessControl
+                && !PackageManagerServiceUtils.isSystemOrRoot()) {
+            return false;
+        }
+
+        /*
+        For {@link CrossProfileIntentFilter#ACCESS_LEVEL_SYSTEM_ADD_ONLY}, allowing only
+        system user to add {@link CrossProfileIntentFilter}. All users(including system) are
+        disallowed to modify/remove.
+         */
+        if (CrossProfileIntentFilter.ACCESS_LEVEL_SYSTEM_ADD_ONLY == effectiveAccessControl
+                && (!addCrossProfileIntentFilter || !PackageManagerServiceUtils.isSystemOrRoot())) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Returns {@link CrossProfileIntentFilter.AccessControlLevel}
+     * that should be assigned to {@link CrossProfileIntentFilter}
+     * computed from source user's and target user's
+     * {@link CrossProfileIntentFilter.AccessControlLevel}.
+     * The Access Level is configured per {@link CrossProfileIntentFilter} and its property of edge
+     * between source and target user e.g. for all {@link CrossProfileIntentFilter}s configured
+     * between Primary user and Clone profile should have access level of
+     * {@link CrossProfileIntentFilter#ACCESS_LEVEL_SYSTEM} which is driven by highest
+     * access value from source or target. The higher value means higher restrictions.
+     * @param sourceUserId userId of source user for whom CrossProfileIntentFilter will be stored
+     * @param targetUserId userId of target user for whom Cross Profile access would be allowed
+     * @return least privileged {@link CrossProfileIntentFilter.AccessControlLevel} from source or
+     * target user.
+     */
+    public @CrossProfileIntentFilter.AccessControlLevel int
+                getCrossProfileIntentFilterAccessControl(int sourceUserId, int targetUserId) {
+        int sourceAccessControlLevel,
+                targetAccessControlLevel, effectiveAccessControl;
+        sourceAccessControlLevel = getCrossProfileIntentFilterAccessControl(sourceUserId);
+        targetAccessControlLevel = getCrossProfileIntentFilterAccessControl(targetUserId);
+        effectiveAccessControl = Math.max(sourceAccessControlLevel, targetAccessControlLevel);
+        return effectiveAccessControl;
+    }
+
     @Override
     public void setUserName(@UserIdInt int userId, String name) {
         checkManageUsersPermission("rename users");
@@ -4556,34 +4682,31 @@
         }
         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;
+        if (size > 1) {
+            for (int idx = 0; idx < size; idx++) {
+                final UserInfo user = users.get(idx);
+                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
+                ));
             }
-
-            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;
     }
@@ -6010,6 +6133,10 @@
 
         } // synchronized (mPackagesLock)
 
+        // Multiple Users on Multiple Display info
+        pw.println("  Supports users on secondary displays: "
+                + UserManager.isUsersOnSecondaryDisplaysEnabled());
+
         // Dump some capabilities
         pw.println();
         pw.print("  Max users: " + UserManager.getMaxSupportedUsers());
diff --git a/services/core/java/com/android/server/pm/UserTypeDetails.java b/services/core/java/com/android/server/pm/UserTypeDetails.java
index 4aad1a7..2f3ca66 100644
--- a/services/core/java/com/android/server/pm/UserTypeDetails.java
+++ b/services/core/java/com/android/server/pm/UserTypeDetails.java
@@ -163,6 +163,14 @@
      */
     private final boolean mIsCredentialSharableWithParent;
 
+    /**
+     * Denotes the default access control for {@link CrossProfileIntentFilter} of user profile.
+     *
+     * <p> Default value is {@link CrossProfileIntentFilter#ACCESS_LEVEL_ALL}
+     */
+    private final @CrossProfileIntentFilter.AccessControlLevel int
+            mCrossProfileIntentFilterAccessControl;
+
     private UserTypeDetails(@NonNull String name, boolean enabled, int maxAllowed,
             @UserInfoFlag int baseType, @UserInfoFlag int defaultUserInfoPropertyFlags, int label,
             int maxAllowedPerParent,
@@ -174,7 +182,8 @@
             @Nullable Bundle defaultSecureSettings,
             @Nullable List<DefaultCrossProfileIntentFilter> defaultCrossProfileIntentFilters,
             boolean isMediaSharedWithParent,
-            boolean isCredentialSharableWithParent) {
+            boolean isCredentialSharableWithParent,
+            @CrossProfileIntentFilter.AccessControlLevel int accessControlLevel) {
         this.mName = name;
         this.mEnabled = enabled;
         this.mMaxAllowed = maxAllowed;
@@ -195,6 +204,7 @@
         this.mDarkThemeBadgeColors = darkThemeBadgeColors;
         this.mIsMediaSharedWithParent = isMediaSharedWithParent;
         this.mIsCredentialSharableWithParent = isCredentialSharableWithParent;
+        this.mCrossProfileIntentFilterAccessControl = accessControlLevel;
     }
 
     /**
@@ -327,6 +337,16 @@
         return mIsCredentialSharableWithParent;
     }
 
+    /**
+     * Returning user's {@link CrossProfileIntentFilter.AccessControlLevel}. If not explicitly
+     * configured, default value is {@link CrossProfileIntentFilter#ACCESS_LEVEL_ALL}
+     * @return user's {@link CrossProfileIntentFilter.AccessControlLevel}
+     */
+    public @CrossProfileIntentFilter.AccessControlLevel int
+            getCrossProfileIntentFilterAccessControl() {
+        return mCrossProfileIntentFilterAccessControl;
+    }
+
     /** Returns a {@link Bundle} representing the default user restrictions. */
     @NonNull Bundle getDefaultRestrictions() {
         return BundleUtils.clone(mDefaultRestrictions);
@@ -420,6 +440,8 @@
         private @DrawableRes int mBadgeNoBackground = Resources.ID_NULL;
         private boolean mIsMediaSharedWithParent = false;
         private boolean mIsCredentialSharableWithParent = false;
+        private @CrossProfileIntentFilter.AccessControlLevel int
+                mCrossProfileIntentFilterAccessControl = CrossProfileIntentFilter.ACCESS_LEVEL_ALL;
 
         public Builder setName(String name) {
             mName = name;
@@ -520,6 +542,16 @@
         }
 
         /**
+         * Sets {@link CrossProfileIntentFilter.AccessControlLevel} for the user.
+         * @param accessControlLevel default access control for user
+         */
+        public Builder setCrossProfileIntentFilterAccessControl(
+                @CrossProfileIntentFilter.AccessControlLevel int accessControlLevel) {
+            mCrossProfileIntentFilterAccessControl = accessControlLevel;
+            return this;
+        }
+
+        /**
          * Sets shared media property for the user.
          * @param isCredentialSharableWithParent  the value to be set, true or false
          */
@@ -571,7 +603,8 @@
                     mDefaultSecureSettings,
                     mDefaultCrossProfileIntentFilters,
                     mIsMediaSharedWithParent,
-                    mIsCredentialSharableWithParent);
+                    mIsCredentialSharableWithParent,
+                    mCrossProfileIntentFilterAccessControl);
         }
 
         private boolean hasBadge() {
diff --git a/services/core/java/com/android/server/pm/UserTypeFactory.java b/services/core/java/com/android/server/pm/UserTypeFactory.java
index 0878da4..857a975 100644
--- a/services/core/java/com/android/server/pm/UserTypeFactory.java
+++ b/services/core/java/com/android/server/pm/UserTypeFactory.java
@@ -122,6 +122,8 @@
                 .setLabel(0)
                 .setDefaultRestrictions(null)
                 .setIsMediaSharedWithParent(true)
+                .setCrossProfileIntentFilterAccessControl(
+                        CrossProfileIntentFilter.ACCESS_LEVEL_SYSTEM)
                 .setIsCredentialSharableWithParent(true);
     }
 
diff --git a/services/core/java/com/android/server/pm/VerifyingSession.java b/services/core/java/com/android/server/pm/VerifyingSession.java
index 9bad4a8..0a39e64 100644
--- a/services/core/java/com/android/server/pm/VerifyingSession.java
+++ b/services/core/java/com/android/server/pm/VerifyingSession.java
@@ -353,45 +353,54 @@
         }
         final int verifierUserId = verifierUser.getIdentifier();
 
-        String requiredVerifierPackage = mPm.mRequiredVerifierPackage;
-        boolean requiredVerifierPackageOverridden = false;
+        String[] requiredVerifierPackages = mPm.mRequiredVerifierPackages;
+        boolean requiredVerifierPackagesOverridden = false;
 
         // Allow verifier override for ADB installations which could already be unverified using
         // PackageManager.INSTALL_DISABLE_VERIFICATION flag.
         if ((mInstallFlags & PackageManager.INSTALL_FROM_ADB) != 0
                 && (mInstallFlags & PackageManager.INSTALL_DISABLE_VERIFICATION) == 0) {
-            final String adbVerifierOverridePackage = SystemProperties.get(
-                    "debug.pm.adb_verifier_override_package", "");
-            // Check if the package installed.
-            if (!TextUtils.isEmpty(adbVerifierOverridePackage)
-                    && packageExists(adbVerifierOverridePackage)) {
-                // Pretend we requested to disable verification from command line.
-                boolean requestedDisableVerification = true;
-                // If this returns false then the caller can already skip verification, so we are
-                // not adding a new way to disable verifications.
-                if (!isAdbVerificationEnabled(pkgLite, verifierUserId,
-                        requestedDisableVerification)) {
-                    requiredVerifierPackage = adbVerifierOverridePackage;
-                    requiredVerifierPackageOverridden = true;
+            String property = SystemProperties.get("debug.pm.adb_verifier_override_packages", "");
+            if (!TextUtils.isEmpty(property)) {
+                String[] verifierPackages = property.split(";");
+                List<String> adbVerifierOverridePackages = new ArrayList<>();
+                for (String verifierPackage : verifierPackages) {
+                    if (!TextUtils.isEmpty(verifierPackage) && packageExists(verifierPackage)) {
+                        adbVerifierOverridePackages.add(verifierPackage);
+                    }
+                }
+                // Check if the package installed.
+                if (adbVerifierOverridePackages.size() > 0) {
+                    // Pretend we requested to disable verification from command line.
+                    boolean requestedDisableVerification = true;
+                    // If this returns false then the caller can already skip verification, so we
+                    // are not adding a new way to disable verifications.
+                    if (!isAdbVerificationEnabled(pkgLite, verifierUserId,
+                            requestedDisableVerification)) {
+                        requiredVerifierPackages = adbVerifierOverridePackages.toArray(
+                                new String[adbVerifierOverridePackages.size()]);
+                        requiredVerifierPackagesOverridden = true;
+                    }
                 }
             }
         }
 
+        if (mOriginInfo.mExisting || !isVerificationEnabled(pkgLite, verifierUserId,
+                requiredVerifierPackages)) {
+            verificationState.passRequiredVerification();
+            return;
+        }
+
         /*
          * Determine if we have any installed package verifiers. If we
          * do, then we'll defer to them to verify the packages.
          */
         final Computer snapshot = mPm.snapshotComputer();
-        final int requiredUid = requiredVerifierPackage == null ? -1
-                : snapshot.getPackageUid(requiredVerifierPackage,
-                        MATCH_DEBUG_TRIAGED_MISSING, verifierUserId);
-        verificationState.setRequiredVerifierUid(requiredUid);
-        final boolean isVerificationEnabled = isVerificationEnabled(pkgLite,
-                verifierUserId);
 
-        if (mOriginInfo.mExisting || !isVerificationEnabled) {
-            verificationState.setVerifierResponse(requiredUid, PackageManager.VERIFICATION_ALLOW);
-            return;
+        for (String requiredVerifierPackage : requiredVerifierPackages) {
+            final int requiredUid = snapshot.getPackageUid(requiredVerifierPackage,
+                    MATCH_DEBUG_TRIAGED_MISSING, verifierUserId);
+            verificationState.addRequiredVerifierUid(requiredUid);
         }
 
         final Intent verification = new Intent(Intent.ACTION_PACKAGE_NEEDS_VERIFICATION);
@@ -428,11 +437,13 @@
         final String baseCodePath = mPackageLite.getBaseApkPath();
         final String[] splitCodePaths = mPackageLite.getSplitApkPaths();
 
+        final String rootHashString;
         if (IncrementalManager.isIncrementalPath(baseCodePath)) {
-            final String rootHashString =
-                    PackageManagerServiceUtils.buildVerificationRootHashString(
-                            baseCodePath, splitCodePaths);
+            rootHashString = PackageManagerServiceUtils.buildVerificationRootHashString(
+                    baseCodePath, splitCodePaths);
             verification.putExtra(PackageManager.EXTRA_VERIFICATION_ROOT_HASH, rootHashString);
+        } else {
+            rootHashString = null;
         }
 
         verification.putExtra(PackageInstaller.EXTRA_DATA_LOADER_TYPE, mDataLoaderType);
@@ -503,8 +514,8 @@
             }
         }
 
-        if (requiredVerifierPackage == null) {
-            Slog.e(TAG, "Required verifier is null");
+        if (requiredVerifierPackages.length == 0) {
+            Slog.e(TAG, "No required verifiers");
             return;
         }
 
@@ -514,47 +525,75 @@
         } else {
             verificationCodeAtTimeout = PackageManager.VERIFICATION_REJECT;
         }
-        final PackageVerificationResponse response = new PackageVerificationResponse(
-                verificationCodeAtTimeout, requiredUid);
 
-        /*
-         * Send the intent to the required verification agent,
-         * but only start the verification timeout after the
-         * target BroadcastReceivers have run.
-         */
-        if (!requiredVerifierPackageOverridden) {
-            final ComponentName requiredVerifierComponent = matchComponentForVerifier(
-                    requiredVerifierPackage, receivers.getList());
-            verification.setComponent(requiredVerifierComponent);
-        } else {
-            verification.setPackage(requiredVerifierPackage);
-        }
-        idleController.addPowerSaveTempWhitelistApp(Process.myUid(),
-                requiredVerifierPackage, verificationTimeout,
-                verifierUserId, false,
-                REASON_PACKAGE_VERIFIER, "package verifier");
+        for (String requiredVerifierPackage : requiredVerifierPackages) {
+            final int requiredUid = snapshot.getPackageUid(requiredVerifierPackage,
+                    MATCH_DEBUG_TRIAGED_MISSING, verifierUserId);
 
-        if (streaming) {
-            // For streaming installations, count verification timeout from the broadcast.
-            startVerificationTimeoutCountdown(verificationId, streaming, response,
-                    verificationTimeout);
-        }
+            final Intent requiredIntent;
+            final String receiverPermission;
+            if (!requiredVerifierPackagesOverridden || requiredVerifierPackages.length == 1) {
+                // Prod code OR test code+single verifier.
+                requiredIntent = new Intent(verification);
+                if (!requiredVerifierPackagesOverridden) {
+                    ComponentName requiredVerifierComponent = matchComponentForVerifier(
+                            requiredVerifierPackage, receivers.getList());
+                    requiredIntent.setComponent(requiredVerifierComponent);
+                } else {
+                    requiredIntent.setPackage(requiredVerifierPackage);
+                }
+                receiverPermission = android.Manifest.permission.PACKAGE_VERIFICATION_AGENT;
+            } else {
+                // Test code+multiple verifiers.
+                // Recreate the intent to contain test-only information.
+                requiredIntent = new Intent(Intent.ACTION_PACKAGE_NEEDS_VERIFICATION);
+                requiredIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+                requiredIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+                requiredIntent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
+                requiredIntent.setDataAndType(Uri.fromFile(new File(mOriginInfo.mResolvedPath)),
+                        PACKAGE_MIME_TYPE);
+                requiredIntent.putExtra(PackageInstaller.EXTRA_SESSION_ID, mSessionId);
+                requiredIntent.putExtra(PackageInstaller.EXTRA_DATA_LOADER_TYPE, mDataLoaderType);
+                if (rootHashString != null) {
+                    requiredIntent.putExtra(PackageManager.EXTRA_VERIFICATION_ROOT_HASH,
+                            rootHashString);
+                }
+                requiredIntent.setPackage(requiredVerifierPackage);
+                // Negative verification id.
+                requiredIntent.putExtra(PackageManager.EXTRA_VERIFICATION_ID, -verificationId);
+                // OK not to have permission.
+                receiverPermission = null;
+            }
 
-        mPm.mContext.sendOrderedBroadcastAsUser(verification, verifierUser,
-                android.Manifest.permission.PACKAGE_VERIFICATION_AGENT,
-                /* appOp= */ AppOpsManager.OP_NONE,
-                /* options= */ options.toBundle(),
-                new BroadcastReceiver() {
-                    @Override
-                    public void onReceive(Context context, Intent intent) {
-                        if (!streaming) {
-                            // For NON-streaming installations, count verification timeout from
-                            // the broadcast was processed by all receivers.
-                            startVerificationTimeoutCountdown(verificationId, streaming, response,
-                                    verificationTimeout);
+            idleController.addPowerSaveTempWhitelistApp(Process.myUid(), requiredVerifierPackage,
+                    verificationTimeout, verifierUserId, false, REASON_PACKAGE_VERIFIER,
+                    "package verifier");
+
+            final PackageVerificationResponse response = new PackageVerificationResponse(
+                    verificationCodeAtTimeout, requiredUid);
+
+            if (streaming) {
+                // For streaming installations, count verification timeout from the broadcast.
+                startVerificationTimeoutCountdown(verificationId, streaming, response,
+                        verificationTimeout);
+            }
+
+            // Send the intent to the required verification agent, but only start the
+            // verification timeout after the target BroadcastReceivers have run.
+            mPm.mContext.sendOrderedBroadcastAsUser(requiredIntent, verifierUser,
+                    receiverPermission, AppOpsManager.OP_NONE, options.toBundle(),
+                    new BroadcastReceiver() {
+                        @Override
+                        public void onReceive(Context context, Intent intent) {
+                            if (!streaming) {
+                                // For NON-streaming installations, count verification timeout from
+                                // the broadcast was processed by all receivers.
+                                startVerificationTimeoutCountdown(verificationId, streaming,
+                                        response, verificationTimeout);
+                            }
                         }
-                    }
-                }, null, 0, null, null);
+                    }, null, 0, null, null);
+        }
 
         Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "verification", verificationId);
 
@@ -617,7 +656,8 @@
      *
      * @return true if verification should be performed
      */
-    private boolean isVerificationEnabled(PackageInfoLite pkgInfoLite, int userId) {
+    private boolean isVerificationEnabled(PackageInfoLite pkgInfoLite, int userId,
+            String[] requiredVerifierPackages) {
         if (!DEFAULT_VERIFY_ENABLE) {
             return false;
         }
@@ -634,18 +674,21 @@
 
         // only when not installed from ADB, skip verification for instant apps when
         // the installer and verifier are the same.
-        if ((installFlags & PackageManager.INSTALL_INSTANT_APP) != 0) {
-            if (mPm.mInstantAppInstallerActivity != null
-                    && mPm.mInstantAppInstallerActivity.packageName.equals(
-                    mPm.mRequiredVerifierPackage)) {
-                try {
-                    mPm.mInjector.getSystemService(AppOpsManager.class)
-                            .checkPackage(installerUid, mPm.mRequiredVerifierPackage);
-                    if (DEBUG_VERIFY) {
-                        Slog.i(TAG, "disable verification for instant app");
+        if ((installFlags & PackageManager.INSTALL_INSTANT_APP) != 0
+                && mPm.mInstantAppInstallerActivity != null) {
+            String installerPackage = mPm.mInstantAppInstallerActivity.packageName;
+            for (String requiredVerifierPackage : requiredVerifierPackages) {
+                if (installerPackage.equals(requiredVerifierPackage)) {
+                    try {
+                        mPm.mInjector.getSystemService(AppOpsManager.class)
+                                .checkPackage(installerUid, requiredVerifierPackage);
+                        if (DEBUG_VERIFY) {
+                            Slog.i(TAG, "disable verification for instant app");
+                        }
+                        return false;
+                    } catch (SecurityException ignore) {
                     }
-                    return false;
-                } catch (SecurityException ignore) { }
+                }
             }
         }
         return true;
diff --git a/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java b/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java
index d49227d..905bcf9 100644
--- a/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java
+++ b/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java
@@ -64,7 +64,7 @@
         COMPILATION_REASON_MAP.put(PackageManagerService.REASON_FIRST_BOOT, ArtStatsLog.
                 ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_FIRST_BOOT);
         COMPILATION_REASON_MAP.put(PackageManagerService.REASON_BOOT_AFTER_OTA, ArtStatsLog.
-                ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_BOOT);
+                ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_BOOT_AFTER_OTA);
         COMPILATION_REASON_MAP.put(PackageManagerService.REASON_POST_BOOT, ArtStatsLog.
                 ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_POST_BOOT);
         COMPILATION_REASON_MAP.put(PackageManagerService.REASON_INSTALL, ArtStatsLog.
diff --git a/services/core/java/com/android/server/pm/pkg/component/InstallConstraintsTagParser.java b/services/core/java/com/android/server/pm/pkg/component/InstallConstraintsTagParser.java
new file mode 100644
index 0000000..1497684
--- /dev/null
+++ b/services/core/java/com/android/server/pm/pkg/component/InstallConstraintsTagParser.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.pkg.component;
+
+
+import android.content.pm.parsing.result.ParseInput;
+import android.content.pm.parsing.result.ParseResult;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.os.Build;
+import android.util.ArraySet;
+
+import com.android.internal.R;
+import com.android.server.SystemConfig;
+import com.android.server.pm.pkg.parsing.ParsingPackage;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.Set;
+
+/**
+ * Utility methods for handling the tag {@code <install-constraints/>}
+ *
+ * @hide
+ */
+public class InstallConstraintsTagParser {
+
+    private static final String TAG_FINGERPRINT_PREFIX = "fingerprint-prefix";
+
+    /**
+     * @hide
+     */
+    public static ParseResult<ParsingPackage> parseInstallConstraints(
+            ParseInput input, ParsingPackage pkg, Resources res, XmlResourceParser parser)
+            throws XmlPullParserException, IOException {
+        Set<String> allowlist = SystemConfig.getInstance().getInstallConstraintsAllowlist();
+        if (!allowlist.contains(pkg.getPackageName())) {
+            return input.skip("install-constraints cannot be used by this package");
+        }
+
+        ParseResult<Set<String>> prefixes = parseFingerprintPrefixes(input, res, parser);
+        if (prefixes.isSuccess()) {
+            if (validateFingerprintPrefixes(prefixes.getResult())) {
+                return input.success(pkg);
+            } else {
+                return input.skip(
+                        "Install of this package is restricted on this device; device fingerprint"
+                                + " does not start with one of the allowed prefixes");
+            }
+        }
+        return input.skip(prefixes.getErrorMessage());
+    }
+
+    private static ParseResult<Set<String>> parseFingerprintPrefixes(
+            ParseInput input, Resources res, XmlResourceParser parser)
+            throws XmlPullParserException, IOException {
+        Set<String> prefixes = new ArraySet<>();
+        int type;
+        while (true) {
+            // move to the tag that contains the next prefix
+            type = parser.next();
+            if (type == XmlPullParser.END_TAG) {
+                if (prefixes.size() == 0) {
+                    return input.error("install-constraints must contain at least one constraint");
+                }
+                return input.success(prefixes);
+            } else if (type == XmlPullParser.START_TAG) {
+                if (parser.getName().equals(TAG_FINGERPRINT_PREFIX)) {
+                    ParseResult<String> parsedPrefix =
+                            readFingerprintPrefixValue(input, res, parser);
+                    if (parsedPrefix.isSuccess()) {
+                        prefixes.add(parsedPrefix.getResult());
+                    } else {
+                        return input.error(parsedPrefix.getErrorMessage());
+                    }
+                } else {
+                    return input.error("Unexpected tag: " + parser.getName());
+                }
+
+                // consume the end tag of this attribute
+                type = parser.next();
+                if (type != XmlPullParser.END_TAG) {
+                    return input.error("Expected end tag; instead got " + type);
+                }
+            }
+        }
+    }
+
+    private static ParseResult<String> readFingerprintPrefixValue(ParseInput input, Resources res,
+            XmlResourceParser parser) {
+        TypedArray sa = res.obtainAttributes(parser,
+                R.styleable.AndroidManifestInstallConstraintsFingerprintPrefix);
+        try {
+            String value = sa.getString(
+                    R.styleable.AndroidManifestInstallConstraintsFingerprintPrefix_value);
+            if (value == null) {
+                return input.error("Failed to specify prefix value");
+            }
+            return input.success(value);
+        } finally {
+            sa.recycle();
+        }
+    }
+
+    private static boolean validateFingerprintPrefixes(Set<String> prefixes) {
+        String fingerprint = Build.FINGERPRINT;
+        for (String prefix : prefixes) {
+            if (fingerprint.startsWith(prefix)) {
+                return true;
+            }
+        }
+        return false;
+    }
+}
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 ee59810..8f953e7 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
@@ -293,6 +293,10 @@
                         pi.requestedPermissionsFlags[i] |=
                                 PackageInfo.REQUESTED_PERMISSION_NEVER_FOR_LOCATION;
                     }
+                    if (pkg.getImplicitPermissions().contains(pi.requestedPermissions[i])) {
+                        pi.requestedPermissionsFlags[i] |=
+                                PackageInfo.REQUESTED_PERMISSION_IMPLICIT;
+                    }
                 }
             }
         }
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 b60b9a0..5b8f473 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
@@ -94,6 +94,7 @@
 import com.android.server.pm.permission.CompatibilityPermissionInfo;
 import com.android.server.pm.pkg.component.ComponentMutateUtils;
 import com.android.server.pm.pkg.component.ComponentParseUtils;
+import com.android.server.pm.pkg.component.InstallConstraintsTagParser;
 import com.android.server.pm.pkg.component.ParsedActivity;
 import com.android.server.pm.pkg.component.ParsedActivityUtils;
 import com.android.server.pm.pkg.component.ParsedApexSystemService;
@@ -168,9 +169,11 @@
 
     public static final String TAG_ADOPT_PERMISSIONS = "adopt-permissions";
     public static final String TAG_APPLICATION = "application";
+    public static final String TAG_ATTRIBUTION = "attribution";
     public static final String TAG_COMPATIBLE_SCREENS = "compatible-screens";
     public static final String TAG_EAT_COMMENT = "eat-comment";
     public static final String TAG_FEATURE_GROUP = "feature-group";
+    public static final String TAG_INSTALL_CONSTRAINTS = "install-constraints";
     public static final String TAG_INSTRUMENTATION = "instrumentation";
     public static final String TAG_KEY_SETS = "key-sets";
     public static final String TAG_MANIFEST = "manifest";
@@ -178,15 +181,16 @@
     public static final String TAG_OVERLAY = "overlay";
     public static final String TAG_PACKAGE = "package";
     public static final String TAG_PACKAGE_VERIFIER = "package-verifier";
-    public static final String TAG_ATTRIBUTION = "attribution";
     public static final String TAG_PERMISSION = "permission";
     public static final String TAG_PERMISSION_GROUP = "permission-group";
     public static final String TAG_PERMISSION_TREE = "permission-tree";
+    public static final String TAG_PROFILEABLE = "profileable";
     public static final String TAG_PROTECTED_BROADCAST = "protected-broadcast";
     public static final String TAG_QUERIES = "queries";
+    public static final String TAG_RECEIVER = "receiver";
     public static final String TAG_RESTRICT_UPDATE = "restrict-update";
-    public static final String TAG_SUPPORT_SCREENS = "supports-screens";
     public static final String TAG_SUPPORTS_INPUT = "supports-input";
+    public static final String TAG_SUPPORT_SCREENS = "supports-screens";
     public static final String TAG_USES_CONFIGURATION = "uses-configuration";
     public static final String TAG_USES_FEATURE = "uses-feature";
     public static final String TAG_USES_GL_TEXTURE = "uses-gl-texture";
@@ -195,8 +199,6 @@
     public static final String TAG_USES_PERMISSION_SDK_M = "uses-permission-sdk-m";
     public static final String TAG_USES_SDK = "uses-sdk";
     public static final String TAG_USES_SPLIT = "uses-split";
-    public static final String TAG_PROFILEABLE = "profileable";
-    public static final String TAG_RECEIVER = "receiver";
 
     public static final String METADATA_MAX_ASPECT_RATIO = "android.max_aspect";
     public static final String METADATA_SUPPORTS_SIZE_CHANGES = "android.supports_size_changes";
@@ -1026,6 +1028,8 @@
                 return input.success(pkg);
             case TAG_RESTRICT_UPDATE:
                 return parseRestrictUpdateHash(flags, input, pkg, res, parser);
+            case TAG_INSTALL_CONSTRAINTS:
+                return parseInstallConstraints(input, pkg, res, parser);
             case TAG_QUERIES:
                 return parseQueries(input, pkg, res, parser);
             default:
@@ -1719,6 +1723,12 @@
         return input.success(pkg);
     }
 
+    private static ParseResult<ParsingPackage> parseInstallConstraints(
+            ParseInput input, ParsingPackage pkg, Resources res, XmlResourceParser parser)
+            throws IOException, XmlPullParserException {
+        return InstallConstraintsTagParser.parseInstallConstraints(input, pkg, res, parser);
+    }
+
     private static ParseResult<ParsingPackage> parseQueries(ParseInput input, ParsingPackage pkg,
             Resources res, XmlResourceParser parser) throws IOException, XmlPullParserException {
         final int depth = parser.getDepth();
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index e062c38..abedd96 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -131,6 +131,7 @@
 import com.android.internal.app.HeavyWeightSwitcherActivity;
 import com.android.internal.app.IVoiceInteractor;
 import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.util.FrameworkStatsLog;
 import com.android.server.am.PendingIntentRecord;
 import com.android.server.pm.InstantAppResolver;
 import com.android.server.power.ShutdownCheckPoints;
@@ -2095,6 +2096,49 @@
             }
         }
 
+        // Log activity starts which violate one of the following rules of the
+        // activity security model (ASM):
+        // 1. Only the top activity on a task can start activities on that task
+        // 2. Only the top activity on the top task can create new (top) tasks
+        // We don't currently block, but these checks may later become blocks
+        // TODO(b/236234252): Shift to BackgroundActivityStartController once
+        // class is ready
+        if (mSourceRecord != null) {
+            int callerUid = mSourceRecord.getUid();
+            ActivityRecord targetTopActivity =
+                    targetTask != null ? targetTask.getTopNonFinishingActivity() : null;
+            boolean passesAsmChecks = newTask
+                    ? mRootWindowContainer.hasResumedActivity(callerUid)
+                    : targetTopActivity != null && targetTopActivity.getUid() == callerUid;
+
+            if (!passesAsmChecks) {
+                Slog.i(TAG, "Launching r: " + r
+                        + " from background: " + mSourceRecord
+                        + ". New task: " + newTask);
+                boolean newOrEmptyTask = newTask || (targetTopActivity == null);
+                FrameworkStatsLog.write(FrameworkStatsLog.ACTIVITY_ACTION_BLOCKED,
+                        /* caller_uid */
+                        callerUid,
+                        /* caller_activity_class_name */
+                        mSourceRecord.info.name,
+                        /* target_task_top_activity_uid */
+                        newOrEmptyTask ? -1 : targetTopActivity.getUid(),
+                        /* target_task_top_activity_class_name */
+                        newOrEmptyTask ? null : targetTopActivity.info.name,
+                        /* target_task_is_different */
+                        newTask || !mSourceRecord.getTask().equals(targetTask),
+                        /* target_activity_uid */
+                        r.getUid(),
+                        /* target_activity_class_name */
+                        r.info.name,
+                        /* target_intent_action */
+                        r.intent.getAction(),
+                        /* target_intent_flags */
+                        r.intent.getFlags()
+                );
+            }
+        }
+
         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 848e9a9..87a4fe9 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -2311,16 +2311,25 @@
      * @return a list of {@link ActivityManager.RunningTaskInfo} with up to {@code maxNum} items
      */
     public List<ActivityManager.RunningTaskInfo> getTasks(int maxNum) {
-        return getTasks(maxNum, false /* filterForVisibleRecents */, false /* keepIntentExtra */);
+        return getTasks(maxNum, false /* filterForVisibleRecents */, false /* keepIntentExtra */,
+                INVALID_DISPLAY);
     }
 
     /**
      * @param filterOnlyVisibleRecents whether to filter the tasks based on whether they would ever
      *                                 be visible in the recent task list in systemui
      */
-    @Override
     public List<ActivityManager.RunningTaskInfo> getTasks(int maxNum,
             boolean filterOnlyVisibleRecents, boolean keepIntentExtra) {
+        return getTasks(maxNum, filterOnlyVisibleRecents, keepIntentExtra, INVALID_DISPLAY);
+    }
+
+    /**
+     * @param displayId the target display id, or {@link INVALID_DISPLAY} not to filter by displayId
+     */
+    @Override
+    public List<ActivityManager.RunningTaskInfo> getTasks(int maxNum,
+            boolean filterOnlyVisibleRecents, boolean keepIntentExtra, int displayId) {
         final int callingUid = Binder.getCallingUid();
         final int callingPid = Binder.getCallingPid();
 
@@ -2342,7 +2351,7 @@
             final boolean allowed = isGetTasksAllowed("getTasks", callingPid, callingUid);
             flags |= (allowed ? RunningTasks.FLAG_ALLOWED : 0);
             mRootWindowContainer.getRunningTasks(
-                    maxNum, list, flags, callingUid, callingProfileIds);
+                    maxNum, list, flags, callingUid, callingProfileIds, displayId);
         }
 
         return list;
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index 345ec11..be995a8 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -886,11 +886,12 @@
 
                 logIfTransactionTooLarge(r.intent, r.getSavedState());
 
-                if (r.isEmbedded()) {
+                final TaskFragment organizedTaskFragment = r.getOrganizedTaskFragment();
+                if (organizedTaskFragment != null) {
                     // Sending TaskFragmentInfo to client to ensure the info is updated before
                     // the activity creation.
                     mService.mTaskFragmentOrganizerController.dispatchPendingInfoChangedEvent(
-                            r.getOrganizedTaskFragment());
+                            organizedTaskFragment);
                 }
 
                 // Create activity launch transaction.
@@ -1874,6 +1875,9 @@
         // End power mode launch before going sleep
         mService.endLaunchPowerMode(ActivityTaskManagerService.POWER_MODE_REASON_ALL);
 
+        // Rank task layers to make sure the {@link Task#mLayerRank} is updated.
+        mRootWindowContainer.rankTaskLayers();
+
         removeSleepTimeouts();
 
         if (mGoingToSleepWakeLock.isHeld()) {
diff --git a/services/core/java/com/android/server/wm/CompatModePackages.java b/services/core/java/com/android/server/wm/CompatModePackages.java
index 2ea043a..6f19450 100644
--- a/services/core/java/com/android/server/wm/CompatModePackages.java
+++ b/services/core/java/com/android/server/wm/CompatModePackages.java
@@ -24,6 +24,7 @@
 
 import android.app.ActivityManager;
 import android.app.AppGlobals;
+import android.app.GameManagerInternal;
 import android.app.compat.CompatChanges;
 import android.compat.annotation.ChangeId;
 import android.compat.annotation.Disabled;
@@ -48,6 +49,7 @@
 import android.util.Xml;
 
 import com.android.internal.protolog.common.ProtoLog;
+import com.android.server.LocalServices;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -64,6 +66,7 @@
     private static final String TAG_CONFIGURATION = TAG + POSTFIX_CONFIGURATION;
 
     private final ActivityTaskManagerService mService;
+    private GameManagerInternal mGameManager;
     private final AtomicFile mFile;
 
     // Compatibility state: no longer ask user to select the mode.
@@ -375,6 +378,18 @@
 
     float getCompatScale(String packageName, int uid) {
         final UserHandle userHandle = UserHandle.getUserHandleForUid(uid);
+        if (mGameManager == null) {
+            mGameManager = LocalServices.getService(GameManagerInternal.class);
+        }
+        if (mGameManager != null) {
+            final int userId = userHandle.getIdentifier();
+            final float scalingFactor = mGameManager.getResolutionScalingFactor(packageName,
+                    userId);
+            if (scalingFactor > 0) {
+                return 1f / scalingFactor;
+            }
+        }
+
         if (CompatChanges.isChangeEnabled(DOWNSCALED, packageName, userHandle)) {
             if (CompatChanges.isChangeEnabled(DOWNSCALE_90, packageName, userHandle)) {
                 return 1f / 0.9f;
diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java
index 3e2d7c9..7a9e6a9 100644
--- a/services/core/java/com/android/server/wm/InsetsPolicy.java
+++ b/services/core/java/com/android/server/wm/InsetsPolicy.java
@@ -398,16 +398,18 @@
 
         if (WindowConfiguration.isFloating(windowingMode)
                 || (windowingMode == WINDOWING_MODE_MULTI_WINDOW && isAlwaysOnTop)) {
-            if (!stateCopied) {
-                state = new InsetsState(state);
-                stateCopied = true;
+            InsetsState newState = new InsetsState();
+
+            // Only caption and IME are needed.
+            if (state.peekSource(ITYPE_CAPTION_BAR) != null) {
+                newState.addSource(state.peekSource(ITYPE_CAPTION_BAR));
             }
-            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);
+            if (windowingMode != WINDOWING_MODE_PINNED && state.peekSource(ITYPE_IME) != null) {
+                newState.addSource(state.peekSource(ITYPE_IME));
             }
+
+            state = newState;
+            stateCopied = true;
         }
 
         return state;
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index d8c3795..552c6a5 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -1820,6 +1820,10 @@
         return getItemFromTaskDisplayAreas(TaskDisplayArea::getFocusedActivity);
     }
 
+    boolean hasResumedActivity(int uid) {
+        return forAllActivities(ar -> ar.isState(RESUMED) && ar.getUid() == uid);
+    }
+
     boolean isTopDisplayFocusedRootTask(Task task) {
         return task != null && task == getTopDisplayFocusedRootTask();
     }
@@ -3326,9 +3330,16 @@
 
     @VisibleForTesting
     void getRunningTasks(int maxNum, List<ActivityManager.RunningTaskInfo> list,
-            int flags, int callingUid, ArraySet<Integer> profileIds) {
-        mTaskSupervisor.getRunningTasks().getTasks(maxNum, list, flags, this, callingUid,
-                profileIds);
+            int flags, int callingUid, ArraySet<Integer> profileIds, int displayId) {
+        WindowContainer root = this;
+        if (displayId != INVALID_DISPLAY) {
+            root = getDisplayContent(displayId);
+            if (root == null) {
+                return;
+            }
+        }
+        mTaskSupervisor.getRunningTasks().getTasks(maxNum, list, flags, mService.getRecentTasks(),
+                root, callingUid, profileIds);
     }
 
     void startPowerModeLaunchIfNeeded(boolean forceSend, ActivityRecord targetActivity) {
diff --git a/services/core/java/com/android/server/wm/RunningTasks.java b/services/core/java/com/android/server/wm/RunningTasks.java
index 1ec191e..120fec0 100644
--- a/services/core/java/com/android/server/wm/RunningTasks.java
+++ b/services/core/java/com/android/server/wm/RunningTasks.java
@@ -60,8 +60,8 @@
     private RecentTasks mRecentTasks;
     private boolean mKeepIntentExtra;
 
-    void getTasks(int maxNum, List<RunningTaskInfo> list, int flags,
-            RootWindowContainer root, int callingUid, ArraySet<Integer> profileIds) {
+    void getTasks(int maxNum, List<RunningTaskInfo> list, int flags, RecentTasks recentTasks,
+            WindowContainer root, int callingUid, ArraySet<Integer> profileIds) {
         // Return early if there are no tasks to fetch
         if (maxNum <= 0) {
             return;
@@ -76,7 +76,7 @@
         mAllowed = (flags & FLAG_ALLOWED) == FLAG_ALLOWED;
         mFilterOnlyVisibleRecents =
                 (flags & FLAG_FILTER_ONLY_VISIBLE_RECENTS) == FLAG_FILTER_ONLY_VISIBLE_RECENTS;
-        mRecentTasks = root.mService.getRecentTasks();
+        mRecentTasks = recentTasks;
         mKeepIntentExtra = (flags & FLAG_KEEP_INTENT_EXTRA) == FLAG_KEEP_INTENT_EXTRA;
 
         final PooledConsumer c = PooledLambda.obtainConsumer(RunningTasks::processTask, this,
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index e65f0bb..8de556a 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -1109,6 +1109,9 @@
         // as the one in the task because either one of them could be the alias activity.
         if (Objects.equals(realActivity, r.mActivityComponent) && this.intent != null) {
             intent.setComponent(this.intent.getComponent());
+            // Make sure the package name the same to prevent one of the intent is set while the
+            // other one is not.
+            intent.setPackage(this.intent.getPackage());
         }
         return intent.filterEquals(this.intent);
     }
@@ -5319,7 +5322,23 @@
                     parentLaunchMode == ActivityInfo.LAUNCH_SINGLE_TASK ||
                     parentLaunchMode == ActivityInfo.LAUNCH_SINGLE_TOP ||
                     (destIntentFlags & Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0) {
-                parent.deliverNewIntentLocked(callingUid, destIntent, destGrants, srec.packageName);
+                boolean abort;
+                try {
+                    abort = !mTaskSupervisor.checkStartAnyActivityPermission(destIntent,
+                            parent.info, null /* resultWho */, -1 /* requestCode */, srec.getPid(),
+                            callingUid, srec.info.packageName, null /* callingFeatureId */,
+                            false /* ignoreTargetSecurity */, false /* launchingInTask */, srec.app,
+                            null /* resultRecord */, null /* resultRootTask */);
+                } catch (SecurityException e) {
+                    abort = true;
+                }
+                if (abort) {
+                    Slog.e(TAG, "Cannot navigateUpTo, intent =" + destIntent);
+                    foundParentInTask = false;
+                } else {
+                    parent.deliverNewIntentLocked(callingUid, destIntent, destGrants,
+                            srec.packageName);
+                }
             } else {
                 try {
                     ActivityInfo aInfo = AppGlobals.getPackageManager().getActivityInfo(
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index 589618e..e6afdd6 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -586,7 +586,10 @@
         }
 
         // Cannot embed activity across TaskFragments for activity result.
-        if (a.resultTo != null && a.resultTo.getTaskFragment() != this) {
+        // If the activity that started for result is finishing, it's likely that this start mode
+        // is used to place an activity in the same task. Since the finishing activity won't be
+        // able to get the results, so it's OK to embed in a different TaskFragment.
+        if (a.resultTo != null && !a.resultTo.finishing && a.resultTo.getTaskFragment() != this) {
             return EMBEDDING_DISALLOWED_NEW_TASK_FRAGMENT;
         }
 
diff --git a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
index 602579f..6ca5648 100644
--- a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
@@ -16,12 +16,21 @@
 
 package com.android.server.wm;
 
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
 import static android.window.TaskFragmentOrganizer.putErrorInfoInBundle;
+import static android.window.TaskFragmentTransaction.TYPE_ACTIVITY_REPARENT_TO_TASK;
+import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_APPEARED;
+import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_ERROR;
+import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_INFO_CHANGED;
+import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_PARENT_INFO_CHANGED;
+import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_VANISHED;
 
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANIZER;
 import static com.android.server.wm.TaskFragment.EMBEDDING_ALLOWED;
 import static com.android.server.wm.WindowOrganizerController.configurationsAreEqualForOrganizer;
 
+import static java.util.Objects.requireNonNull;
+
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -32,18 +41,21 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.util.ArrayMap;
+import android.util.ArraySet;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.view.RemoteAnimationDefinition;
 import android.window.ITaskFragmentOrganizer;
 import android.window.ITaskFragmentOrganizerController;
 import android.window.TaskFragmentInfo;
+import android.window.TaskFragmentTransaction;
 
 import com.android.internal.protolog.common.ProtoLog;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
+import java.util.List;
 import java.util.Map;
 import java.util.WeakHashMap;
 
@@ -63,10 +75,12 @@
     private final ArrayMap<IBinder, TaskFragmentOrganizerState> mTaskFragmentOrganizerState =
             new ArrayMap<>();
     /**
-     * A List which manages the TaskFragment pending event {@link PendingTaskFragmentEvent}
+     * Map from {@link ITaskFragmentOrganizer} to a list of related {@link PendingTaskFragmentEvent}
      */
-    private final ArrayList<PendingTaskFragmentEvent> mPendingTaskFragmentEvents =
-            new ArrayList<>();
+    private final ArrayMap<IBinder, List<PendingTaskFragmentEvent>> mPendingTaskFragmentEvents =
+            new ArrayMap<>();
+
+    private final ArraySet<Task> mTmpTaskSet = new ArraySet<>();
 
     TaskFragmentOrganizerController(ActivityTaskManagerService atm) {
         mAtmService = atm;
@@ -82,10 +96,30 @@
         private final ITaskFragmentOrganizer mOrganizer;
         private final int mOrganizerPid;
         private final int mOrganizerUid;
+
+        /**
+         * Map from {@link TaskFragment} to the last {@link TaskFragmentInfo} sent to the
+         * organizer.
+         */
         private final Map<TaskFragment, TaskFragmentInfo> mLastSentTaskFragmentInfos =
                 new WeakHashMap<>();
-        private final Map<TaskFragment, Configuration> mLastSentTaskFragmentParentConfigs =
-                new WeakHashMap<>();
+
+        /**
+         * Map from {@link TaskFragment} to its leaf {@link Task#mTaskId}. Embedded
+         * {@link TaskFragment} will not be reparented until it is removed.
+         */
+        private final Map<TaskFragment, Integer> mTaskFragmentTaskIds = new WeakHashMap<>();
+
+        /**
+         * Map from {@link Task#mTaskId} to the last Task {@link Configuration} sent to the
+         * organizer.
+         */
+        private final SparseArray<Configuration> mLastSentTaskFragmentParentConfigs =
+                new SparseArray<>();
+
+        /**
+         * Map from temporary activity token to the corresponding {@link ActivityRecord}.
+         */
         private final Map<IBinder, ActivityRecord> mTemporaryActivityTokens =
                 new WeakHashMap<>();
 
@@ -145,107 +179,120 @@
             mOrganizer.asBinder().unlinkToDeath(this, 0 /*flags*/);
         }
 
-        void onTaskFragmentAppeared(TaskFragment tf) {
+        @NonNull
+        TaskFragmentTransaction.Change prepareTaskFragmentAppeared(@NonNull TaskFragment tf) {
             ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "TaskFragment appeared name=%s", tf.getName());
             final TaskFragmentInfo info = tf.getTaskFragmentInfo();
-            try {
-                mOrganizer.onTaskFragmentAppeared(info);
-                mLastSentTaskFragmentInfos.put(tf, info);
-                tf.mTaskFragmentAppearedSent = true;
-            } catch (RemoteException e) {
-                Slog.d(TAG, "Exception sending onTaskFragmentAppeared callback", e);
-            }
-            onTaskFragmentParentInfoChanged(tf);
+            final int taskId = tf.getTask().mTaskId;
+            tf.mTaskFragmentAppearedSent = true;
+            mLastSentTaskFragmentInfos.put(tf, info);
+            mTaskFragmentTaskIds.put(tf, taskId);
+            return new TaskFragmentTransaction.Change(
+                    TYPE_TASK_FRAGMENT_APPEARED)
+                    .setTaskFragmentToken(tf.getFragmentToken())
+                    .setTaskFragmentInfo(info)
+                    .setTaskId(taskId);
         }
 
-        void onTaskFragmentVanished(TaskFragment tf) {
+        @NonNull
+        TaskFragmentTransaction.Change prepareTaskFragmentVanished(@NonNull TaskFragment tf) {
             ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "TaskFragment vanished name=%s", tf.getName());
-            try {
-                mOrganizer.onTaskFragmentVanished(tf.getTaskFragmentInfo());
-            } catch (RemoteException e) {
-                Slog.d(TAG, "Exception sending onTaskFragmentVanished callback", e);
-            }
             tf.mTaskFragmentAppearedSent = false;
             mLastSentTaskFragmentInfos.remove(tf);
-            mLastSentTaskFragmentParentConfigs.remove(tf);
+
+            // Cleanup TaskFragmentParentConfig if this is the last TaskFragment in the Task.
+            final int taskId;
+            if (mTaskFragmentTaskIds.containsKey(tf)) {
+                taskId = mTaskFragmentTaskIds.remove(tf);
+                if (!mTaskFragmentTaskIds.containsValue(taskId)) {
+                    // No more TaskFragment in the Task.
+                    mLastSentTaskFragmentParentConfigs.remove(taskId);
+                }
+            } else {
+                // This can happen if the appeared wasn't sent before remove.
+                taskId = INVALID_TASK_ID;
+            }
+
+            return new TaskFragmentTransaction.Change(TYPE_TASK_FRAGMENT_VANISHED)
+                    .setTaskFragmentToken(tf.getFragmentToken())
+                    .setTaskFragmentInfo(tf.getTaskFragmentInfo())
+                    .setTaskId(taskId);
         }
 
-        void onTaskFragmentInfoChanged(TaskFragment tf) {
-            // Parent config may have changed. The controller will check if there is any important
-            // config change for the organizer.
-            onTaskFragmentParentInfoChanged(tf);
-
+        @Nullable
+        TaskFragmentTransaction.Change prepareTaskFragmentInfoChanged(
+                @NonNull TaskFragment tf) {
             // Check if the info is different from the last reported info.
             final TaskFragmentInfo info = tf.getTaskFragmentInfo();
             final TaskFragmentInfo lastInfo = mLastSentTaskFragmentInfos.get(tf);
             if (info.equalsForTaskFragmentOrganizer(lastInfo) && configurationsAreEqualForOrganizer(
                     info.getConfiguration(), lastInfo.getConfiguration())) {
-                return;
+                return null;
             }
+
             ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "TaskFragment info changed name=%s",
                     tf.getName());
-            try {
-                mOrganizer.onTaskFragmentInfoChanged(info);
-                mLastSentTaskFragmentInfos.put(tf, info);
-            } catch (RemoteException e) {
-                Slog.d(TAG, "Exception sending onTaskFragmentInfoChanged callback", e);
-            }
+            mLastSentTaskFragmentInfos.put(tf, info);
+            return new TaskFragmentTransaction.Change(
+                    TYPE_TASK_FRAGMENT_INFO_CHANGED)
+                    .setTaskFragmentToken(tf.getFragmentToken())
+                    .setTaskFragmentInfo(info)
+                    .setTaskId(tf.getTask().mTaskId);
         }
 
-        void onTaskFragmentParentInfoChanged(TaskFragment tf) {
+        @Nullable
+        TaskFragmentTransaction.Change prepareTaskFragmentParentInfoChanged(
+                @NonNull Task task) {
+            final int taskId = task.mTaskId;
             // Check if the parent info is different from the last reported parent info.
-            if (tf.getParent() == null || tf.getParent().asTask() == null) {
-                mLastSentTaskFragmentParentConfigs.remove(tf);
-                return;
-            }
-            final Task parent = tf.getParent().asTask();
-            final Configuration parentConfig = parent.getConfiguration();
-            final Configuration lastParentConfig = mLastSentTaskFragmentParentConfigs.get(tf);
-            if (configurationsAreEqualForOrganizer(parentConfig, lastParentConfig)
-                    && parentConfig.windowConfiguration.getWindowingMode()
+            final Configuration taskConfig = task.getConfiguration();
+            final Configuration lastParentConfig = mLastSentTaskFragmentParentConfigs.get(taskId);
+            if (configurationsAreEqualForOrganizer(taskConfig, lastParentConfig)
+                    && taskConfig.windowConfiguration.getWindowingMode()
                     == lastParentConfig.windowConfiguration.getWindowingMode()) {
-                return;
+                return null;
             }
+
             ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER,
                     "TaskFragment parent info changed name=%s parentTaskId=%d",
-                    tf.getName(), parent.mTaskId);
-            try {
-                mOrganizer.onTaskFragmentParentInfoChanged(tf.getFragmentToken(), parentConfig);
-                mLastSentTaskFragmentParentConfigs.put(tf, new Configuration(parentConfig));
-            } catch (RemoteException e) {
-                Slog.d(TAG, "Exception sending onTaskFragmentParentInfoChanged callback", e);
-            }
+                    task.getName(), taskId);
+            mLastSentTaskFragmentParentConfigs.put(taskId, new Configuration(taskConfig));
+            return new TaskFragmentTransaction.Change(TYPE_TASK_FRAGMENT_PARENT_INFO_CHANGED)
+                    .setTaskId(taskId)
+                    .setTaskConfiguration(taskConfig);
         }
 
-        void onTaskFragmentError(IBinder errorCallbackToken, @Nullable TaskFragment taskFragment,
-                int opType, Throwable exception) {
+        @NonNull
+        TaskFragmentTransaction.Change prepareTaskFragmentError(
+                @Nullable IBinder errorCallbackToken, @Nullable TaskFragment taskFragment,
+                int opType, @NonNull Throwable exception) {
             ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER,
                     "Sending TaskFragment error exception=%s", exception.toString());
             final TaskFragmentInfo info =
                     taskFragment != null ? taskFragment.getTaskFragmentInfo() : null;
             final Bundle errorBundle = putErrorInfoInBundle(exception, info, opType);
-            try {
-                mOrganizer.onTaskFragmentError(errorCallbackToken, errorBundle);
-            } catch (RemoteException e) {
-                Slog.d(TAG, "Exception sending onTaskFragmentError callback", e);
-            }
+            return new TaskFragmentTransaction.Change(TYPE_TASK_FRAGMENT_ERROR)
+                    .setErrorCallbackToken(errorCallbackToken)
+                    .setErrorBundle(errorBundle);
         }
 
-        void onActivityReparentToTask(ActivityRecord activity) {
+        @Nullable
+        TaskFragmentTransaction.Change prepareActivityReparentToTask(
+                @NonNull ActivityRecord activity) {
             if (activity.finishing) {
                 Slog.d(TAG, "Reparent activity=" + activity.token + " is finishing");
-                return;
+                return null;
             }
             final Task task = activity.getTask();
             if (task == null || task.effectiveUid != mOrganizerUid) {
                 Slog.d(TAG, "Reparent activity=" + activity.token
                         + " is not in a task belong to the organizer app.");
-                return;
+                return null;
             }
             if (task.isAllowedToEmbedActivity(activity, mOrganizerUid) != EMBEDDING_ALLOWED) {
                 Slog.d(TAG, "Reparent activity=" + activity.token
                         + " is not allowed to be embedded.");
-                return;
+                return null;
             }
 
             final IBinder activityToken;
@@ -268,11 +315,10 @@
             }
             ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Activity=%s reparent to taskId=%d",
                     activity.token, task.mTaskId);
-            try {
-                mOrganizer.onActivityReparentToTask(task.mTaskId, activity.intent, activityToken);
-            } catch (RemoteException e) {
-                Slog.d(TAG, "Exception sending onActivityReparentToTask callback", e);
-            }
+            return new TaskFragmentTransaction.Change(TYPE_ACTIVITY_REPARENT_TO_TASK)
+                    .setTaskId(task.mTaskId)
+                    .setActivityIntent(activity.intent)
+                    .setActivityToken(activityToken);
         }
     }
 
@@ -303,6 +349,7 @@
             }
             mTaskFragmentOrganizerState.put(organizer.asBinder(),
                     new TaskFragmentOrganizerState(organizer, pid, uid));
+            mPendingTaskFragmentEvents.put(organizer.asBinder(), new ArrayList<>());
         }
     }
 
@@ -375,7 +422,7 @@
      */
     @Nullable
     public RemoteAnimationDefinition getRemoteAnimationDefinition(
-            ITaskFragmentOrganizer organizer, int taskId) {
+            @NonNull ITaskFragmentOrganizer organizer, int taskId) {
         synchronized (mGlobalLock) {
             final TaskFragmentOrganizerState organizerState =
                     mTaskFragmentOrganizerState.get(organizer.asBinder());
@@ -385,12 +432,18 @@
         }
     }
 
-    int getTaskFragmentOrganizerUid(ITaskFragmentOrganizer organizer) {
+    int getTaskFragmentOrganizerUid(@NonNull ITaskFragmentOrganizer organizer) {
         final TaskFragmentOrganizerState state = validateAndGetState(organizer);
         return state.mOrganizerUid;
     }
 
-    void onTaskFragmentAppeared(ITaskFragmentOrganizer organizer, TaskFragment taskFragment) {
+    void onTaskFragmentAppeared(@NonNull ITaskFragmentOrganizer organizer,
+            @NonNull TaskFragment taskFragment) {
+        if (taskFragment.getTask() == null) {
+            Slog.w(TAG, "onTaskFragmentAppeared failed because it is not attached tf="
+                    + taskFragment);
+            return;
+        }
         final TaskFragmentOrganizerState state = validateAndGetState(organizer);
         if (!state.addTaskFragment(taskFragment)) {
             return;
@@ -398,27 +451,15 @@
         PendingTaskFragmentEvent pendingEvent = getPendingTaskFragmentEvent(taskFragment,
                 PendingTaskFragmentEvent.EVENT_APPEARED);
         if (pendingEvent == null) {
-            pendingEvent = new PendingTaskFragmentEvent.Builder(
+            addPendingEvent(new PendingTaskFragmentEvent.Builder(
                     PendingTaskFragmentEvent.EVENT_APPEARED, organizer)
                     .setTaskFragment(taskFragment)
-                    .build();
-            mPendingTaskFragmentEvents.add(pendingEvent);
+                    .build());
         }
     }
 
-    void onTaskFragmentInfoChanged(ITaskFragmentOrganizer organizer, TaskFragment taskFragment) {
-        handleTaskFragmentInfoChanged(organizer, taskFragment,
-                PendingTaskFragmentEvent.EVENT_INFO_CHANGED);
-    }
-
-    void onTaskFragmentParentInfoChanged(ITaskFragmentOrganizer organizer,
-            TaskFragment taskFragment) {
-        handleTaskFragmentInfoChanged(organizer, taskFragment,
-                PendingTaskFragmentEvent.EVENT_PARENT_INFO_CHANGED);
-    }
-
-    private void handleTaskFragmentInfoChanged(ITaskFragmentOrganizer organizer,
-            TaskFragment taskFragment, int eventType) {
+    void onTaskFragmentInfoChanged(@NonNull ITaskFragmentOrganizer organizer,
+            @NonNull TaskFragment taskFragment) {
         validateAndGetState(organizer);
         if (!taskFragment.mTaskFragmentAppearedSent) {
             // Skip if TaskFragment still not appeared.
@@ -426,64 +467,61 @@
         }
         PendingTaskFragmentEvent pendingEvent = getLastPendingLifecycleEvent(taskFragment);
         if (pendingEvent == null) {
-            pendingEvent = new PendingTaskFragmentEvent.Builder(eventType, organizer)
-                            .setTaskFragment(taskFragment)
-                            .build();
+            pendingEvent = new PendingTaskFragmentEvent.Builder(
+                    PendingTaskFragmentEvent.EVENT_INFO_CHANGED, organizer)
+                    .setTaskFragment(taskFragment)
+                    .build();
         } else {
             if (pendingEvent.mEventType == PendingTaskFragmentEvent.EVENT_VANISHED) {
                 // Skipped the info changed event if vanished event is pending.
                 return;
             }
             // Remove and add for re-ordering.
-            mPendingTaskFragmentEvents.remove(pendingEvent);
+            removePendingEvent(pendingEvent);
             // Reset the defer time when TaskFragment is changed, so that it can check again if
             // the event should be sent to the organizer, for example the TaskFragment may become
             // empty.
             pendingEvent.mDeferTime = 0;
         }
-        mPendingTaskFragmentEvents.add(pendingEvent);
+        addPendingEvent(pendingEvent);
     }
 
-    void onTaskFragmentVanished(ITaskFragmentOrganizer organizer, TaskFragment taskFragment) {
+    void onTaskFragmentVanished(@NonNull ITaskFragmentOrganizer organizer,
+            @NonNull TaskFragment taskFragment) {
         final TaskFragmentOrganizerState state = validateAndGetState(organizer);
-        for (int i = mPendingTaskFragmentEvents.size() - 1; i >= 0; i--) {
-            PendingTaskFragmentEvent entry = mPendingTaskFragmentEvents.get(i);
-            if (taskFragment == entry.mTaskFragment) {
-                mPendingTaskFragmentEvents.remove(i);
-                if (entry.mEventType == PendingTaskFragmentEvent.EVENT_APPEARED) {
-                    // If taskFragment appeared callback is pending, ignore the vanished request.
-                    return;
-                }
+        final List<PendingTaskFragmentEvent> pendingEvents = mPendingTaskFragmentEvents
+                .get(organizer.asBinder());
+        // Remove any pending events since this TaskFragment is being removed.
+        for (int i = pendingEvents.size() - 1; i >= 0; i--) {
+            final PendingTaskFragmentEvent event = pendingEvents.get(i);
+            if (taskFragment == event.mTaskFragment) {
+                pendingEvents.remove(i);
             }
         }
-        if (!taskFragment.mTaskFragmentAppearedSent) {
-            return;
-        }
-        final PendingTaskFragmentEvent pendingEvent = new PendingTaskFragmentEvent.Builder(
+        addPendingEvent(new PendingTaskFragmentEvent.Builder(
                 PendingTaskFragmentEvent.EVENT_VANISHED, organizer)
                 .setTaskFragment(taskFragment)
-                .build();
-        mPendingTaskFragmentEvents.add(pendingEvent);
+                .build());
         state.removeTaskFragment(taskFragment);
     }
 
-    void onTaskFragmentError(ITaskFragmentOrganizer organizer, IBinder errorCallbackToken,
-            TaskFragment taskFragment, int opType, Throwable exception) {
+    void onTaskFragmentError(@NonNull ITaskFragmentOrganizer organizer,
+            @Nullable IBinder errorCallbackToken, @Nullable TaskFragment taskFragment,
+            int opType, @NonNull Throwable exception) {
         validateAndGetState(organizer);
         Slog.w(TAG, "onTaskFragmentError ", exception);
-        final PendingTaskFragmentEvent pendingEvent = new PendingTaskFragmentEvent.Builder(
+        addPendingEvent(new PendingTaskFragmentEvent.Builder(
                 PendingTaskFragmentEvent.EVENT_ERROR, organizer)
                 .setErrorCallbackToken(errorCallbackToken)
                 .setTaskFragment(taskFragment)
                 .setException(exception)
                 .setOpType(opType)
-                .build();
-        mPendingTaskFragmentEvents.add(pendingEvent);
+                .build());
         // Make sure the error event will be dispatched if there are no other changes.
         mAtmService.mWindowManager.mWindowPlacerLocked.requestTraversal();
     }
 
-    void onActivityReparentToTask(ActivityRecord activity) {
+    void onActivityReparentToTask(@NonNull ActivityRecord activity) {
         final ITaskFragmentOrganizer organizer;
         if (activity.mLastTaskFragmentOrganizerBeforePip != null) {
             // If the activity is previously embedded in an organized TaskFragment.
@@ -508,28 +546,30 @@
             Slog.w(TAG, "The last TaskFragmentOrganizer no longer exists");
             return;
         }
-        final PendingTaskFragmentEvent pendingEvent = new PendingTaskFragmentEvent.Builder(
+        addPendingEvent(new PendingTaskFragmentEvent.Builder(
                 PendingTaskFragmentEvent.EVENT_ACTIVITY_REPARENT_TO_TASK, organizer)
                 .setActivity(activity)
-                .build();
-        mPendingTaskFragmentEvents.add(pendingEvent);
+                .build());
     }
 
-    boolean isOrganizerRegistered(ITaskFragmentOrganizer organizer) {
+    private void addPendingEvent(@NonNull PendingTaskFragmentEvent event) {
+        mPendingTaskFragmentEvents.get(event.mTaskFragmentOrg.asBinder()).add(event);
+    }
+
+    private void removePendingEvent(@NonNull PendingTaskFragmentEvent event) {
+        mPendingTaskFragmentEvents.get(event.mTaskFragmentOrg.asBinder()).remove(event);
+    }
+
+    boolean isOrganizerRegistered(@NonNull ITaskFragmentOrganizer organizer) {
         return mTaskFragmentOrganizerState.containsKey(organizer.asBinder());
     }
 
-    private void removeOrganizer(ITaskFragmentOrganizer organizer) {
+    private void removeOrganizer(@NonNull ITaskFragmentOrganizer organizer) {
         final TaskFragmentOrganizerState state = validateAndGetState(organizer);
         // remove all of the children of the organized TaskFragment
         state.dispose();
         // Remove any pending event of this organizer.
-        for (int i = mPendingTaskFragmentEvents.size() - 1; i >= 0; i--) {
-            final PendingTaskFragmentEvent event = mPendingTaskFragmentEvents.get(i);
-            if (event.mTaskFragmentOrg.asBinder().equals(organizer.asBinder())) {
-                mPendingTaskFragmentEvents.remove(i);
-            }
-        }
+        mPendingTaskFragmentEvents.remove(organizer.asBinder());
         mTaskFragmentOrganizerState.remove(organizer.asBinder());
     }
 
@@ -539,7 +579,9 @@
      * we wouldn't register {@link DeathRecipient} for the organizer, and might not remove the
      * {@link TaskFragment} after the organizer process died.
      */
-    private TaskFragmentOrganizerState validateAndGetState(ITaskFragmentOrganizer organizer) {
+    @NonNull
+    private TaskFragmentOrganizerState validateAndGetState(
+            @NonNull ITaskFragmentOrganizer organizer) {
         final TaskFragmentOrganizerState state =
                 mTaskFragmentOrganizerState.get(organizer.asBinder());
         if (state == null) {
@@ -583,6 +625,8 @@
         private final Throwable mException;
         @Nullable
         private final ActivityRecord mActivity;
+        @Nullable
+        private final Task mTask;
         // Set when the event is deferred due to the host task is invisible. The defer time will
         // be the last active time of the host task.
         private long mDeferTime;
@@ -594,6 +638,7 @@
                 @Nullable IBinder errorCallbackToken,
                 @Nullable Throwable exception,
                 @Nullable ActivityRecord activity,
+                @Nullable Task task,
                 int opType) {
             mEventType = eventType;
             mTaskFragmentOrg = taskFragmentOrg;
@@ -601,6 +646,7 @@
             mErrorCallbackToken = errorCallbackToken;
             mException = exception;
             mActivity = activity;
+            mTask = task;
             mOpType = opType;
         }
 
@@ -632,11 +678,13 @@
             private Throwable mException;
             @Nullable
             private ActivityRecord mActivity;
+            @Nullable
+            private Task mTask;
             private int mOpType;
 
-            Builder(@EventType int eventType, ITaskFragmentOrganizer taskFragmentOrg) {
+            Builder(@EventType int eventType, @NonNull ITaskFragmentOrganizer taskFragmentOrg) {
                 mEventType = eventType;
-                mTaskFragmentOrg = taskFragmentOrg;
+                mTaskFragmentOrg = requireNonNull(taskFragmentOrg);
             }
 
             Builder setTaskFragment(@Nullable TaskFragment taskFragment) {
@@ -649,13 +697,18 @@
                 return this;
             }
 
-            Builder setException(@Nullable Throwable exception) {
-                mException = exception;
+            Builder setException(@NonNull Throwable exception) {
+                mException = requireNonNull(exception);
                 return this;
             }
 
-            Builder setActivity(@Nullable ActivityRecord activity) {
-                mActivity = activity;
+            Builder setActivity(@NonNull ActivityRecord activity) {
+                mActivity = requireNonNull(activity);
+                return this;
+            }
+
+            Builder setTask(@NonNull Task task) {
+                mTask = requireNonNull(task);
                 return this;
             }
 
@@ -666,29 +719,35 @@
 
             PendingTaskFragmentEvent build() {
                 return new PendingTaskFragmentEvent(mEventType, mTaskFragmentOrg, mTaskFragment,
-                        mErrorCallbackToken, mException, mActivity, mOpType);
+                        mErrorCallbackToken, mException, mActivity, mTask, mOpType);
             }
         }
     }
 
     @Nullable
-    private PendingTaskFragmentEvent getLastPendingLifecycleEvent(TaskFragment tf) {
-        for (int i = mPendingTaskFragmentEvents.size() - 1; i >= 0; i--) {
-            PendingTaskFragmentEvent entry = mPendingTaskFragmentEvents.get(i);
-            if (tf == entry.mTaskFragment && entry.isLifecycleEvent()) {
-                return entry;
+    private PendingTaskFragmentEvent getLastPendingLifecycleEvent(@NonNull TaskFragment tf) {
+        final ITaskFragmentOrganizer organizer = tf.getTaskFragmentOrganizer();
+        final List<PendingTaskFragmentEvent> events = mPendingTaskFragmentEvents
+                .get(organizer.asBinder());
+        for (int i = events.size() - 1; i >= 0; i--) {
+            final PendingTaskFragmentEvent event = events.get(i);
+            if (tf == event.mTaskFragment && event.isLifecycleEvent()) {
+                return event;
             }
         }
         return null;
     }
 
     @Nullable
-    private PendingTaskFragmentEvent getPendingTaskFragmentEvent(TaskFragment taskFragment,
+    private PendingTaskFragmentEvent getPendingTaskFragmentEvent(@NonNull TaskFragment taskFragment,
             int type) {
-        for (int i = mPendingTaskFragmentEvents.size() - 1; i >= 0; i--) {
-            PendingTaskFragmentEvent entry = mPendingTaskFragmentEvents.get(i);
-            if (taskFragment == entry.mTaskFragment && type == entry.mEventType) {
-                return entry;
+        final ITaskFragmentOrganizer organizer = taskFragment.getTaskFragmentOrganizer();
+        final List<PendingTaskFragmentEvent> events = mPendingTaskFragmentEvents
+                .get(organizer.asBinder());
+        for (int i = events.size() - 1; i >= 0; i--) {
+            final PendingTaskFragmentEvent event = events.get(i);
+            if (taskFragment == event.mTaskFragment && type == event.mEventType) {
+                return event;
             }
         }
         return null;
@@ -714,12 +773,25 @@
                 || mPendingTaskFragmentEvents.isEmpty()) {
             return;
         }
+        final int organizerNum = mPendingTaskFragmentEvents.size();
+        for (int i = 0; i < organizerNum; i++) {
+            final ITaskFragmentOrganizer organizer = mTaskFragmentOrganizerState.get(
+                    mPendingTaskFragmentEvents.keyAt(i)).mOrganizer;
+            dispatchPendingEvents(organizer, mPendingTaskFragmentEvents.valueAt(i));
+        }
+    }
+
+    void dispatchPendingEvents(@NonNull ITaskFragmentOrganizer organizer,
+            @NonNull List<PendingTaskFragmentEvent> pendingEvents) {
+        if (pendingEvents.isEmpty()) {
+            return;
+        }
 
         final ArrayList<Task> visibleTasks = new ArrayList<>();
         final ArrayList<Task> invisibleTasks = new ArrayList<>();
         final ArrayList<PendingTaskFragmentEvent> candidateEvents = new ArrayList<>();
-        for (int i = 0, n = mPendingTaskFragmentEvents.size(); i < n; i++) {
-            final PendingTaskFragmentEvent event = mPendingTaskFragmentEvents.get(i);
+        for (int i = 0, n = pendingEvents.size(); i < n; i++) {
+            final PendingTaskFragmentEvent event = pendingEvents.get(i);
             final Task task = event.mTaskFragment != null ? event.mTaskFragment.getTask() : null;
             if (task != null && (task.lastActiveTime <= event.mDeferTime
                     || !(isTaskVisible(task, visibleTasks, invisibleTasks)
@@ -731,16 +803,35 @@
             candidateEvents.add(event);
         }
         final int numEvents = candidateEvents.size();
+        if (numEvents == 0) {
+            return;
+        }
+
+        mTmpTaskSet.clear();
+        final TaskFragmentTransaction transaction = new TaskFragmentTransaction();
         for (int i = 0; i < numEvents; i++) {
-            dispatchEvent(candidateEvents.get(i));
+            final PendingTaskFragmentEvent event = candidateEvents.get(i);
+            if (event.mEventType == PendingTaskFragmentEvent.EVENT_APPEARED
+                    || event.mEventType == PendingTaskFragmentEvent.EVENT_INFO_CHANGED) {
+                final Task task = event.mTaskFragment.getTask();
+                if (mTmpTaskSet.add(task)) {
+                    // Make sure the organizer know about the Task config.
+                    transaction.addChange(prepareChange(new PendingTaskFragmentEvent.Builder(
+                            PendingTaskFragmentEvent.EVENT_PARENT_INFO_CHANGED, organizer)
+                            .setTask(task)
+                            .build()));
+                }
+            }
+            transaction.addChange(prepareChange(event));
         }
-        if (numEvents > 0) {
-            mPendingTaskFragmentEvents.removeAll(candidateEvents);
-        }
+        mTmpTaskSet.clear();
+        dispatchTransactionInfo(organizer, transaction);
+        pendingEvents.removeAll(candidateEvents);
     }
 
-    private static boolean isTaskVisible(Task task, ArrayList<Task> knownVisibleTasks,
-            ArrayList<Task> knownInvisibleTasks) {
+    private static boolean isTaskVisible(@NonNull Task task,
+            @NonNull ArrayList<Task> knownVisibleTasks,
+            @NonNull ArrayList<Task> knownInvisibleTasks) {
         if (knownVisibleTasks.contains(task)) {
             return true;
         }
@@ -756,44 +847,63 @@
         }
     }
 
-    void dispatchPendingInfoChangedEvent(TaskFragment taskFragment) {
-        PendingTaskFragmentEvent event = getPendingTaskFragmentEvent(taskFragment,
+    void dispatchPendingInfoChangedEvent(@NonNull TaskFragment taskFragment) {
+        final PendingTaskFragmentEvent event = getPendingTaskFragmentEvent(taskFragment,
                 PendingTaskFragmentEvent.EVENT_INFO_CHANGED);
         if (event == null) {
             return;
         }
 
-        dispatchEvent(event);
-        mPendingTaskFragmentEvents.remove(event);
+        final ITaskFragmentOrganizer organizer = taskFragment.getTaskFragmentOrganizer();
+        final TaskFragmentTransaction transaction = new TaskFragmentTransaction();
+        // Make sure the organizer know about the Task config.
+        transaction.addChange(prepareChange(new PendingTaskFragmentEvent.Builder(
+                PendingTaskFragmentEvent.EVENT_PARENT_INFO_CHANGED, organizer)
+                .setTask(taskFragment.getTask())
+                .build()));
+        transaction.addChange(prepareChange(event));
+        dispatchTransactionInfo(event.mTaskFragmentOrg, transaction);
+        mPendingTaskFragmentEvents.get(organizer.asBinder()).remove(event);
     }
 
-    private void dispatchEvent(PendingTaskFragmentEvent event) {
+    private void dispatchTransactionInfo(@NonNull ITaskFragmentOrganizer organizer,
+            @NonNull TaskFragmentTransaction transaction) {
+        if (transaction.isEmpty()) {
+            return;
+        }
+        try {
+            organizer.onTransactionReady(transaction);
+        } catch (RemoteException e) {
+            Slog.d(TAG, "Exception sending TaskFragmentTransaction", e);
+        }
+    }
+
+    @Nullable
+    private TaskFragmentTransaction.Change prepareChange(
+            @NonNull PendingTaskFragmentEvent event) {
         final ITaskFragmentOrganizer taskFragmentOrg = event.mTaskFragmentOrg;
         final TaskFragment taskFragment = event.mTaskFragment;
         final TaskFragmentOrganizerState state =
                 mTaskFragmentOrganizerState.get(taskFragmentOrg.asBinder());
         if (state == null) {
-            return;
+            return null;
         }
         switch (event.mEventType) {
             case PendingTaskFragmentEvent.EVENT_APPEARED:
-                state.onTaskFragmentAppeared(taskFragment);
-                break;
+                return state.prepareTaskFragmentAppeared(taskFragment);
             case PendingTaskFragmentEvent.EVENT_VANISHED:
-                state.onTaskFragmentVanished(taskFragment);
-                break;
+                return state.prepareTaskFragmentVanished(taskFragment);
             case PendingTaskFragmentEvent.EVENT_INFO_CHANGED:
-                state.onTaskFragmentInfoChanged(taskFragment);
-                break;
+                return state.prepareTaskFragmentInfoChanged(taskFragment);
             case PendingTaskFragmentEvent.EVENT_PARENT_INFO_CHANGED:
-                state.onTaskFragmentParentInfoChanged(taskFragment);
-                break;
+                return state.prepareTaskFragmentParentInfoChanged(event.mTask);
             case PendingTaskFragmentEvent.EVENT_ERROR:
-                state.onTaskFragmentError(event.mErrorCallbackToken, taskFragment, event.mOpType,
-                        event.mException);
-                break;
+                return state.prepareTaskFragmentError(event.mErrorCallbackToken, taskFragment,
+                        event.mOpType, event.mException);
             case PendingTaskFragmentEvent.EVENT_ACTIVITY_REPARENT_TO_TASK:
-                state.onActivityReparentToTask(event.mActivity);
+                return state.prepareActivityReparentToTask(event.mActivity);
+            default:
+                throw new IllegalArgumentException("Unknown TaskFragmentEvent=" + event.mEventType);
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index adb8bf6..70dd9f3 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -3389,6 +3389,13 @@
             pw.println(prefix + "mLastOrientationSource=" + mLastOrientationSource);
             pw.println(prefix + "deepestLastOrientationSource=" + getLastOrientationSource());
         }
+        if (mLocalInsetsSourceProviders != null && mLocalInsetsSourceProviders.size() != 0) {
+            pw.println(prefix + mLocalInsetsSourceProviders.size() + " LocalInsetsSourceProviders");
+            final String childPrefix = prefix + "  ";
+            for (int i = 0; i < mLocalInsetsSourceProviders.size(); ++i) {
+                mLocalInsetsSourceProviders.valueAt(i).dump(pw, childPrefix);
+            }
+        }
     }
 
     final void updateSurfacePositionNonOrganized() {
diff --git a/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp b/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
index af5718f..1d56078 100644
--- a/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
+++ b/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
@@ -29,8 +29,10 @@
 #include <dirent.h>
 #include <jni.h>
 #include <linux/errno.h>
+#include <linux/time.h>
 #include <log/log.h>
 #include <meminfo/procmeminfo.h>
+#include <meminfo/sysmeminfo.h>
 #include <nativehelper/JNIHelp.h>
 #include <processgroup/processgroup.h>
 #include <stddef.h>
@@ -42,6 +44,7 @@
 #include <sys/sysinfo.h>
 #include <sys/types.h>
 #include <unistd.h>
+#include <utils/Timers.h>
 #include <utils/Trace.h>
 
 #include <algorithm>
@@ -457,6 +460,12 @@
     ATRACE_INSTANT_FOR_TRACK(ATRACE_COMPACTION_TRACK, "Cancel compaction");
 }
 
+static jlong com_android_server_am_CachedAppOptimizer_threadCpuTimeNs(JNIEnv*, jobject) {
+    int64_t currentCpuTime = systemTime(CLOCK_THREAD_CPUTIME_ID);
+
+    return currentCpuTime;
+}
+
 static jdouble com_android_server_am_CachedAppOptimizer_getFreeSwapPercent(JNIEnv*, jobject) {
     struct sysinfo memoryInfo;
     int error = sysinfo(&memoryInfo);
@@ -467,6 +476,16 @@
     return (double)memoryInfo.freeswap / (double)memoryInfo.totalswap;
 }
 
+static jlong com_android_server_am_CachedAppOptimizer_getUsedZramMemory() {
+    android::meminfo::SysMemInfo sysmeminfo;
+    return sysmeminfo.mem_zram_kb();
+}
+
+static jlong com_android_server_am_CachedAppOptimizer_getMemoryFreedCompaction() {
+    android::meminfo::SysMemInfo sysmeminfo;
+    return sysmeminfo.mem_compacted_kb("/sys/block/zram0/");
+}
+
 static void com_android_server_am_CachedAppOptimizer_compactProcess(JNIEnv*, jobject, jint pid,
                                                                     jint compactionFlags) {
     compactProcessOrFallback(pid, compactionFlags);
@@ -520,8 +539,13 @@
         /* name, signature, funcPtr */
         {"cancelCompaction", "()V",
          (void*)com_android_server_am_CachedAppOptimizer_cancelCompaction},
+        {"threadCpuTimeNs", "()J", (void*)com_android_server_am_CachedAppOptimizer_threadCpuTimeNs},
         {"getFreeSwapPercent", "()D",
          (void*)com_android_server_am_CachedAppOptimizer_getFreeSwapPercent},
+        {"getUsedZramMemory", "()J",
+         (void*)com_android_server_am_CachedAppOptimizer_getUsedZramMemory},
+        {"getMemoryFreedCompaction", "()J",
+         (void*)com_android_server_am_CachedAppOptimizer_getMemoryFreedCompaction},
         {"compactSystem", "()V", (void*)com_android_server_am_CachedAppOptimizer_compactSystem},
         {"compactProcess", "(II)V", (void*)com_android_server_am_CachedAppOptimizer_compactProcess},
         {"freezeBinder", "(IZ)I", (void*)com_android_server_am_CachedAppOptimizer_freezeBinder},
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 74ed3df..891bc0f 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -462,10 +462,10 @@
     mInputManager->getReader().dump(dump);
     dump += "\n";
 
-    mInputManager->getUnwantedInteractionBlocker().dump(dump);
+    mInputManager->getBlocker().dump(dump);
     dump += "\n";
 
-    mInputManager->getClassifier().dump(dump);
+    mInputManager->getProcessor().dump(dump);
     dump += "\n";
 
     mInputManager->getDispatcher().dump(dump);
@@ -702,7 +702,7 @@
 
 void NativeInputManager::notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices) {
     ATRACE_CALL();
-    mInputManager->getUnwantedInteractionBlocker().notifyInputDevicesChanged(inputDevices);
+    mInputManager->getBlocker().notifyInputDevicesChanged(inputDevices);
     JNIEnv* env = jniEnv();
 
     size_t count = inputDevices.size();
@@ -1483,7 +1483,7 @@
 }
 
 void NativeInputManager::setMotionClassifierEnabled(bool enabled) {
-    mInputManager->getClassifier().setMotionClassifierEnabled(enabled);
+    mInputManager->getProcessor().setMotionClassifierEnabled(enabled);
 }
 
 bool NativeInputManager::isPerDisplayTouchModeEnabled() {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index b4cade3..c1c7bce 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -3552,11 +3552,6 @@
         final CallerIdentity caller = getCallerIdentity();
         Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle));
 
-        if (mInjector.getPackageManagerInternal().filterAppAccess(packageName, caller.getUid(),
-                userHandle)) {
-            return false;
-        }
-
         synchronized (getLockObject()) {
             DevicePolicyData policy = getUserData(userHandle);
             final int N = policy.mAdminList.size();
diff --git a/services/proguard.flags b/services/proguard.flags
index c648f7d..c930346 100644
--- a/services/proguard.flags
+++ b/services/proguard.flags
@@ -64,6 +64,12 @@
 # Referenced via CarServiceHelperService in car-frameworks-service (avoid removing)
 -keep public class com.android.server.utils.Slogf { *; }
 
+# Referenced in wear-service
+# HIDL interfaces
+-keep public class android.hidl.base.** { *; }
+-keep public class android.hidl.manager.** { *; }
+-keep public class com.android.server.wm.WindowManagerInternal { *; }
+
 # Notification extractors
 # TODO(b/210510433): Revisit and consider generating from frameworks/base/core/res/res/values/config.xml.
 -keep,allowoptimization,allowaccessmodification public class com.android.server.notification.** implements com.android.server.notification.NotificationSignalExtractor
diff --git a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/SystemStubMultiUserDisableUninstallTest.kt b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/SystemStubMultiUserDisableUninstallTest.kt
index 45e0d09..4ef6875 100644
--- a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/SystemStubMultiUserDisableUninstallTest.kt
+++ b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/SystemStubMultiUserDisableUninstallTest.kt
@@ -628,6 +628,7 @@
             CodePath.SAME, CodePath.DIFFERENT ->
                 throw AssertionError("secondDataPath cannot be a data path")
             CodePath.SYSTEM -> assertThat(codePaths[1]).isEqualTo(stubFile.parent.toString())
+            else -> {}
         }
     }
 
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java b/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java
index 2baa1ec..05cad16 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java
@@ -17,12 +17,9 @@
 package com.android.server.am;
 
 import static android.app.ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
-
 import static com.android.server.am.ActivityManagerService.Injector;
-import static com.android.server.am.CachedAppOptimizer.compactActionIntToString;
-
+import static com.android.server.am.CachedAppOptimizer.compactActionIntToAction;
 import static com.google.common.truth.Truth.assertThat;
-
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.spy;
 
@@ -37,15 +34,18 @@
 import android.platform.test.annotations.Presubmit;
 import android.provider.DeviceConfig;
 import android.text.TextUtils;
-
 import androidx.test.platform.app.InstrumentationRegistry;
-
 import com.android.modules.utils.testing.TestableDeviceConfig;
 import com.android.server.LocalServices;
 import com.android.server.ServiceThread;
 import com.android.server.appop.AppOpsService;
 import com.android.server.wm.ActivityTaskManagerService;
-
+import java.io.File;
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
 import org.junit.After;
 import org.junit.Assume;
 import org.junit.Before;
@@ -55,13 +55,6 @@
 import org.mockito.Mock;
 import org.mockito.junit.MockitoJUnitRunner;
 
-import java.io.File;
-import java.io.IOException;
-import java.util.HashSet;
-import java.util.Set;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
 /**
  * Tests for {@link CachedAppOptimizer}.
  *
@@ -147,36 +140,40 @@
     @Test
     public void init_setsDefaults() {
         mCachedAppOptimizerUnderTest.init();
-        assertThat(mCachedAppOptimizerUnderTest.useCompaction()).isEqualTo(
-                CachedAppOptimizer.DEFAULT_USE_COMPACTION);
-        assertThat(mCachedAppOptimizerUnderTest.mCompactActionSome).isEqualTo(
-                compactActionIntToString(CachedAppOptimizer.DEFAULT_COMPACT_ACTION_1));
-        assertThat(mCachedAppOptimizerUnderTest.mCompactActionFull).isEqualTo(
-                compactActionIntToString(CachedAppOptimizer.DEFAULT_COMPACT_ACTION_2));
-        assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleSomeSome).isEqualTo(
-                CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_1);
-        assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleSomeFull).isEqualTo(
-                CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_2);
-        assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleFullSome).isEqualTo(
-                CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_3);
-        assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleFullFull).isEqualTo(
-                CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_4);
-        assertThat(mCachedAppOptimizerUnderTest.mCompactStatsdSampleRate).isEqualTo(
-                CachedAppOptimizer.DEFAULT_STATSD_SAMPLE_RATE);
-        assertThat(mCachedAppOptimizerUnderTest.mFreezerStatsdSampleRate).isEqualTo(
-                CachedAppOptimizer.DEFAULT_STATSD_SAMPLE_RATE);
-        assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleBFGS).isEqualTo(
-                CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_5);
-        assertThat(mCachedAppOptimizerUnderTest.mCompactThrottlePersistent).isEqualTo(
-                CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_6);
-        assertThat(mCachedAppOptimizerUnderTest.mFullAnonRssThrottleKb).isEqualTo(
-                CachedAppOptimizer.DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB);
-        assertThat(mCachedAppOptimizerUnderTest.mFullDeltaRssThrottleKb).isEqualTo(
-                CachedAppOptimizer.DEFAULT_COMPACT_FULL_DELTA_RSS_THROTTLE_KB);
-        assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleMinOomAdj).isEqualTo(
-                CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_MIN_OOM_ADJ);
-        assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleMaxOomAdj).isEqualTo(
-                CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_MAX_OOM_ADJ);
+        synchronized (mCachedAppOptimizerUnderTest.mPhenotypeFlagLock) {
+            assertThat(mCachedAppOptimizerUnderTest.useCompaction()).isEqualTo(
+                    CachedAppOptimizer.DEFAULT_USE_COMPACTION);
+            assertThat(mCachedAppOptimizerUnderTest.mCompactActionSome)
+                    .isEqualTo(
+                            compactActionIntToAction(CachedAppOptimizer.DEFAULT_COMPACT_ACTION_1));
+            assertThat(mCachedAppOptimizerUnderTest.mCompactActionFull)
+                    .isEqualTo(
+                            compactActionIntToAction(CachedAppOptimizer.DEFAULT_COMPACT_ACTION_2));
+            assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleSomeSome).isEqualTo(
+                    CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_1);
+            assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleSomeFull).isEqualTo(
+                    CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_2);
+            assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleFullSome).isEqualTo(
+                    CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_3);
+            assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleFullFull).isEqualTo(
+                    CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_4);
+            assertThat(mCachedAppOptimizerUnderTest.mCompactStatsdSampleRate).isEqualTo(
+                    CachedAppOptimizer.DEFAULT_STATSD_SAMPLE_RATE);
+            assertThat(mCachedAppOptimizerUnderTest.mFreezerStatsdSampleRate).isEqualTo(
+                    CachedAppOptimizer.DEFAULT_STATSD_SAMPLE_RATE);
+            assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleBFGS).isEqualTo(
+                    CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_5);
+            assertThat(mCachedAppOptimizerUnderTest.mCompactThrottlePersistent).isEqualTo(
+                    CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_6);
+            assertThat(mCachedAppOptimizerUnderTest.mFullAnonRssThrottleKb).isEqualTo(
+                    CachedAppOptimizer.DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB);
+            assertThat(mCachedAppOptimizerUnderTest.mFullDeltaRssThrottleKb).isEqualTo(
+                    CachedAppOptimizer.DEFAULT_COMPACT_FULL_DELTA_RSS_THROTTLE_KB);
+            assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleMinOomAdj).isEqualTo(
+                    CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_MIN_OOM_ADJ);
+            assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleMaxOomAdj).isEqualTo(
+                    CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_MAX_OOM_ADJ);
+        }
 
 
         Set<Integer> expected = new HashSet<>();
@@ -192,6 +189,7 @@
                 CachedAppOptimizer.DEFAULT_USE_FREEZER);
     }
 
+    @SuppressWarnings("GuardedBy")
     @Test
     public void init_withDeviceConfigSetsParameters() {
         // When the DeviceConfig already has a flag value stored (note this test will need to
@@ -256,11 +254,11 @@
         assertThat(mCachedAppOptimizerUnderTest.useCompaction()).isTrue();
         assertThat(mCachedAppOptimizerUnderTest.mCachedAppOptimizerThread.isAlive()).isTrue();
 
-        assertThat(mCachedAppOptimizerUnderTest.mCompactActionSome).isEqualTo(
-                compactActionIntToString(
+        assertThat(mCachedAppOptimizerUnderTest.mCompactActionSome)
+                .isEqualTo(compactActionIntToAction(
                         (CachedAppOptimizer.DEFAULT_COMPACT_ACTION_1 + 1 % 4) + 1));
-        assertThat(mCachedAppOptimizerUnderTest.mCompactActionFull).isEqualTo(
-                compactActionIntToString(
+        assertThat(mCachedAppOptimizerUnderTest.mCompactActionFull)
+                .isEqualTo(compactActionIntToAction(
                         (CachedAppOptimizer.DEFAULT_COMPACT_ACTION_2 + 1 % 4) + 1));
         assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleSomeSome).isEqualTo(
                 CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_1 + 1);
@@ -412,10 +410,12 @@
             assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
 
             // Then the updates are reflected in the flags.
-            assertThat(mCachedAppOptimizerUnderTest.mCompactActionSome).isEqualTo(
-                    compactActionIntToString(expectedSome));
-            assertThat(mCachedAppOptimizerUnderTest.mCompactActionFull).isEqualTo(
-                    compactActionIntToString(expectedFull));
+            synchronized (mCachedAppOptimizerUnderTest.mPhenotypeFlagLock) {
+                assertThat(mCachedAppOptimizerUnderTest.mCompactActionSome)
+                        .isEqualTo(compactActionIntToAction(expectedSome));
+                assertThat(mCachedAppOptimizerUnderTest.mCompactActionFull)
+                        .isEqualTo(compactActionIntToAction(expectedFull));
+            }
         }
     }
 
@@ -431,11 +431,15 @@
                 CachedAppOptimizer.KEY_COMPACT_ACTION_2, "foo", false);
         assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
 
-        // Then the default values are reflected in the flag
-        assertThat(mCachedAppOptimizerUnderTest.mCompactActionSome).isEqualTo(
-                compactActionIntToString(CachedAppOptimizer.DEFAULT_COMPACT_ACTION_1));
-        assertThat(mCachedAppOptimizerUnderTest.mCompactActionFull).isEqualTo(
-                compactActionIntToString(CachedAppOptimizer.DEFAULT_COMPACT_ACTION_2));
+        synchronized (mCachedAppOptimizerUnderTest.mPhenotypeFlagLock) {
+            // Then the default values are reflected in the flag
+            assertThat(mCachedAppOptimizerUnderTest.mCompactActionSome)
+                    .isEqualTo(
+                            compactActionIntToAction(CachedAppOptimizer.DEFAULT_COMPACT_ACTION_1));
+            assertThat(mCachedAppOptimizerUnderTest.mCompactActionFull)
+                    .isEqualTo(
+                            compactActionIntToAction(CachedAppOptimizer.DEFAULT_COMPACT_ACTION_2));
+        }
 
         mCountDown = new CountDownLatch(2);
         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
@@ -444,10 +448,14 @@
                 CachedAppOptimizer.KEY_COMPACT_ACTION_2, "", false);
         assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
 
-        assertThat(mCachedAppOptimizerUnderTest.mCompactActionSome).isEqualTo(
-                compactActionIntToString(CachedAppOptimizer.DEFAULT_COMPACT_ACTION_1));
-        assertThat(mCachedAppOptimizerUnderTest.mCompactActionFull).isEqualTo(
-                compactActionIntToString(CachedAppOptimizer.DEFAULT_COMPACT_ACTION_2));
+        synchronized (mCachedAppOptimizerUnderTest.mPhenotypeFlagLock) {
+            assertThat(mCachedAppOptimizerUnderTest.mCompactActionSome)
+                    .isEqualTo(
+                            compactActionIntToAction(CachedAppOptimizer.DEFAULT_COMPACT_ACTION_1));
+            assertThat(mCachedAppOptimizerUnderTest.mCompactActionFull)
+                    .isEqualTo(
+                            compactActionIntToAction(CachedAppOptimizer.DEFAULT_COMPACT_ACTION_2));
+        }
     }
 
     @Test
@@ -893,7 +901,9 @@
         mProcessDependencies.setRss(rssBefore1);
         mProcessDependencies.setRssAfterCompaction(rssAfter1); //
         // WHEN we try to run compaction
-        mCachedAppOptimizerUnderTest.compactAppFull(processRecord, false);
+        mCachedAppOptimizerUnderTest.compactApp(processRecord,
+                CachedAppOptimizer.CompactProfile.FULL, CachedAppOptimizer.CompactSource.APP,
+                false);
         waitForHandler();
         // THEN process IS compacted.
         assertThat(mCachedAppOptimizerUnderTest.mLastCompactionStats.get(pid)).isNotNull();
@@ -908,7 +918,9 @@
         processRecord.mOptRecord.setLastCompactTime(
                 processRecord.mOptRecord.getLastCompactTime() - 10_000);
         // WHEN we try to run compaction.
-        mCachedAppOptimizerUnderTest.compactAppFull(processRecord, false);
+        mCachedAppOptimizerUnderTest.compactApp(processRecord,
+                CachedAppOptimizer.CompactProfile.FULL, CachedAppOptimizer.CompactSource.APP,
+                false);
         waitForHandler();
         // THEN process IS NOT compacted - values after compaction for process 1 should remain the
         // same as from the last compaction.
@@ -924,7 +936,9 @@
         processRecord.mOptRecord.setLastCompactTime(
                 processRecord.mOptRecord.getLastCompactTime() - 10_000);
         // WHEN we try to run compaction
-        mCachedAppOptimizerUnderTest.compactAppFull(processRecord, false);
+        mCachedAppOptimizerUnderTest.compactApp(processRecord,
+                CachedAppOptimizer.CompactProfile.FULL, CachedAppOptimizer.CompactSource.APP,
+                false);
         waitForHandler();
         // THEN process IS compacted - values after compaction for process 1 should be updated.
         assertThat(mCachedAppOptimizerUnderTest.mLastCompactionStats.get(pid)).isNotNull();
@@ -965,7 +979,9 @@
         mProcessDependencies.setRss(rssBelowThreshold);
         mProcessDependencies.setRssAfterCompaction(rssBelowThresholdAfter);
         // WHEN we try to run compaction
-        mCachedAppOptimizerUnderTest.compactAppFull(processRecord, false);
+        mCachedAppOptimizerUnderTest.compactApp(processRecord,
+                CachedAppOptimizer.CompactProfile.FULL, CachedAppOptimizer.CompactSource.APP,
+                false);
         waitForHandler();
         // THEN process IS NOT compacted.
         assertThat(mCachedAppOptimizerUnderTest.mLastCompactionStats.get(pid)).isNull();
@@ -974,7 +990,9 @@
         mProcessDependencies.setRss(rssAboveThreshold);
         mProcessDependencies.setRssAfterCompaction(rssAboveThresholdAfter);
         // WHEN we try to run compaction
-        mCachedAppOptimizerUnderTest.compactAppFull(processRecord, false);
+        mCachedAppOptimizerUnderTest.compactApp(processRecord,
+                CachedAppOptimizer.CompactProfile.FULL, CachedAppOptimizer.CompactSource.APP,
+                false);
         waitForHandler();
         // THEN process IS compacted.
         assertThat(mCachedAppOptimizerUnderTest.mLastCompactionStats.get(pid)).isNotNull();
@@ -1008,22 +1026,16 @@
         mProcessDependencies.setRss(rssBefore);
         mProcessDependencies.setRssAfterCompaction(rssAfter);
 
-        // Compaction should occur if (setAdj < min for process || setAdj > max for process) &&
-        // (MIN < curAdj <  MAX)
-        // GIVEN OomAdj score below threshold.
-        processRecord.mState.setSetAdj(899);
-        processRecord.mState.setCurAdj(970);
-        // WHEN we try to run compaction
-        mCachedAppOptimizerUnderTest.compactAppFull(processRecord, false);
+        // When moving within cached state
+        mCachedAppOptimizerUnderTest.onOomAdjustChanged(
+                ProcessList.CACHED_APP_MIN_ADJ, ProcessList.CACHED_APP_MIN_ADJ + 1, processRecord);
         waitForHandler();
         // THEN process IS NOT compacted.
         assertThat(mCachedAppOptimizerUnderTest.mLastCompactionStats.get(pid)).isNull();
 
-        // GIVEN (setAdj < MIN || setAdj > MAX) && (MIN < curAdj <  MAX)
-        processRecord.mState.setSetAdj(910);
-        processRecord.mState.setCurAdj(930);
-        // WHEN we try to run compaction
-        mCachedAppOptimizerUnderTest.compactAppFull(processRecord, false);
+        // When moving into cached state
+        mCachedAppOptimizerUnderTest.onOomAdjustChanged(ProcessList.CACHED_APP_MIN_ADJ - 1,
+                ProcessList.CACHED_APP_MIN_ADJ + 1, processRecord);
         waitForHandler();
         // THEN process IS compacted.
         assertThat(mCachedAppOptimizerUnderTest.mLastCompactionStats.get(pid)).isNotNull();
@@ -1057,13 +1069,17 @@
         processRecord.mState.setCurAdj(100);
 
         // Compact process full
-        mCachedAppOptimizerUnderTest.compactAppFull(processRecord, false);
+        mCachedAppOptimizerUnderTest.compactApp(processRecord,
+                CachedAppOptimizer.CompactProfile.FULL, CachedAppOptimizer.CompactSource.APP,
+                false);
         waitForHandler();
         // the process is not compacted
         assertThat(mCachedAppOptimizerUnderTest.mLastCompactionStats.get(pid)).isNull();
 
         // Compact process some
-        mCachedAppOptimizerUnderTest.compactAppSome(processRecord, false);
+        mCachedAppOptimizerUnderTest.compactApp(processRecord,
+                CachedAppOptimizer.CompactProfile.SOME, CachedAppOptimizer.CompactSource.APP,
+                false);
         waitForHandler();
         // the process is not compacted
         assertThat(mCachedAppOptimizerUnderTest.mLastCompactionStats.get(pid)).isNull();
@@ -1072,7 +1088,9 @@
         processRecord.mState.setCurAdj(100);
 
         // We force a full compaction
-        mCachedAppOptimizerUnderTest.compactAppFull(processRecord, true);
+        mCachedAppOptimizerUnderTest.compactApp(processRecord,
+                CachedAppOptimizer.CompactProfile.FULL, CachedAppOptimizer.CompactSource.APP,
+                true);
         waitForHandler();
         // then process is compacted.
         assertThat(mCachedAppOptimizerUnderTest.mLastCompactionStats.get(pid)).isNotNull();
@@ -1080,13 +1098,13 @@
         mCachedAppOptimizerUnderTest.mLastCompactionStats.clear();
 
         // We force a some compaction
-        mCachedAppOptimizerUnderTest.compactAppSome(processRecord, true);
+        mCachedAppOptimizerUnderTest.compactApp(processRecord,
+                CachedAppOptimizer.CompactProfile.SOME, CachedAppOptimizer.CompactSource.APP, true);
         waitForHandler();
         // then process is compacted.
-        String executedCompactAction =
-                compactActionIntToString(processRecord.mOptRecord.getLastCompactAction());
-        assertThat(executedCompactAction)
-                .isEqualTo(mCachedAppOptimizerUnderTest.mCompactActionSome);
+        CachedAppOptimizer.CompactProfile executedCompactProfile =
+                processRecord.mOptRecord.getLastCompactProfile();
+        assertThat(executedCompactProfile).isEqualTo(CachedAppOptimizer.CompactProfile.SOME);
     }
 
     private void setFlag(String key, String value, boolean defaultValue) throws Exception {
@@ -1162,7 +1180,8 @@
         }
 
         @Override
-        public void performCompaction(String action, int pid) throws IOException {
+        public void performCompaction(CachedAppOptimizer.CompactAction action, int pid)
+                throws IOException {
             mRss = mRssAfterCompaction;
         }
 
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
index 8d59dce..598c6b0 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
@@ -40,6 +40,7 @@
 
 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
 
+import static com.android.server.am.OomAdjuster.OOM_ADJ_REASON_ACTIVITY;
 import static com.android.server.am.ProcessList.BACKUP_APP_ADJ;
 import static com.android.server.am.ProcessList.CACHED_APP_MAX_ADJ;
 import static com.android.server.am.ProcessList.CACHED_APP_MIN_ADJ;
@@ -146,6 +147,7 @@
     private static PackageManagerInternal sPackageManagerInternal;
     private static ActivityManagerService sService;
 
+    @SuppressWarnings("GuardedBy")
     @BeforeClass
     public static void setUpOnce() {
         sContext = getInstrumentation().getTargetContext();
@@ -196,7 +198,7 @@
                 new ActivityManagerService.Injector(sContext));
         doReturn(mock(AppOpsManager.class)).when(sService).getAppOpsManager();
         doCallRealMethod().when(sService).enqueueOomAdjTargetLocked(any(ProcessRecord.class));
-        doCallRealMethod().when(sService).updateOomAdjPendingTargetsLocked(any(String.class));
+        doCallRealMethod().when(sService).updateOomAdjPendingTargetsLocked(OOM_ADJ_REASON_ACTIVITY);
         setFieldValue(AppProfiler.class, profiler, "mProfilerLock", new Object());
         doReturn(new ActivityManagerService.ProcessChangeItem()).when(pr)
                 .enqueueProcessChangeItemLocked(anyInt(), anyInt());
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 d675b0a..b53a2c6 100644
--- a/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
@@ -201,7 +201,6 @@
     public void tearDown() throws Exception {
         LocalServices.removeServiceForTest(PowerManagerInternal.class);
         GameManagerService gameManagerService = new GameManagerService(mMockContext);
-        gameManagerService.disableCompatScale(mPackageName);
         if (mMockingSession != null) {
             mMockingSession.finishMocking();
         }
@@ -450,13 +449,13 @@
 
 
         startUser(gameManagerService, USER_ID_1);
-        gameManagerService.updateConfigsForUser(USER_ID_1, mPackageName);
+        gameManagerService.updateConfigsForUser(USER_ID_1, true, mPackageName);
         mockModifyGameModeGranted();
         assertEquals(GameManager.GAME_MODE_UNSUPPORTED,
                 gameManagerService.getGameMode(mPackageName, USER_ID_1));
         // We need to make sure the mode is supported before setting it.
         mockDeviceConfigAll();
-        gameManagerService.updateConfigsForUser(USER_ID_1, mPackageName);
+        gameManagerService.updateConfigsForUser(USER_ID_1, true, mPackageName);
         gameManagerService.setGameMode(mPackageName, GameManager.GAME_MODE_STANDARD, USER_ID_1);
         assertEquals(GameManager.GAME_MODE_STANDARD,
                 gameManagerService.getGameMode(mPackageName, USER_ID_1));
@@ -534,8 +533,8 @@
 
         startUser(gameManagerService, USER_ID_1);
         startUser(gameManagerService, USER_ID_2);
-        gameManagerService.updateConfigsForUser(USER_ID_1, mPackageName);
-        gameManagerService.updateConfigsForUser(USER_ID_2, mPackageName);
+        gameManagerService.updateConfigsForUser(USER_ID_1, true, mPackageName);
+        gameManagerService.updateConfigsForUser(USER_ID_2, true, mPackageName);
 
         // Set User 1 to Standard
         gameManagerService.setGameMode(mPackageName, GameManager.GAME_MODE_STANDARD, USER_ID_1);
@@ -563,7 +562,7 @@
         if (gameManagerService == null) {
             gameManagerService = new GameManagerService(mMockContext, mTestLooper.getLooper());
             startUser(gameManagerService, USER_ID_1);
-            gameManagerService.updateConfigsForUser(USER_ID_1, mPackageName);
+            gameManagerService.updateConfigsForUser(USER_ID_1, true, mPackageName);
         }
         ArraySet<Integer> reportedModes = new ArraySet<>();
         int[] modes = gameManagerService.getAvailableGameModes(mPackageName);
@@ -578,20 +577,20 @@
     }
 
     private void checkDownscaling(GameManagerService gameManagerService,
-                int gameMode, String scaling) {
+                int gameMode, float scaling) {
         if (gameManagerService == null) {
             gameManagerService = new GameManagerService(mMockContext, mTestLooper.getLooper());
             startUser(gameManagerService, USER_ID_1);
-            gameManagerService.updateConfigsForUser(USER_ID_1, mPackageName);
+            gameManagerService.updateConfigsForUser(USER_ID_1, true, mPackageName);
         }
         GameManagerService.GamePackageConfiguration config =
                 gameManagerService.getConfig(mPackageName);
-        assertEquals(config.getGameModeConfiguration(gameMode).getScaling(), scaling);
+        assertEquals(scaling, config.getGameModeConfiguration(gameMode).getScaling(), 0.01f);
     }
 
     private void checkAngleEnabled(GameManagerService gameManagerService, int gameMode,
             boolean angleEnabled) {
-        gameManagerService.updateConfigsForUser(USER_ID_1, mPackageName);
+        gameManagerService.updateConfigsForUser(USER_ID_1, true, mPackageName);
 
         // Validate GamePackageConfiguration returns the correct value.
         GameManagerService.GamePackageConfiguration config =
@@ -604,7 +603,7 @@
 
     private void checkLoadingBoost(GameManagerService gameManagerService, int gameMode,
             int loadingBoost) {
-        gameManagerService.updateConfigsForUser(USER_ID_1, mPackageName);
+        gameManagerService.updateConfigsForUser(USER_ID_1, true, mPackageName);
 
         // Validate GamePackageConfiguration returns the correct value.
         GameManagerService.GamePackageConfiguration config =
@@ -621,7 +620,7 @@
         if (gameManagerService == null) {
             gameManagerService = new GameManagerService(mMockContext, mTestLooper.getLooper());
             startUser(gameManagerService, USER_ID_1);
-            gameManagerService.updateConfigsForUser(USER_ID_1, mPackageName);
+            gameManagerService.updateConfigsForUser(USER_ID_1, true, mPackageName);
         }
         GameManagerService.GamePackageConfiguration config =
                 gameManagerService.getConfig(mPackageName);
@@ -715,7 +714,7 @@
 
         checkReportedModes(gameManagerService, GameManager.GAME_MODE_PERFORMANCE,
                 GameManager.GAME_MODE_STANDARD);
-        checkDownscaling(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, "0.3");
+        checkDownscaling(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, 0.3f);
         checkFps(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, 120);
     }
 
@@ -735,7 +734,7 @@
 
         checkReportedModes(gameManagerService, GameManager.GAME_MODE_BATTERY,
                 GameManager.GAME_MODE_STANDARD);
-        checkDownscaling(gameManagerService, GameManager.GAME_MODE_BATTERY, "0.5");
+        checkDownscaling(gameManagerService, GameManager.GAME_MODE_BATTERY, 0.5f);
         checkFps(gameManagerService, GameManager.GAME_MODE_BATTERY, 60);
     }
 
@@ -757,9 +756,9 @@
 
         checkReportedModes(gameManagerService, GameManager.GAME_MODE_PERFORMANCE,
                 GameManager.GAME_MODE_BATTERY, GameManager.GAME_MODE_STANDARD);
-        checkDownscaling(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, "0.3");
+        checkDownscaling(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, 0.3f);
         checkFps(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, 120);
-        checkDownscaling(gameManagerService, GameManager.GAME_MODE_BATTERY, "0.5");
+        checkDownscaling(gameManagerService, GameManager.GAME_MODE_BATTERY, 0.5f);
         checkFps(gameManagerService, GameManager.GAME_MODE_BATTERY, 60);
     }
 
@@ -782,7 +781,7 @@
 
         checkReportedModes(gameManagerService, GameManager.GAME_MODE_PERFORMANCE,
                 GameManager.GAME_MODE_STANDARD);
-        checkDownscaling(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, "0.5");
+        checkDownscaling(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, 0.5f);
         checkFps(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, 90);
     }
 
@@ -805,7 +804,7 @@
 
         checkReportedModes(gameManagerService, GameManager.GAME_MODE_BATTERY,
                 GameManager.GAME_MODE_STANDARD);
-        checkDownscaling(gameManagerService, GameManager.GAME_MODE_BATTERY, "0.7");
+        checkDownscaling(gameManagerService, GameManager.GAME_MODE_BATTERY, 0.7f);
         checkFps(gameManagerService, GameManager.GAME_MODE_BATTERY, 30);
     }
 
@@ -829,9 +828,9 @@
 
         checkReportedModes(gameManagerService, GameManager.GAME_MODE_PERFORMANCE,
                 GameManager.GAME_MODE_BATTERY, GameManager.GAME_MODE_STANDARD);
-        checkDownscaling(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, "0.5");
+        checkDownscaling(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, 0.5f);
         checkFps(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, 90);
-        checkDownscaling(gameManagerService, GameManager.GAME_MODE_BATTERY, "0.7");
+        checkDownscaling(gameManagerService, GameManager.GAME_MODE_BATTERY, 0.7f);
         checkFps(gameManagerService, GameManager.GAME_MODE_BATTERY, 30);
     }
 
@@ -858,9 +857,9 @@
 
         checkReportedModes(gameManagerService, GameManager.GAME_MODE_PERFORMANCE,
                 GameManager.GAME_MODE_BATTERY, GameManager.GAME_MODE_STANDARD);
-        checkDownscaling(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, "0.3");
+        checkDownscaling(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, 0.3f);
         checkFps(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, 120);
-        checkDownscaling(gameManagerService, GameManager.GAME_MODE_BATTERY, "0.7");
+        checkDownscaling(gameManagerService, GameManager.GAME_MODE_BATTERY, 0.7f);
         checkFps(gameManagerService, GameManager.GAME_MODE_BATTERY, 30);
     }
 
@@ -933,7 +932,7 @@
     public void testInterventionAllowScalingDefault() throws Exception {
         mockDeviceConfigPerformance();
         mockModifyGameModeGranted();
-        checkDownscaling(null, GameManager.GAME_MODE_PERFORMANCE, "0.5");
+        checkDownscaling(null, GameManager.GAME_MODE_PERFORMANCE, 0.5f);
     }
 
     /**
@@ -944,7 +943,7 @@
         mockDeviceConfigPerformance();
         mockInterventionAllowDownscaleFalse();
         mockModifyGameModeGranted();
-        checkDownscaling(null, GameManager.GAME_MODE_PERFORMANCE, "1.0");
+        checkDownscaling(null, GameManager.GAME_MODE_PERFORMANCE, -1.0f);
     }
 
     /**
@@ -956,7 +955,7 @@
         mockDeviceConfigPerformance();
         mockInterventionAllowDownscaleTrue();
         mockModifyGameModeGranted();
-        checkDownscaling(null, GameManager.GAME_MODE_PERFORMANCE, "0.5");
+        checkDownscaling(null, GameManager.GAME_MODE_PERFORMANCE, 0.5f);
     }
 
     /**
@@ -1091,7 +1090,7 @@
         GameManagerService gameManagerService =
                 new GameManagerService(mMockContext, mTestLooper.getLooper());
         startUser(gameManagerService, USER_ID_1);
-        gameManagerService.updateConfigsForUser(USER_ID_1, mPackageName);
+        gameManagerService.updateConfigsForUser(USER_ID_1, true, mPackageName);
         GameManagerService.GamePackageConfiguration config =
                 gameManagerService.getConfig(mPackageName);
         assertNull(config.getGameModeConfiguration(GameManager.GAME_MODE_PERFORMANCE));
@@ -1109,7 +1108,7 @@
                 new GameManagerService(mMockContext, mTestLooper.getLooper());
         startUser(gameManagerService, USER_ID_1);
         gameManagerService.setGameMode(mPackageName, GameManager.GAME_MODE_PERFORMANCE, USER_ID_1);
-        gameManagerService.updateConfigsForUser(USER_ID_1, mPackageName);
+        gameManagerService.updateConfigsForUser(USER_ID_1, true, mPackageName);
         assertEquals(GameManager.GAME_MODE_UNSUPPORTED,
                 gameManagerService.getGameMode(mPackageName, USER_ID_1));
     }
@@ -1126,7 +1125,7 @@
                 new GameManagerService(mMockContext, mTestLooper.getLooper());
         startUser(gameManagerService, USER_ID_1);
         gameManagerService.setGameMode(mPackageName, GameManager.GAME_MODE_BATTERY, USER_ID_1);
-        gameManagerService.updateConfigsForUser(USER_ID_1, mPackageName);
+        gameManagerService.updateConfigsForUser(USER_ID_1, true, mPackageName);
         assertEquals(GameManager.GAME_MODE_STANDARD,
                 gameManagerService.getGameMode(mPackageName, USER_ID_1));
     }
@@ -1143,7 +1142,7 @@
                 new GameManagerService(mMockContext, mTestLooper.getLooper());
         startUser(gameManagerService, USER_ID_1);
         gameManagerService.setGameMode(mPackageName, GameManager.GAME_MODE_UNSUPPORTED, USER_ID_1);
-        gameManagerService.updateConfigsForUser(USER_ID_1, mPackageName);
+        gameManagerService.updateConfigsForUser(USER_ID_1, true, mPackageName);
         assertEquals(GameManager.GAME_MODE_STANDARD,
                 gameManagerService.getGameMode(mPackageName, USER_ID_1));
     }
@@ -1404,4 +1403,102 @@
         assertEquals(splitLine[6], "angle=0,scaling=0.7,fps=30");
 
     }
+
+    @Test
+    public void testUpdateResolutionScalingFactor() {
+        mockModifyGameModeGranted();
+        mockDeviceConfigBattery();
+        GameManagerService gameManagerService =
+                new GameManagerService(mMockContext, mTestLooper.getLooper());
+        startUser(gameManagerService, USER_ID_1);
+        float scalingFactor = 0.123f;
+        gameManagerService.updateResolutionScalingFactor(mPackageName,
+                GameManager.GAME_MODE_BATTERY, scalingFactor,
+                USER_ID_1);
+        assertEquals(scalingFactor, gameManagerService.getResolutionScalingFactor(mPackageName,
+                GameManager.GAME_MODE_BATTERY, USER_ID_1), 0.001f);
+        scalingFactor = 0.321f;
+        gameManagerService.updateResolutionScalingFactor(mPackageName,
+                GameManager.GAME_MODE_BATTERY, scalingFactor,
+                USER_ID_1);
+        assertEquals(scalingFactor, gameManagerService.getResolutionScalingFactor(mPackageName,
+                GameManager.GAME_MODE_BATTERY, USER_ID_1), 0.001f);
+    }
+
+    @Test
+    public void testUpdateResolutionScalingFactor_noDeviceConfig() {
+        mockModifyGameModeGranted();
+        GameManagerService gameManagerService =
+                new GameManagerService(mMockContext, mTestLooper.getLooper());
+        startUser(gameManagerService, USER_ID_1);
+        float scalingFactor = 0.123f;
+        gameManagerService.updateResolutionScalingFactor(mPackageName,
+                GameManager.GAME_MODE_BATTERY, scalingFactor,
+                USER_ID_1);
+        assertEquals(scalingFactor, gameManagerService.getResolutionScalingFactor(mPackageName,
+                GameManager.GAME_MODE_BATTERY, USER_ID_1), 0.001f);
+        scalingFactor = 0.321f;
+        gameManagerService.updateResolutionScalingFactor(mPackageName,
+                GameManager.GAME_MODE_BATTERY, scalingFactor,
+                USER_ID_1);
+        assertEquals(scalingFactor, gameManagerService.getResolutionScalingFactor(mPackageName,
+                GameManager.GAME_MODE_BATTERY,
+                USER_ID_1), 0.001f);
+    }
+
+    @Test
+    public void testUpdateResolutionScalingFactor_permissionDenied() {
+        mockModifyGameModeDenied();
+        mockDeviceConfigAll();
+        GameManagerService gameManagerService =
+                new GameManagerService(mMockContext, mTestLooper.getLooper());
+        startUser(gameManagerService, USER_ID_1);
+        float scalingFactor = 0.123f;
+        assertThrows(SecurityException.class, () -> {
+            gameManagerService.updateResolutionScalingFactor(mPackageName,
+                    GameManager.GAME_MODE_BATTERY, scalingFactor,
+                    USER_ID_1);
+        });
+        mockModifyGameModeGranted();
+        assertEquals(0.7f, gameManagerService.getResolutionScalingFactor(mPackageName,
+                GameManager.GAME_MODE_BATTERY, USER_ID_1), 0.001f);
+    }
+
+    @Test
+    public void testUpdateResolutionScalingFactor_noUserId() {
+        mockModifyGameModeGranted();
+        GameManagerService gameManagerService =
+                new GameManagerService(mMockContext, mTestLooper.getLooper());
+        startUser(gameManagerService, USER_ID_2);
+        final float scalingFactor = 0.123f;
+        assertThrows(IllegalArgumentException.class, () -> {
+            gameManagerService.updateResolutionScalingFactor(mPackageName,
+                    GameManager.GAME_MODE_BATTERY, scalingFactor,
+                    USER_ID_1);
+        });
+    }
+
+    @Test
+    public void testGetResolutionScalingFactor_permissionDenied() {
+        mockModifyGameModeDenied();
+        mockDeviceConfigAll();
+        GameManagerService gameManagerService =
+                new GameManagerService(mMockContext, mTestLooper.getLooper());
+        startUser(gameManagerService, USER_ID_1);
+        assertThrows(SecurityException.class, () -> {
+            gameManagerService.getResolutionScalingFactor(mPackageName,
+                    GameManager.GAME_MODE_BATTERY, USER_ID_1);
+        });
+    }
+
+    @Test
+    public void testGetResolutionScalingFactor_noUserId() {
+        mockModifyGameModeDenied();
+        mockDeviceConfigAll();
+        GameManagerService gameManagerService =
+                new GameManagerService(mMockContext, mTestLooper.getLooper());
+        startUser(gameManagerService, USER_ID_2);
+        assertEquals(-1f, gameManagerService.getResolutionScalingFactor(mPackageName,
+                GameManager.GAME_MODE_BATTERY, USER_ID_1), 0.001f);
+    }
 }
diff --git a/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java b/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java
index a8b340c..e4f9eaf 100644
--- a/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java
@@ -218,8 +218,8 @@
         }).when(mMockWindowManagerInternal).unregisterTaskSystemBarsListener(any());
 
         mRunningTaskInfos = new ArrayList<>();
-        when(mMockActivityTaskManager.getTasks(anyInt(), anyBoolean(), anyBoolean())).thenReturn(
-                mRunningTaskInfos);
+        when(mMockActivityTaskManager.getTasks(anyInt(), anyBoolean(), anyBoolean(), anyInt()))
+                .thenReturn(mRunningTaskInfos);
 
 
         final UserHandle userHandle = new UserHandle(USER_ID);
diff --git a/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUpgradeTest.java b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUpgradeTest.java
index 99693d2..7111047 100644
--- a/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUpgradeTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUpgradeTest.java
@@ -35,6 +35,7 @@
 import android.os.HandlerThread;
 import android.util.Log;
 import android.util.SparseArray;
+import android.util.SparseIntArray;
 import android.util.TypedXmlPullParser;
 import android.util.Xml;
 
@@ -97,9 +98,10 @@
         final int defaultModeOp2 = AppOpsManager.opToDefaultMode(op2);
         for(int i = 0; i < uidStates.size(); i++) {
             final AppOpsService.UidState uidState = uidStates.valueAt(i);
-            if (uidState.opModes != null) {
-                final int uidMode1 = uidState.opModes.get(op1, defaultModeOp1);
-                final int uidMode2 = uidState.opModes.get(op2, defaultModeOp2);
+            SparseIntArray opModes = uidState.getNonDefaultUidModes();
+            if (opModes != null) {
+                final int uidMode1 = opModes.get(op1, defaultModeOp1);
+                final int uidMode2 = opModes.get(op2, defaultModeOp2);
                 assertEquals(uidMode1, uidMode2);
                 if (uidMode1 != defaultModeOp1) {
                     numberOfNonDefaultOps++;
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java
new file mode 100644
index 0000000..6a18dc1
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+
+
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isA;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.res.Resources;
+import android.hardware.Sensor;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.hardware.display.DisplayManagerInternal.DisplayPowerCallbacks;
+import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
+import android.os.Handler;
+import android.os.PowerManager;
+import android.os.test.TestLooper;
+import android.util.FloatProperty;
+import android.view.Display;
+import android.view.DisplayInfo;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.LocalServices;
+import com.android.server.display.RampAnimator.DualRampAnimator;
+import com.android.server.policy.WindowManagerPolicy;
+import com.android.server.testutils.OffsettableClock;
+
+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;
+
+import java.util.List;
+
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public final class DisplayPowerControllerTest {
+    private static final String UNIQUE_DISPLAY_ID = "unique_id_test123";
+    private static final int DISPLAY_ID = 42;
+
+    private OffsettableClock mClock;
+    private TestLooper mTestLooper;
+    private Handler mHandler;
+    private DisplayPowerController.Injector mInjector;
+    private Context mContextSpy;
+
+    @Mock
+    private DisplayPowerCallbacks mDisplayPowerCallbacksMock;
+    @Mock
+    private SensorManager mSensorManagerMock;
+    @Mock
+    private DisplayBlanker mDisplayBlankerMock;
+    @Mock
+    private LogicalDisplay mLogicalDisplayMock;
+    @Mock
+    private DisplayDevice mDisplayDeviceMock;
+    @Mock
+    private BrightnessTracker mBrightnessTrackerMock;
+    @Mock
+    private BrightnessSetting mBrightnessSettingMock;
+    @Mock
+    private WindowManagerPolicy mWindowManagerPolicyMock;
+    @Mock
+    private PowerManager mPowerManagerMock;
+    @Mock
+    private Resources mResourcesMock;
+    @Mock
+    private DisplayDeviceConfig mDisplayDeviceConfigMock;
+    @Mock
+    private DisplayPowerState mDisplayPowerStateMock;
+    @Mock
+    private DualRampAnimator<DisplayPowerState> mDualRampAnimatorMock;
+
+    @Captor
+    private ArgumentCaptor<SensorEventListener> mSensorEventListenerCaptor;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        mContextSpy = spy(new ContextWrapper(ApplicationProvider.getApplicationContext()));
+        mClock = new OffsettableClock.Stopped();
+        mTestLooper = new TestLooper(mClock::now);
+        mHandler = new Handler(mTestLooper.getLooper());
+        mInjector = new DisplayPowerController.Injector() {
+            @Override
+            DisplayPowerController.Clock getClock() {
+                return mClock::now;
+            }
+
+            @Override
+            DisplayPowerState getDisplayPowerState(DisplayBlanker blanker, ColorFade colorFade,
+                    int displayId, int displayState) {
+                return mDisplayPowerStateMock;
+            }
+
+            @Override
+            DualRampAnimator<DisplayPowerState> getDualRampAnimator(DisplayPowerState dps,
+                    FloatProperty<DisplayPowerState> firstProperty,
+                    FloatProperty<DisplayPowerState> secondProperty) {
+                return mDualRampAnimatorMock;
+            }
+        };
+
+        addLocalServiceMock(WindowManagerPolicy.class, mWindowManagerPolicyMock);
+
+        when(mContextSpy.getSystemService(eq(PowerManager.class))).thenReturn(mPowerManagerMock);
+        when(mContextSpy.getResources()).thenReturn(mResourcesMock);
+    }
+
+    @After
+    public void tearDown() {
+        LocalServices.removeServiceForTest(WindowManagerPolicy.class);
+    }
+
+    @Test
+    public void testReleaseProxSuspendBlockersOnExit() throws Exception {
+        setUpDisplay(DISPLAY_ID, UNIQUE_DISPLAY_ID);
+
+        Sensor proxSensor = setUpProxSensor();
+
+        DisplayPowerController dpc = new DisplayPowerController(
+                mContextSpy, mInjector, mDisplayPowerCallbacksMock, mHandler,
+                mSensorManagerMock, mDisplayBlankerMock, mLogicalDisplayMock,
+                mBrightnessTrackerMock, mBrightnessSettingMock, () -> {
+        });
+
+        when(mDisplayPowerStateMock.getScreenState()).thenReturn(Display.STATE_ON);
+        // send a display power request
+        DisplayPowerRequest dpr = new DisplayPowerRequest();
+        dpr.policy = DisplayPowerRequest.POLICY_BRIGHT;
+        dpr.useProximitySensor = true;
+        dpc.requestPowerState(dpr, false /* waitForNegativeProximity */);
+
+        // Run updatePowerState to start listener for the prox sensor
+        advanceTime(1);
+
+        SensorEventListener listener = getSensorEventListener(proxSensor);
+        assertNotNull(listener);
+
+        listener.onSensorChanged(TestUtils.createSensorEvent(proxSensor, 5 /* lux */));
+        advanceTime(1);
+
+        // two times, one for unfinished business and one for proximity
+        verify(mDisplayPowerCallbacksMock).acquireSuspendBlocker(
+                dpc.getSuspendBlockerUnfinishedBusinessId(DISPLAY_ID));
+        verify(mDisplayPowerCallbacksMock).acquireSuspendBlocker(
+                dpc.getSuspendBlockerProxDebounceId(DISPLAY_ID));
+
+        dpc.stop();
+        advanceTime(1);
+
+        // two times, one for unfinished business and one for proximity
+        verify(mDisplayPowerCallbacksMock).releaseSuspendBlocker(
+                dpc.getSuspendBlockerUnfinishedBusinessId(DISPLAY_ID));
+        verify(mDisplayPowerCallbacksMock).releaseSuspendBlocker(
+                dpc.getSuspendBlockerProxDebounceId(DISPLAY_ID));
+    }
+
+    /**
+     * Creates a mock and registers it to {@link LocalServices}.
+     */
+    private static <T> void addLocalServiceMock(Class<T> clazz, T mock) {
+        LocalServices.removeServiceForTest(clazz);
+        LocalServices.addService(clazz, mock);
+    }
+
+    private void advanceTime(long timeMs) {
+        mClock.fastForward(timeMs);
+        mTestLooper.dispatchAll();
+    }
+
+    private Sensor setUpProxSensor() throws Exception {
+        Sensor proxSensor = TestUtils.createSensor(
+                Sensor.TYPE_PROXIMITY, Sensor.STRING_TYPE_PROXIMITY);
+        when(mSensorManagerMock.getSensorList(eq(Sensor.TYPE_ALL)))
+                .thenReturn(List.of(proxSensor));
+        return proxSensor;
+    }
+
+    private SensorEventListener getSensorEventListener(Sensor sensor) {
+        verify(mSensorManagerMock).registerListener(mSensorEventListenerCaptor.capture(),
+                eq(sensor), eq(SensorManager.SENSOR_DELAY_NORMAL), isA(Handler.class));
+        return mSensorEventListenerCaptor.getValue();
+    }
+
+    private void setUpDisplay(int displayId, String uniqueId) {
+        DisplayInfo info = new DisplayInfo();
+        DisplayDeviceInfo deviceInfo = new DisplayDeviceInfo();
+
+        when(mLogicalDisplayMock.getDisplayIdLocked()).thenReturn(displayId);
+        when(mLogicalDisplayMock.getPrimaryDisplayDeviceLocked()).thenReturn(mDisplayDeviceMock);
+        when(mLogicalDisplayMock.getDisplayInfoLocked()).thenReturn(info);
+        when(mLogicalDisplayMock.isEnabled()).thenReturn(true);
+        when(mLogicalDisplayMock.getPhase()).thenReturn(LogicalDisplay.DISPLAY_PHASE_ENABLED);
+        when(mDisplayDeviceMock.getDisplayDeviceInfoLocked()).thenReturn(deviceInfo);
+        when(mDisplayDeviceMock.getUniqueId()).thenReturn(uniqueId);
+        when(mDisplayDeviceMock.getDisplayDeviceConfig()).thenReturn(mDisplayDeviceConfigMock);
+        when(mDisplayDeviceConfigMock.getProximitySensor()).thenReturn(
+                new DisplayDeviceConfig.SensorData() {
+                    {
+                        type = Sensor.STRING_TYPE_PROXIMITY;
+                        name = null;
+                    }
+                });
+        when(mDisplayDeviceConfigMock.getNits()).thenReturn(new float[]{2, 500});
+    }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/InitAppsHelperTest.kt b/services/tests/mockingservicestests/src/com/android/server/pm/InitAppsHelperTest.kt
new file mode 100644
index 0000000..2165301
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/InitAppsHelperTest.kt
@@ -0,0 +1,135 @@
+/*
+ * 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.os.Build
+import android.os.SystemProperties
+import com.android.server.extendedtestutils.wheneverStatic
+import com.android.server.testutils.spy
+import com.android.server.testutils.whenever
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+class InitAppsHelperTest {
+    @Rule
+    @JvmField
+    val rule = MockSystemRule()
+
+    @Before
+    fun setUp() {
+        rule.system().stageNominalSystemState()
+    }
+
+    private fun mockNoFirstBoot() {
+        // To mock that this is not the first boot
+        rule.system().stageScanExistingPackage("a.data.package", 1L,
+            rule.system().dataAppDirectory)
+    }
+
+    private fun mockFingerprintChanged() {
+        val versionInfo = Settings.VersionInfo()
+        versionInfo.fingerprint = "mockFingerprintForTesting"
+        whenever(rule.mocks().settings.internalVersion) {
+            versionInfo
+        }
+    }
+
+    private fun mockFingerprintUnchanged() {
+        // To mock that this is not the first boot
+        val versionInfo = Settings.VersionInfo()
+        versionInfo.fingerprint = MockSystem.DEFAULT_VERSION_INFO.fingerprint
+        whenever(rule.mocks().settings.internalVersion) {
+            versionInfo
+        }
+    }
+
+    private fun mockOta() {
+        wheneverStatic {
+            SystemProperties.getBoolean("persist.pm.mock-upgrade", false)
+        }.thenReturn(true)
+    }
+
+    private fun createPackageManagerService(): PackageManagerService {
+        return spy(PackageManagerService(rule.mocks().injector,
+            false /*factoryTest*/,
+            MockSystem.DEFAULT_VERSION_INFO.fingerprint,
+            false /*isEngBuild*/,
+            false /*isUserDebugBuild*/,
+            Build.VERSION_CODES.CUR_DEVELOPMENT,
+            Build.VERSION.INCREMENTAL))
+    }
+
+    @Test
+    fun testSystemScanFlagOnFirstBoot() {
+        val pms = createPackageManagerService()
+        assertThat(pms.isFirstBoot).isEqualTo(true)
+        assertThat(pms.isDeviceUpgrading).isEqualTo(false)
+        val initAppsHelper = InitAppsHelper(pms, rule.mocks().apexManager, null, null,
+            listOf<ScanPartition>())
+        assertThat(
+            initAppsHelper.systemScanFlags and PackageManagerService.SCAN_FIRST_BOOT_OR_UPGRADE)
+            .isEqualTo(PackageManagerService.SCAN_FIRST_BOOT_OR_UPGRADE)
+    }
+
+    @Test
+    fun testSystemScanFlagWithMockOTA() {
+        mockNoFirstBoot()
+        mockFingerprintUnchanged()
+        mockOta()
+        val pms = createPackageManagerService()
+        assertThat(pms.isFirstBoot).isEqualTo(false)
+        assertThat(pms.isDeviceUpgrading).isEqualTo(true)
+        val initAppsHelper = InitAppsHelper(pms, rule.mocks().apexManager, null, null,
+            listOf<ScanPartition>())
+        assertThat(
+            initAppsHelper.systemScanFlags and PackageManagerService.SCAN_FIRST_BOOT_OR_UPGRADE)
+            .isEqualTo(PackageManagerService.SCAN_FIRST_BOOT_OR_UPGRADE)
+    }
+
+    @Test
+    fun testSystemScanFlagNoOTA() {
+        mockNoFirstBoot()
+        mockFingerprintUnchanged()
+        val pms = createPackageManagerService()
+        assertThat(pms.isFirstBoot).isEqualTo(false)
+        assertThat(pms.isDeviceUpgrading).isEqualTo(false)
+        val initAppsHelper = InitAppsHelper(pms, rule.mocks().apexManager, null, null,
+            listOf<ScanPartition>())
+        assertThat(
+            initAppsHelper.systemScanFlags and PackageManagerService.SCAN_FIRST_BOOT_OR_UPGRADE)
+            .isEqualTo(0)
+    }
+
+    @Test
+    fun testSystemScanFlagWithFingerprintChanged() {
+        mockNoFirstBoot()
+        mockFingerprintChanged()
+        val pms = createPackageManagerService()
+        assertThat(pms.isFirstBoot).isEqualTo(false)
+        assertThat(pms.isDeviceUpgrading).isEqualTo(true)
+        val initAppsHelper = InitAppsHelper(pms, rule.mocks().apexManager, null, null,
+            listOf<ScanPartition>())
+        assertThat(
+            initAppsHelper.systemScanFlags and PackageManagerService.SCAN_FIRST_BOOT_OR_UPGRADE)
+            .isEqualTo(PackageManagerService.SCAN_FIRST_BOOT_OR_UPGRADE)
+    }
+}
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index a9ee84f..1921ce7 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -135,8 +135,9 @@
 java_library {
     name: "servicestests-core-utils",
     srcs: [
-        "src/com/android/server/pm/PackageSettingBuilder.java",
         "src/com/android/server/am/DeviceConfigSession.java",
+        "src/com/android/server/display/TestUtils.java",
+        "src/com/android/server/pm/PackageSettingBuilder.java",
     ],
     static_libs: [
         "services.core",
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 c80547c..966df4f 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
@@ -124,6 +124,8 @@
     private VirtualDeviceImpl mDeviceImpl;
     private InputController mInputController;
     private AssociationInfo mAssociationInfo;
+    private VirtualDeviceManagerService mVdms;
+    private VirtualDeviceManagerInternal mLocalService;
     @Mock
     private InputController.NativeWrapper mNativeWrapperMock;
     @Mock
@@ -139,6 +141,8 @@
     @Mock
     private Consumer<ArraySet<Integer>> mRunningAppsChangedCallback;
     @Mock
+    private VirtualDeviceManagerInternal.VirtualDisplayListener mDisplayListener;
+    @Mock
     IPowerManager mIPowerManagerMock;
     @Mock
     IThermalService mIThermalServiceMock;
@@ -215,6 +219,9 @@
         mAssociationInfo = new AssociationInfo(1, 0, null,
                 MacAddress.BROADCAST_ADDRESS, "", null, true, false, false, 0, 0);
 
+        mVdms = new VirtualDeviceManagerService(mContext);
+        mLocalService = mVdms.getLocalServiceInstance();
+
         VirtualDeviceParams params = new VirtualDeviceParams
                 .Builder()
                 .setBlockedActivities(getBlockedActivities())
@@ -235,6 +242,28 @@
     }
 
     @Test
+    public void onVirtualDisplayCreatedLocked_listenersNotified() throws RemoteException {
+        mLocalService.registerVirtualDisplayListener(mDisplayListener);
+
+        mLocalService.onVirtualDisplayCreated(DISPLAY_ID);
+        TestableLooper.get(this).processAllMessages();
+
+        verify(mDisplayListener).onVirtualDisplayCreated(DISPLAY_ID);
+    }
+
+    @Test
+    public void onVirtualDisplayRemovedLocked_listenersNotified() throws RemoteException {
+        mLocalService.registerVirtualDisplayListener(mDisplayListener);
+        mDeviceImpl.onVirtualDisplayCreatedLocked(
+                mDeviceImpl.createWindowPolicyController(), DISPLAY_ID);
+
+        mLocalService.onVirtualDisplayRemoved(mDeviceImpl, DISPLAY_ID);
+        TestableLooper.get(this).processAllMessages();
+
+        verify(mDisplayListener).onVirtualDisplayRemoved(DISPLAY_ID);
+    }
+
+    @Test
     public void onVirtualDisplayCreatedLocked_wakeLockIsAcquired() throws RemoteException {
         verify(mIPowerManagerMock, never()).acquireWakeLock(any(Binder.class), anyInt(),
                 nullable(String.class), nullable(String.class), nullable(WorkSource.class),
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/FakeGateKeeperService.java b/services/tests/servicestests/src/com/android/server/locksettings/FakeGateKeeperService.java
index 094b7af..b044d7a 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/FakeGateKeeperService.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/FakeGateKeeperService.java
@@ -42,7 +42,7 @@
             ByteBuffer buffer = ByteBuffer.allocate(handle.length);
             buffer.put(handle, 0, handle.length);
             buffer.flip();
-            int version = buffer.get();
+            buffer.get(); // version
             sid = buffer.getLong();
             password = new byte[buffer.remaining()];
             buffer.get(password);
@@ -50,7 +50,7 @@
 
         public byte[] toBytes() {
             ByteBuffer buffer = ByteBuffer.allocate(1 + Long.BYTES + password.length);
-            buffer.put((byte)0);
+            buffer.put((byte)0); // version
             buffer.putLong(sid);
             buffer.put(password);
             return buffer.array();
@@ -70,14 +70,14 @@
             ByteBuffer buffer = ByteBuffer.allocate(handle.length);
             buffer.put(handle, 0, handle.length);
             buffer.flip();
-            int version = buffer.get();
+            buffer.get(); // version
             challenge = buffer.getLong();
             sid = buffer.getLong();
         }
 
         public byte[] toBytes() {
             ByteBuffer buffer = ByteBuffer.allocate(1 + Long.BYTES + Long.BYTES);
-            buffer.put((byte)0);
+            buffer.put((byte)0); // version
             buffer.putLong(challenge);
             buffer.putLong(sid);
             return buffer.array();
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStrongAuthTest.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStrongAuthTest.java
index 6de7fdd..ec708ad 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStrongAuthTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStrongAuthTest.java
@@ -250,8 +250,8 @@
         // schedule (a) an alarm for non-strong biometric fallback timeout and (b) an alarm for
         // non-strong biometric idle timeout, so later we can verify that unlocking with
         // strong biometric or primary auth will cancel those alarms
-        mStrongAuth.reportSuccessfulBiometricUnlock(false /* isStrongBiometric */, PRIMARY_USER_ID);
-        mStrongAuth.scheduleNonStrongBiometricIdleTimeout(PRIMARY_USER_ID);
+        mStrongAuth.reportSuccessfulBiometricUnlock(false /* isStrongBiometric */, userId);
+        mStrongAuth.scheduleNonStrongBiometricIdleTimeout(userId);
     }
 
     private void verifyAlarmsCancelledAndNonStrongBiometricAllowed(int userId) {
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/MockSyntheticPasswordManager.java b/services/tests/servicestests/src/com/android/server/locksettings/MockSyntheticPasswordManager.java
index 186a04f..8cb18a8 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/MockSyntheticPasswordManager.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/MockSyntheticPasswordManager.java
@@ -35,7 +35,6 @@
 
     private FakeGateKeeperService mGateKeeper;
     private IWeaver mWeaverService;
-    private PasswordSlotManagerTestable mPasswordSlotManager;
 
     public MockSyntheticPasswordManager(Context context, LockSettingsStorage storage,
             FakeGateKeeperService gatekeeper, UserManager userManager,
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/PasswordSlotManagerTests.java b/services/tests/servicestests/src/com/android/server/locksettings/PasswordSlotManagerTests.java
index 0f24fb2..2faf6a2 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/PasswordSlotManagerTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/PasswordSlotManagerTests.java
@@ -16,8 +16,9 @@
 
 package com.android.server.locksettings;
 
+import static org.junit.Assert.assertEquals;
+
 import android.platform.test.annotations.Presubmit;
-import android.test.AndroidTestCase;
 
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -37,7 +38,7 @@
 @SmallTest
 @Presubmit
 @RunWith(AndroidJUnit4.class)
-public class PasswordSlotManagerTests extends AndroidTestCase {
+public class PasswordSlotManagerTests {
 
     PasswordSlotManagerTestable mManager;
 
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java
index b01c1c8..858f658 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java
@@ -125,7 +125,6 @@
 
     static class MockInjector extends RebootEscrowManager.Injector {
         private final IRebootEscrow mRebootEscrow;
-        private final ResumeOnRebootServiceConnection mServiceConnection;
         private final RebootEscrowProviderInterface mDefaultRebootEscrowProvider;
         private final UserManager mUserManager;
         private final MockableRebootEscrowInjected mInjected;
@@ -140,7 +139,6 @@
                 MockableRebootEscrowInjected injected) {
             super(context, storage);
             mRebootEscrow = rebootEscrow;
-            mServiceConnection = null;
             mServerBased = false;
             RebootEscrowProviderHalImpl.Injector halInjector =
                     new RebootEscrowProviderHalImpl.Injector() {
@@ -161,7 +159,6 @@
                 LockSettingsStorageTestable storage,
                 MockableRebootEscrowInjected injected) {
             super(context, storage);
-            mServiceConnection = serviceConnection;
             mRebootEscrow = null;
             mServerBased = true;
             RebootEscrowProviderServerBasedImpl.Injector injector =
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
index c2e83f2..ea5caa8 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
@@ -678,8 +678,7 @@
         mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
                 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
         when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
-        SecretKey applicationKey =
-                addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
+        addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
 
         mKeySyncTask.run();
 
@@ -710,8 +709,7 @@
         mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
                 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
         when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
-        SecretKey applicationKey =
-                addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
+        addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
 
         mKeySyncTask.run();
 
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/PlatformKeyManagerTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/PlatformKeyManagerTest.java
index 3fd2c97..c546a74 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/PlatformKeyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/PlatformKeyManagerTest.java
@@ -587,7 +587,7 @@
         return keyGenerator.generateKey();
     }
 
-    class PlatformKeyManagerTestable extends PlatformKeyManager {
+    static class PlatformKeyManagerTestable extends PlatformKeyManager {
         private IGateKeeperService mGateKeeperService;
 
         PlatformKeyManagerTestable(
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
index aceae61..281195d 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
@@ -134,7 +134,6 @@
             "V1 reencrypted_recovery_key".getBytes(StandardCharsets.UTF_8);
     private static final String TEST_ALIAS = "nick";
     private static final String TEST_ALIAS2 = "bob";
-    private static final int RECOVERABLE_KEY_SIZE_BYTES = 32;
     private static final int APPLICATION_KEY_SIZE_BYTES = 32;
     private static final int GENERATION_ID = 1;
     private static final byte[] NONCE = getUtf8Bytes("nonce");
@@ -503,8 +502,6 @@
 
     @Test
     public void initRecoveryService_throwsExceptionOnSmallerSerial() throws Exception {
-        int uid = Binder.getCallingUid();
-        int userId = UserHandle.getCallingUserId();
         long certSerial = 1000L;
 
         mRecoverableKeyStoreManager.initRecoveryService(ROOT_CERTIFICATE_ALIAS,
@@ -636,7 +633,6 @@
             throws Exception {
         int uid = Binder.getCallingUid();
         int userId = UserHandle.getCallingUserId();
-        long certSerial = 1000L;
         mRecoverableKeyStoreDb.setShouldCreateSnapshot(userId, uid, false);
 
         mRecoverableKeyStoreManager.initRecoveryServiceWithSigFile(
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/TestData.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/TestData.java
index 5d4be1b..4bf99e0 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/TestData.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/TestData.java
@@ -39,7 +39,6 @@
 
     private static final String KEY_ALGORITHM = "AES";
     private static final long DEFAULT_SERIAL = 10001;
-    private static final String CERT_PATH_ENCODING = "PkiPath";
 
     private static final String CERT_PATH_1_BASE64 = ""
             + "MIIIXTCCBRowggMCoAMCAQICEB35ZwzVpI9ssXg9SAehnU0wDQYJKoZIhvcNAQEL"
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/CleanupManagerTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/CleanupManagerTest.java
index 0b15a12..1c9c6dc 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/CleanupManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/CleanupManagerTest.java
@@ -21,11 +21,9 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-import android.content.Context;
 import android.os.UserHandle;
 import android.os.UserManager;
 
-import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
@@ -49,7 +47,6 @@
     private static final long USER_SERIAL_NUMBER = 101L;
     private static final long USER_SERIAL_NUMBER_2 = 202L;
 
-    private Context mContext;
     private CleanupManager mManager;
 
     @Mock private RecoverableKeyStoreDb mDatabase;
@@ -60,8 +57,7 @@
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
-        mContext = InstrumentationRegistry.getTargetContext();
-        mManager = new CleanupManager(mContext, mRecoverySnapshotStorage, mDatabase, mUserManager,
+        mManager = new CleanupManager(mRecoverySnapshotStorage, mDatabase, mUserManager,
                 mApplicationKeyStorage);
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageVerificationStateTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageVerificationStateTest.java
index 51498a6..a62569f 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageVerificationStateTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageVerificationStateTest.java
@@ -23,7 +23,9 @@
 
 @Presubmit
 public class PackageVerificationStateTest extends AndroidTestCase {
-    private static final int REQUIRED_UID = 1948;
+    private static final int REQUIRED_UID_1 = 1948;
+
+    private static final int REQUIRED_UID_2 = 1949;
 
     private static final int SUFFICIENT_UID_1 = 1005;
 
@@ -31,12 +33,12 @@
 
     public void testPackageVerificationState_OnlyRequiredVerifier_AllowedInstall() {
         PackageVerificationState state = new PackageVerificationState(null);
-        state.setRequiredVerifierUid(REQUIRED_UID);
+        state.addRequiredVerifierUid(REQUIRED_UID_1);
 
         assertFalse("Verification should not be marked as complete yet",
                 state.isVerificationComplete());
 
-        state.setVerifierResponse(REQUIRED_UID, PackageManager.VERIFICATION_ALLOW);
+        state.setVerifierResponse(REQUIRED_UID_1, PackageManager.VERIFICATION_ALLOW);
 
         assertTrue("Verification should be considered complete now",
                 state.isVerificationComplete());
@@ -47,23 +49,111 @@
 
     public void testPackageVerificationState_OnlyRequiredVerifier_DeniedInstall() {
         PackageVerificationState state = new PackageVerificationState(null);
-        state.setRequiredVerifierUid(REQUIRED_UID);
+        state.addRequiredVerifierUid(REQUIRED_UID_1);
 
         assertFalse("Verification should not be marked as complete yet",
                 state.isVerificationComplete());
 
-        state.setVerifierResponse(REQUIRED_UID, PackageManager.VERIFICATION_REJECT);
+        state.setVerifierResponse(REQUIRED_UID_1, PackageManager.VERIFICATION_REJECT);
 
         assertTrue("Verification should be considered complete now",
                 state.isVerificationComplete());
 
-        assertFalse("Installation should be marked as allowed",
+        assertFalse("Installation should be marked as denied",
+                state.isInstallAllowed());
+    }
+
+    public void testPackageVerificationState_TwoRequiredVerifiers_AllowedInstall() {
+        PackageVerificationState state = new PackageVerificationState(null);
+        state.addRequiredVerifierUid(REQUIRED_UID_1);
+        state.addRequiredVerifierUid(REQUIRED_UID_2);
+
+        assertFalse("Verification should not be marked as complete yet",
+                state.isVerificationComplete());
+
+        state.setVerifierResponse(REQUIRED_UID_1, PackageManager.VERIFICATION_ALLOW);
+
+        assertFalse("Verification should not be marked as complete yet",
+                state.isVerificationComplete());
+
+        state.setVerifierResponse(REQUIRED_UID_2, PackageManager.VERIFICATION_ALLOW);
+
+        assertTrue("Verification should be considered complete now",
+                state.isVerificationComplete());
+
+        assertTrue("Installation should be marked as allowed",
+                state.isInstallAllowed());
+    }
+
+    public void testPackageVerificationState_TwoRequiredVerifiers_DeniedInstall() {
+        PackageVerificationState state = new PackageVerificationState(null);
+        state.addRequiredVerifierUid(REQUIRED_UID_1);
+        state.addRequiredVerifierUid(REQUIRED_UID_2);
+
+        assertFalse("Verification should not be marked as complete yet",
+                state.isVerificationComplete());
+
+        state.setVerifierResponse(REQUIRED_UID_1, PackageManager.VERIFICATION_REJECT);
+
+        assertFalse("Verification should not be marked as complete yet",
+                state.isVerificationComplete());
+
+        state.setVerifierResponse(REQUIRED_UID_2, PackageManager.VERIFICATION_REJECT);
+
+        assertTrue("Verification should be considered complete now",
+                state.isVerificationComplete());
+
+        assertFalse("Installation should be marked as denied",
+                state.isInstallAllowed());
+    }
+
+    public void testPackageVerificationState_TwoRequiredVerifiers_FirstDeniedInstall() {
+        PackageVerificationState state = new PackageVerificationState(null);
+        state.addRequiredVerifierUid(REQUIRED_UID_1);
+        state.addRequiredVerifierUid(REQUIRED_UID_2);
+
+        assertFalse("Verification should not be marked as complete yet",
+                state.isVerificationComplete());
+
+        state.setVerifierResponse(REQUIRED_UID_1, PackageManager.VERIFICATION_REJECT);
+
+        assertFalse("Verification should not be marked as complete yet",
+                state.isVerificationComplete());
+
+        state.setVerifierResponse(REQUIRED_UID_2, PackageManager.VERIFICATION_ALLOW);
+
+        assertTrue("Verification should be considered complete now",
+                state.isVerificationComplete());
+
+        assertFalse("Installation should be marked as denied",
+                state.isInstallAllowed());
+    }
+
+    public void testPackageVerificationState_TwoRequiredVerifiers_SecondDeniedInstall() {
+        PackageVerificationState state = new PackageVerificationState(null);
+        state.addRequiredVerifierUid(REQUIRED_UID_1);
+        state.addRequiredVerifierUid(REQUIRED_UID_2);
+
+        assertFalse("Verification should not be marked as complete yet",
+                state.isVerificationComplete());
+
+        state.setVerifierResponse(REQUIRED_UID_1, PackageManager.VERIFICATION_ALLOW);
+
+        assertFalse("Verification should not be marked as complete yet",
+                state.isVerificationComplete());
+
+        state.setVerifierResponse(REQUIRED_UID_2, PackageManager.VERIFICATION_REJECT);
+
+        assertTrue("Verification should be considered complete now",
+                state.isVerificationComplete());
+
+        assertFalse("Installation should be marked as denied",
                 state.isInstallAllowed());
     }
 
     public void testPackageVerificationState_RequiredAndOneSufficient_RequiredDeniedInstall() {
         PackageVerificationState state = new PackageVerificationState(null);
-        state.setRequiredVerifierUid(REQUIRED_UID);
+        state.addRequiredVerifierUid(REQUIRED_UID_1);
 
         assertFalse("Verification should not be marked as complete yet",
                 state.isVerificationComplete());
@@ -78,18 +168,46 @@
         assertFalse("Verification should not be marked as complete yet",
                 state.isVerificationComplete());
 
-        state.setVerifierResponse(REQUIRED_UID, PackageManager.VERIFICATION_REJECT);
+        state.setVerifierResponse(REQUIRED_UID_1, PackageManager.VERIFICATION_REJECT);
 
         assertTrue("Verification should be considered complete now",
                 state.isVerificationComplete());
 
-        assertFalse("Installation should be marked as allowed",
+        assertFalse("Installation should be marked as denied",
+                state.isInstallAllowed());
+    }
+
+    public void testPackageVerificationState_RequiredAndOneSufficient_OneRequiredDeniedInstall() {
+        PackageVerificationState state = new PackageVerificationState(null);
+        state.addRequiredVerifierUid(REQUIRED_UID_1);
+        state.addRequiredVerifierUid(REQUIRED_UID_2);
+
+        assertFalse("Verification should not be marked as complete yet",
+                state.isVerificationComplete());
+
+        state.addSufficientVerifier(SUFFICIENT_UID_1);
+
+        assertFalse("Verification should not be marked as complete yet",
+                state.isVerificationComplete());
+
+        state.setVerifierResponse(SUFFICIENT_UID_1, PackageManager.VERIFICATION_ALLOW);
+
+        assertFalse("Verification should not be marked as complete yet",
+                state.isVerificationComplete());
+
+        state.setVerifierResponse(REQUIRED_UID_1, PackageManager.VERIFICATION_ALLOW);
+        state.setVerifierResponse(REQUIRED_UID_2, PackageManager.VERIFICATION_REJECT);
+
+        assertTrue("Verification should be considered complete now",
+                state.isVerificationComplete());
+
+        assertFalse("Installation should be marked as denied",
                 state.isInstallAllowed());
     }
 
     public void testPackageVerificationState_RequiredAndOneSufficient_SufficientDeniedInstall() {
         PackageVerificationState state = new PackageVerificationState(null);
-        state.setRequiredVerifierUid(REQUIRED_UID);
+        state.addRequiredVerifierUid(REQUIRED_UID_1);
 
         assertFalse("Verification should not be marked as complete yet",
                 state.isVerificationComplete());
@@ -104,7 +222,7 @@
         assertFalse("Verification should not be marked as complete yet",
                 state.isVerificationComplete());
 
-        state.setVerifierResponse(REQUIRED_UID, PackageManager.VERIFICATION_ALLOW);
+        state.setVerifierResponse(REQUIRED_UID_1, PackageManager.VERIFICATION_ALLOW);
 
         assertTrue("Verification should be considered complete now",
                 state.isVerificationComplete());
@@ -115,7 +233,7 @@
 
     public void testPackageVerificationState_RequiredAndTwoSufficient_OneSufficientIsEnough() {
         PackageVerificationState state = new PackageVerificationState(null);
-        state.setRequiredVerifierUid(REQUIRED_UID);
+        state.addRequiredVerifierUid(REQUIRED_UID_1);
 
         assertFalse("Verification should not be marked as complete yet",
                 state.isVerificationComplete());
@@ -131,7 +249,7 @@
         assertFalse("Verification should not be marked as complete yet",
                 state.isVerificationComplete());
 
-        state.setVerifierResponse(REQUIRED_UID, PackageManager.VERIFICATION_ALLOW);
+        state.setVerifierResponse(REQUIRED_UID_1, PackageManager.VERIFICATION_ALLOW);
 
         assertTrue("Verification should be considered complete now",
                 state.isVerificationComplete());
@@ -142,7 +260,7 @@
 
     public void testPackageVerificationState_RequiredAndTwoSufficient_SecondSufficientIsEnough() {
         PackageVerificationState state = new PackageVerificationState(null);
-        state.setRequiredVerifierUid(REQUIRED_UID);
+        state.addRequiredVerifierUid(REQUIRED_UID_1);
 
         assertFalse("Verification should not be marked as complete yet",
                 state.isVerificationComplete());
@@ -153,7 +271,7 @@
         assertFalse("Verification should not be marked as complete yet",
                 state.isVerificationComplete());
 
-        state.setVerifierResponse(REQUIRED_UID, PackageManager.VERIFICATION_ALLOW);
+        state.setVerifierResponse(REQUIRED_UID_1, PackageManager.VERIFICATION_ALLOW);
 
         assertFalse("Verification should not be marked as complete yet",
                 state.isVerificationComplete());
@@ -174,7 +292,7 @@
 
     public void testPackageVerificationState_RequiredAndTwoSufficient_RequiredOverrides() {
         PackageVerificationState state = new PackageVerificationState(null);
-        state.setRequiredVerifierUid(REQUIRED_UID);
+        state.addRequiredVerifierUid(REQUIRED_UID_1);
 
         assertFalse("Verification should not be marked as complete yet",
                 state.isVerificationComplete());
@@ -185,7 +303,7 @@
         assertFalse("Verification should not be marked as complete yet",
                 state.isVerificationComplete());
 
-        state.setVerifierResponse(REQUIRED_UID,
+        state.setVerifierResponse(REQUIRED_UID_1,
                 PackageManager.VERIFICATION_ALLOW_WITHOUT_SUFFICIENT);
 
         assertTrue("Verification should be marked as complete immediately",
@@ -213,17 +331,17 @@
 
     public void testAreAllVerificationsComplete_onlyVerificationPasses() {
         PackageVerificationState state = new PackageVerificationState(null);
-        state.setRequiredVerifierUid(REQUIRED_UID);
+        state.addRequiredVerifierUid(REQUIRED_UID_1);
         assertFalse(state.areAllVerificationsComplete());
 
-        state.setVerifierResponse(REQUIRED_UID, PackageManager.VERIFICATION_ALLOW);
+        state.setVerifierResponse(REQUIRED_UID_1, PackageManager.VERIFICATION_ALLOW);
 
         assertFalse(state.areAllVerificationsComplete());
     }
 
     public void testAreAllVerificationsComplete_onlyIntegrityCheckPasses() {
         PackageVerificationState state = new PackageVerificationState(null);
-        state.setRequiredVerifierUid(REQUIRED_UID);
+        state.addRequiredVerifierUid(REQUIRED_UID_1);
         assertFalse(state.areAllVerificationsComplete());
 
         state.setIntegrityVerificationResult(PackageManagerInternal.INTEGRITY_VERIFICATION_ALLOW);
@@ -233,28 +351,28 @@
 
     public void testAreAllVerificationsComplete_bothPasses() {
         PackageVerificationState state = new PackageVerificationState(null);
-        state.setRequiredVerifierUid(REQUIRED_UID);
+        state.addRequiredVerifierUid(REQUIRED_UID_1);
         assertFalse(state.areAllVerificationsComplete());
 
         state.setIntegrityVerificationResult(PackageManagerInternal.INTEGRITY_VERIFICATION_ALLOW);
-        state.setVerifierResponse(REQUIRED_UID, PackageManager.VERIFICATION_ALLOW);
+        state.setVerifierResponse(REQUIRED_UID_1, PackageManager.VERIFICATION_ALLOW);
 
         assertTrue(state.areAllVerificationsComplete());
     }
 
     public void testAreAllVerificationsComplete_onlyVerificationFails() {
         PackageVerificationState state = new PackageVerificationState(null);
-        state.setRequiredVerifierUid(REQUIRED_UID);
+        state.addRequiredVerifierUid(REQUIRED_UID_1);
         assertFalse(state.areAllVerificationsComplete());
 
-        state.setVerifierResponse(REQUIRED_UID, PackageManager.VERIFICATION_REJECT);
+        state.setVerifierResponse(REQUIRED_UID_1, PackageManager.VERIFICATION_REJECT);
 
         assertFalse(state.areAllVerificationsComplete());
     }
 
     public void testAreAllVerificationsComplete_onlyIntegrityCheckFails() {
         PackageVerificationState state = new PackageVerificationState(null);
-        state.setRequiredVerifierUid(REQUIRED_UID);
+        state.addRequiredVerifierUid(REQUIRED_UID_1);
         assertFalse(state.areAllVerificationsComplete());
 
         state.setIntegrityVerificationResult(PackageManagerInternal.INTEGRITY_VERIFICATION_REJECT);
diff --git a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
index e9171c0c..92c7871 100644
--- a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
@@ -336,6 +336,59 @@
         assertThat(mSysConfig.getAllowedVendorApexes()).isEmpty();
     }
 
+    /**
+     * Tests that readPermissions works correctly for the tag: {@code install-constraints-allowed}.
+     */
+    @Test
+    public void readPermissions_installConstraints_successful() throws IOException {
+        final String contents =
+                "<config>\n"
+                        + "    <install-constraints-allowed package=\"com.android.apex1\" />\n"
+                        + "</config>";
+        final File folder = createTempSubfolder("folder");
+        createTempFile(folder, "install-constraints-allowlist.xml", contents);
+
+        readPermissions(folder, /* Grant all permission flags */ ~0);
+
+        assertThat(mSysConfig.getInstallConstraintsAllowlist())
+                .containsExactly("com.android.apex1");
+    }
+
+    /**
+     * Tests that readPermissions works correctly for the tag: {@code install-constraints-allowed}.
+     */
+    @Test
+    public void readPermissions_installConstraints_noPackage() throws IOException {
+        final String contents =
+                "<config>\n"
+                        + "    <install-constraints-allowed/>\n"
+                        + "</config>";
+        final File folder = createTempSubfolder("folder");
+        createTempFile(folder, "install-constraints-allowlist.xml", contents);
+
+        readPermissions(folder, /* Grant all permission flags */ ~0);
+
+        assertThat(mSysConfig.getInstallConstraintsAllowlist()).isEmpty();
+    }
+
+    /**
+     * Tests that readPermissions works correctly for the tag {@code install-constraints-allowed}
+     * without {@link SystemConfig#ALLOW_VENDOR_APEX}.
+     */
+    @Test
+    public void readPermissions_installConstraints_noAppConfigs() throws IOException {
+        final String contents =
+                "<config>\n"
+                        + "    <install-constraints-allowed package=\"com.android.apex1\" />\n"
+                        + "</config>";
+        final File folder = createTempSubfolder("folder");
+        createTempFile(folder, "install-constraints-allowlist.xml", contents);
+
+        readPermissions(folder,  /* Grant all but ALLOW_APP_CONFIGS flag */ ~0x08);
+
+        assertThat(mSysConfig.getInstallConstraintsAllowlist()).isEmpty();
+    }
+
     @Test
     public void readApexPrivAppPermissions_addAllPermissions()
             throws Exception {
diff --git a/services/tests/wmtests/src/com/android/server/wm/CompatModePackagesTests.java b/services/tests/wmtests/src/com/android/server/wm/CompatModePackagesTests.java
new file mode 100644
index 0000000..1f7b65e
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/CompatModePackagesTests.java
@@ -0,0 +1,95 @@
+/*
+ * 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.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+
+import android.app.GameManagerInternal;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.server.LocalServices;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests for the {@link CompatModePackages} class.
+ *
+ * Build/Install/Run:
+ * atest WmTests:CompatModePackagesTests
+ */
+@SmallTest
+@Presubmit
+public class CompatModePackagesTests extends SystemServiceTestsBase {
+    ActivityTaskManagerService mAtm;
+    GameManagerInternal mGm;
+    static final String TEST_PACKAGE = "compat.mode.packages";
+    static final int TEST_USER_ID = 1;
+
+    @Before
+    public void setUp() {
+        mAtm = mSystemServicesTestRule.getActivityTaskManagerService();
+        mGm = mock(GameManagerInternal.class);
+    }
+
+    @After
+    public void tearDown() {
+        LocalServices.removeServiceForTest(GameManagerInternal.class);
+    }
+
+    @Test
+    public void testGetCompatScale_gameManagerReturnsPositive() {
+        LocalServices.addService(GameManagerInternal.class, mGm);
+        float scale = 0.25f;
+        doReturn(scale).when(mGm).getResolutionScalingFactor(anyString(), anyInt());
+        assertEquals(mAtm.mCompatModePackages.getCompatScale(TEST_PACKAGE, TEST_USER_ID), 1 / scale,
+                0.01f);
+    }
+
+    @Test
+    public void testGetCompatScale_gameManagerReturnsZero() {
+        LocalServices.addService(GameManagerInternal.class, mGm);
+        float scale = 0f;
+        doReturn(scale).when(mGm).getResolutionScalingFactor(anyString(), anyInt());
+        assertEquals(mAtm.mCompatModePackages.getCompatScale(TEST_PACKAGE, TEST_USER_ID), 1f,
+                0.01f);
+    }
+
+    @Test
+    public void testGetCompatScale_gameManagerReturnsNegative() {
+        LocalServices.addService(GameManagerInternal.class, mGm);
+        float scale = -1f;
+        doReturn(scale).when(mGm).getResolutionScalingFactor(anyString(), anyInt());
+        assertEquals(mAtm.mCompatModePackages.getCompatScale(TEST_PACKAGE, TEST_USER_ID), 1f,
+                0.01f);
+    }
+
+    @Test
+    public void testGetCompatScale_noGameManager() {
+        assertEquals(mAtm.mCompatModePackages.getCompatScale(TEST_PACKAGE, TEST_USER_ID), 1f,
+                0.01f);
+    }
+
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
index 83f375f..adf694c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
@@ -1531,10 +1531,10 @@
         public boolean mLastAllowed;
 
         @Override
-        void getTasks(int maxNum, List<RunningTaskInfo> list, int flags,
-                RootWindowContainer root, int callingUid, ArraySet<Integer> profileIds) {
+        void getTasks(int maxNum, List<RunningTaskInfo> list, int flags, RecentTasks recentTasks,
+                WindowContainer root, int callingUid, ArraySet<Integer> profileIds) {
             mLastAllowed = (flags & FLAG_ALLOWED) == FLAG_ALLOWED;
-            super.getTasks(maxNum, list, flags, root, callingUid, profileIds);
+            super.getTasks(maxNum, list, flags, recentTasks, root, callingUid, profileIds);
         }
     }
 }
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 68079f4..9e658e0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
@@ -72,6 +72,7 @@
 import android.content.pm.ResolveInfo;
 import android.content.res.Configuration;
 import android.content.res.Resources;
+import android.os.PowerManager;
 import android.os.UserHandle;
 import android.platform.test.annotations.Presubmit;
 import android.util.MergedConfiguration;
@@ -169,7 +170,8 @@
     public void testTaskLayerRank() {
         final Task rootTask = new TaskBuilder(mSupervisor).build();
         final Task task1 = new TaskBuilder(mSupervisor).setParentTaskFragment(rootTask).build();
-        new ActivityBuilder(mAtm).setTask(task1).build().mVisibleRequested = true;
+        final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task1).build();
+        activity1.mVisibleRequested = true;
         mWm.mRoot.rankTaskLayers();
 
         assertEquals(1, task1.mLayerRank);
@@ -177,7 +179,8 @@
         assertEquals(Task.LAYER_RANK_INVISIBLE, rootTask.mLayerRank);
 
         final Task task2 = new TaskBuilder(mSupervisor).build();
-        new ActivityBuilder(mAtm).setTask(task2).build().mVisibleRequested = true;
+        final ActivityRecord activity2 = new ActivityBuilder(mAtm).setTask(task2).build();
+        activity2.mVisibleRequested = true;
         mWm.mRoot.rankTaskLayers();
 
         // Note that ensureActivitiesVisible is disabled in SystemServicesTestRule, so both the
@@ -192,6 +195,17 @@
 
         assertEquals(1, task1.mLayerRank);
         assertEquals(2, task2.mLayerRank);
+
+        // The rank should be updated to invisible when device went to sleep.
+        activity1.mVisibleRequested = false;
+        activity2.mVisibleRequested = false;
+        doReturn(true).when(mAtm).isSleepingOrShuttingDownLocked();
+        doReturn(true).when(mRootWindowContainer).putTasksToSleep(anyBoolean(), anyBoolean());
+        mSupervisor.mGoingToSleepWakeLock = mock(PowerManager.WakeLock.class);
+        doReturn(false).when(mSupervisor.mGoingToSleepWakeLock).isHeld();
+        mAtm.mTaskSupervisor.checkReadyForSleepLocked(false /* allowDelay */);
+        assertEquals(Task.LAYER_RANK_INVISIBLE, task1.mLayerRank);
+        assertEquals(Task.LAYER_RANK_INVISIBLE, task2.mLayerRank);
     }
 
     @Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java
index 33b2366..b1acae2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java
@@ -35,6 +35,8 @@
 
 import androidx.test.filters.MediumTest;
 
+import com.google.common.truth.Correspondence;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -52,6 +54,9 @@
 public class RunningTasksTest extends WindowTestsBase {
 
     private static final ArraySet<Integer> PROFILE_IDS = new ArraySet<>();
+    private static final Correspondence<RunningTaskInfo, Integer> TASKINFO_HAS_ID =
+            Correspondence.transforming((RunningTaskInfo t) -> t.taskId, "has id");
+
 
     private RunningTasks mRunningTasks;
 
@@ -91,8 +96,8 @@
         // collected from all tasks across all the stacks
         final int numFetchTasks = 5;
         ArrayList<RunningTaskInfo> tasks = new ArrayList<>();
-        mRunningTasks.getTasks(5, tasks, FLAG_ALLOWED | FLAG_CROSS_USERS, mRootWindowContainer,
-                -1 /* callingUid */, PROFILE_IDS);
+        mRunningTasks.getTasks(5, tasks, FLAG_ALLOWED | FLAG_CROSS_USERS,
+                mAtm.getRecentTasks(), mRootWindowContainer, -1 /* callingUid */, PROFILE_IDS);
         assertThat(tasks).hasSize(numFetchTasks);
         for (int i = 0; i < numFetchTasks; i++) {
             assertEquals(numTasks - i - 1, tasks.get(i).id);
@@ -102,7 +107,7 @@
         // and does not crash
         tasks.clear();
         mRunningTasks.getTasks(100, tasks, FLAG_ALLOWED | FLAG_CROSS_USERS,
-                mRootWindowContainer, -1 /* callingUid */, PROFILE_IDS);
+                mAtm.getRecentTasks(), mRootWindowContainer, -1 /* callingUid */, PROFILE_IDS);
         assertThat(tasks).hasSize(numTasks);
         for (int i = 0; i < numTasks; i++) {
             assertEquals(numTasks - i - 1, tasks.get(i).id);
@@ -126,7 +131,7 @@
         final int numFetchTasks = 5;
         final ArrayList<RunningTaskInfo> tasks = new ArrayList<>();
         mRunningTasks.getTasks(numFetchTasks, tasks, FLAG_ALLOWED | FLAG_CROSS_USERS,
-                mRootWindowContainer, -1 /* callingUid */, PROFILE_IDS);
+                mAtm.getRecentTasks(), mRootWindowContainer, -1 /* callingUid */, PROFILE_IDS);
         assertThat(tasks).hasSize(numFetchTasks);
         for (int i = 0; i < tasks.size(); i++) {
             final Bundle extras = tasks.get(i).baseIntent.getExtras();
@@ -151,8 +156,8 @@
         final int numFetchTasks = 5;
         final ArrayList<RunningTaskInfo> tasks = new ArrayList<>();
         mRunningTasks.getTasks(numFetchTasks, tasks,
-                FLAG_ALLOWED | FLAG_CROSS_USERS | FLAG_KEEP_INTENT_EXTRA, mRootWindowContainer,
-                -1 /* callingUid */, PROFILE_IDS);
+                FLAG_ALLOWED | FLAG_CROSS_USERS | FLAG_KEEP_INTENT_EXTRA,
+                mAtm.getRecentTasks(), mRootWindowContainer, -1 /* callingUid */, PROFILE_IDS);
         assertThat(tasks).hasSize(numFetchTasks);
         for (int i = 0; i < tasks.size(); i++) {
             final Bundle extras = tasks.get(i).baseIntent.getExtras();
@@ -184,8 +189,8 @@
         final int numFetchTasks = 5;
         final ArrayList<RunningTaskInfo> fetchTasks = new ArrayList<>();
         mRunningTasks.getTasks(numFetchTasks, fetchTasks,
-                FLAG_ALLOWED | FLAG_CROSS_USERS | FLAG_KEEP_INTENT_EXTRA, mRootWindowContainer,
-                -1 /* callingUid */, PROFILE_IDS);
+                FLAG_ALLOWED | FLAG_CROSS_USERS | FLAG_KEEP_INTENT_EXTRA,
+                mAtm.getRecentTasks(), mRootWindowContainer, -1 /* callingUid */, PROFILE_IDS);
         assertThat(fetchTasks).hasSize(numFetchTasks);
         assertEquals(fetchTasks.get(0).id, focusedTask.mTaskId);
         assertEquals(fetchTasks.get(1).id, visibleTask.mTaskId);
@@ -210,4 +215,46 @@
         task.intent = activity.intent;
         return task;
     }
+
+    @Test
+    public void testMultipleDisplays() {
+        final DisplayContent display0 = new TestDisplayContent.Builder(mAtm, 1000, 2500).build();
+        final DisplayContent display1 = new TestDisplayContent.Builder(mAtm, 1000, 2500).build();
+        final int numTasks = 10;
+        final ArrayList<Task> tasks = new ArrayList<>();
+        for (int i = 0; i < numTasks; i++) {
+            final Task stack = new TaskBuilder(mSupervisor)
+                    .setDisplay(i % 2 == 0 ? display0 : display1)
+                    .setOnTop(true)
+                    .build();
+            final Task task = createTask(stack, ".Task" + i, i, i, null);
+            tasks.add(task);
+        }
+
+        final int numFetchTasks = numTasks;
+        final ArrayList<RunningTaskInfo> fetchTasks = new ArrayList<>();
+
+        mRunningTasks.getTasks(numFetchTasks, fetchTasks,
+                FLAG_ALLOWED | FLAG_CROSS_USERS,
+                mAtm.getRecentTasks(), display0, -1 /* callingUid */, PROFILE_IDS);
+        assertThat(fetchTasks).hasSize(numTasks / 2);
+        assertThat(fetchTasks).comparingElementsUsing(TASKINFO_HAS_ID)
+                .containsExactly(0, 2, 4, 6, 8);
+
+        fetchTasks.clear();
+        mRunningTasks.getTasks(numFetchTasks, fetchTasks,
+                FLAG_ALLOWED | FLAG_CROSS_USERS,
+                mAtm.getRecentTasks(), display1, -1 /* callingUid */, PROFILE_IDS);
+        assertThat(fetchTasks).hasSize(numTasks / 2);
+        assertThat(fetchTasks).comparingElementsUsing(TASKINFO_HAS_ID)
+                .containsExactly(1, 3, 5, 7, 9);
+
+        fetchTasks.clear();
+        mRunningTasks.getTasks(numFetchTasks, fetchTasks,
+                FLAG_ALLOWED | FLAG_CROSS_USERS,
+                mAtm.getRecentTasks(), mRootWindowContainer, -1 /* callingUid */, PROFILE_IDS);
+        assertThat(fetchTasks).hasSize(numTasks);
+        assertThat(fetchTasks).comparingElementsUsing(TASKINFO_HAS_ID)
+                .containsExactly(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
index eba2755..8b3cff8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
@@ -75,6 +75,8 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
 
 /**
  * Build/Install/Run:
@@ -92,7 +94,6 @@
     private TaskFragmentOrganizerToken mOrganizerToken;
     private ITaskFragmentOrganizer mIOrganizer;
     private TaskFragment mTaskFragment;
-    private TaskFragmentInfo mTaskFragmentInfo;
     private IBinder mFragmentToken;
     private WindowContainerTransaction mTransaction;
     private WindowContainerToken mFragmentWindowToken;
@@ -100,14 +101,19 @@
     private IBinder mErrorToken;
     private Rect mTaskFragBounds;
 
+    @Mock
+    private TaskFragmentInfo mTaskFragmentInfo;
+    @Mock
+    private Task mTask;
+
     @Before
     public void setup() {
+        MockitoAnnotations.initMocks(this);
         mWindowOrganizerController = mAtm.mWindowOrganizerController;
         mController = mWindowOrganizerController.mTaskFragmentOrganizerController;
         mOrganizer = new TaskFragmentOrganizer(Runnable::run);
         mOrganizerToken = mOrganizer.getOrganizerToken();
         mIOrganizer = ITaskFragmentOrganizer.Stub.asInterface(mOrganizerToken.asBinder());
-        mTaskFragmentInfo = mock(TaskFragmentInfo.class);
         mFragmentToken = new Binder();
         mTaskFragment =
                 new TaskFragment(mAtm, mFragmentToken, true /* createdByOrganizer */);
@@ -131,6 +137,8 @@
 
     @Test
     public void testCallTaskFragmentCallbackWithoutRegister_throwsException() {
+        doReturn(mTask).when(mTaskFragment).getTask();
+
         assertThrows(IllegalArgumentException.class, () -> mController
                 .onTaskFragmentAppeared(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment));
 
@@ -140,16 +148,21 @@
 
         assertThrows(IllegalArgumentException.class, () -> mController
                 .onTaskFragmentVanished(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment));
-
-        assertThrows(IllegalArgumentException.class, () -> mController
-                .onTaskFragmentParentInfoChanged(mTaskFragment.getTaskFragmentOrganizer(),
-                        mTaskFragment));
     }
 
     @Test
     public void testOnTaskFragmentAppeared() {
         mController.registerOrganizer(mIOrganizer);
 
+        // No-op when the TaskFragment is not attached.
+        mController.onTaskFragmentAppeared(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
+        mController.dispatchPendingEvents();
+
+        verify(mOrganizer, never()).onTaskFragmentAppeared(any());
+
+        // Send callback when the TaskFragment is attached.
+        setupMockParent(mTaskFragment, mTask);
+
         mController.onTaskFragmentAppeared(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
         mController.dispatchPendingEvents();
 
@@ -159,9 +172,21 @@
     @Test
     public void testOnTaskFragmentInfoChanged() {
         mController.registerOrganizer(mIOrganizer);
+        setupMockParent(mTaskFragment, mTask);
+
+        // No-op if onTaskFragmentAppeared is not called yet.
+        mController.onTaskFragmentInfoChanged(mTaskFragment.getTaskFragmentOrganizer(),
+                mTaskFragment);
+        mController.dispatchPendingEvents();
+
+        verify(mOrganizer, never()).onTaskFragmentInfoChanged(any());
+
+        // Call onTaskFragmentAppeared first.
         mController.onTaskFragmentAppeared(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
         mController.dispatchPendingEvents();
 
+        verify(mOrganizer).onTaskFragmentAppeared(any());
+
         // No callback if the info is not changed.
         doReturn(true).when(mTaskFragmentInfo).equalsForTaskFragmentOrganizer(any());
         doReturn(new Configuration()).when(mTaskFragmentInfo).getConfiguration();
@@ -194,47 +219,74 @@
     }
 
     @Test
+    public void testOnTaskFragmentVanished_clearUpRemaining() {
+        mController.registerOrganizer(mIOrganizer);
+        setupMockParent(mTaskFragment, mTask);
+
+        // Not trigger onTaskFragmentAppeared.
+        mController.onTaskFragmentAppeared(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
+        mController.onTaskFragmentVanished(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
+        mController.dispatchPendingEvents();
+
+        verify(mOrganizer, never()).onTaskFragmentAppeared(any());
+        verify(mOrganizer, never()).onTaskFragmentInfoChanged(any());
+        verify(mOrganizer, never()).onTaskFragmentParentInfoChanged(anyInt(), any());
+        verify(mOrganizer).onTaskFragmentVanished(mTaskFragmentInfo);
+
+        // Not trigger onTaskFragmentInfoChanged.
+        // Call onTaskFragmentAppeared before calling onTaskFragmentInfoChanged.
+        mController.onTaskFragmentAppeared(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
+        mController.dispatchPendingEvents();
+        clearInvocations(mOrganizer);
+        doReturn(true).when(mTaskFragmentInfo).equalsForTaskFragmentOrganizer(any());
+        mController.onTaskFragmentInfoChanged(mTaskFragment.getTaskFragmentOrganizer(),
+                mTaskFragment);
+        mController.onTaskFragmentVanished(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
+        mController.dispatchPendingEvents();
+
+        verify(mOrganizer, never()).onTaskFragmentAppeared(any());
+        verify(mOrganizer, never()).onTaskFragmentInfoChanged(any());
+        verify(mOrganizer, never()).onTaskFragmentParentInfoChanged(anyInt(), any());
+        verify(mOrganizer).onTaskFragmentVanished(mTaskFragmentInfo);
+    }
+
+    @Test
     public void testOnTaskFragmentParentInfoChanged() {
         mController.registerOrganizer(mIOrganizer);
-        final Task parent = mock(Task.class);
-        final Configuration parentConfig = new Configuration();
-        parentConfig.smallestScreenWidthDp = 10;
-        doReturn(parent).when(mTaskFragment).getParent();
-        doReturn(parentConfig).when(parent).getConfiguration();
-        doReturn(parent).when(parent).asTask();
+        setupMockParent(mTaskFragment, mTask);
+        mTask.getConfiguration().smallestScreenWidthDp = 10;
 
-        mTaskFragment.mTaskFragmentAppearedSent = true;
-        mController.onTaskFragmentParentInfoChanged(
+        mController.onTaskFragmentAppeared(
                 mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
         mController.dispatchPendingEvents();
 
-        verify(mOrganizer).onTaskFragmentParentInfoChanged(eq(mFragmentToken), any());
+        verify(mOrganizer).onTaskFragmentParentInfoChanged(eq(mTask.mTaskId), any());
 
         // No extra callback if the info is not changed.
         clearInvocations(mOrganizer);
 
-        mController.onTaskFragmentParentInfoChanged(
+        mController.onTaskFragmentInfoChanged(
                 mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
         mController.dispatchPendingEvents();
 
-        verify(mOrganizer, never()).onTaskFragmentParentInfoChanged(any(), any());
+        verify(mOrganizer, never()).onTaskFragmentParentInfoChanged(anyInt(), any());
 
         // Trigger callback if the size is changed.
-        parentConfig.smallestScreenWidthDp = 100;
-        mController.onTaskFragmentParentInfoChanged(
+        mTask.getConfiguration().smallestScreenWidthDp = 100;
+        mController.onTaskFragmentInfoChanged(
                 mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
         mController.dispatchPendingEvents();
 
-        verify(mOrganizer).onTaskFragmentParentInfoChanged(eq(mFragmentToken), any());
+        verify(mOrganizer).onTaskFragmentParentInfoChanged(eq(mTask.mTaskId), any());
 
         // Trigger callback if the windowing mode is changed.
         clearInvocations(mOrganizer);
-        parentConfig.windowConfiguration.setWindowingMode(WINDOWING_MODE_PINNED);
-        mController.onTaskFragmentParentInfoChanged(
+        mTask.getConfiguration().windowConfiguration.setWindowingMode(WINDOWING_MODE_PINNED);
+        mController.onTaskFragmentInfoChanged(
                 mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
         mController.dispatchPendingEvents();
 
-        verify(mOrganizer).onTaskFragmentParentInfoChanged(eq(mFragmentToken), any());
+        verify(mOrganizer).onTaskFragmentParentInfoChanged(eq(mTask.mTaskId), any());
     }
 
     @Test
@@ -1089,4 +1141,15 @@
                 .put(mFragmentToken, mTaskFragment);
         mTaskFragment.getTask().setWindowingMode(WINDOWING_MODE_PINNED);
     }
+
+    /** Setups the mock Task as the parent of the given TaskFragment. */
+    private static void setupMockParent(TaskFragment taskFragment, Task mockParent) {
+        doReturn(mockParent).when(taskFragment).getTask();
+        final Configuration taskConfig = new Configuration();
+        doReturn(taskConfig).when(mockParent).getConfiguration();
+
+        // Task needs to be visible
+        mockParent.lastActiveTime = 100;
+        doReturn(true).when(mockParent).shouldBeVisible(any());
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
index 1096351..88eadfc 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
@@ -32,6 +32,7 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
 import static com.android.server.wm.ActivityRecord.State.RESUMED;
+import static com.android.server.wm.TaskFragment.EMBEDDING_ALLOWED;
 import static com.android.server.wm.TaskFragment.EMBEDDING_DISALLOWED_MIN_DIMENSION_VIOLATION;
 import static com.android.server.wm.TaskFragment.EMBEDDING_DISALLOWED_NEW_TASK_FRAGMENT;
 import static com.android.server.wm.TaskFragment.EMBEDDING_DISALLOWED_UNTRUSTED_HOST;
@@ -468,6 +469,10 @@
         newActivity.resultTo = activity;
         assertEquals(EMBEDDING_DISALLOWED_NEW_TASK_FRAGMENT,
                 newTaskFragment.isAllowedToEmbedActivity(newActivity));
+
+        // Allow embedding if the resultTo activity is finishing.
+        activity.finishing = true;
+        assertEquals(EMBEDDING_ALLOWED, newTaskFragment.isAllowedToEmbedActivity(newActivity));
     }
 
     @Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
index f4323db..6a7e388 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
@@ -846,6 +846,7 @@
                 new ComponentName(DEFAULT_COMPONENT_PACKAGE_NAME, targetClassName);
 
         final Intent intent = new Intent();
+        intent.setPackage(DEFAULT_COMPONENT_PACKAGE_NAME);
         intent.setComponent(aliasComponent);
         final ActivityInfo info = new ActivityInfo();
         info.applicationInfo = new ApplicationInfo();
diff --git a/services/usb/java/com/android/server/usb/hal/port/UsbPortHalInstance.java b/services/usb/java/com/android/server/usb/hal/port/UsbPortHalInstance.java
index 41f9fae..6fc4b67 100644
--- a/services/usb/java/com/android/server/usb/hal/port/UsbPortHalInstance.java
+++ b/services/usb/java/com/android/server/usb/hal/port/UsbPortHalInstance.java
@@ -31,15 +31,14 @@
     public static UsbPortHal getInstance(UsbPortManager portManager, IndentingPrintWriter pw) {
 
         logAndPrint(Log.DEBUG, null, "Querying USB HAL version");
-        if (UsbPortHidl.isServicePresent(null)) {
-            logAndPrint(Log.INFO, null, "USB HAL HIDL present");
-            return new UsbPortHidl(portManager, pw);
-        }
         if (UsbPortAidl.isServicePresent(null)) {
             logAndPrint(Log.INFO, null, "USB HAL AIDL present");
             return new UsbPortAidl(portManager, pw);
         }
-
+        if (UsbPortHidl.isServicePresent(null)) {
+            logAndPrint(Log.INFO, null, "USB HAL HIDL present");
+            return new UsbPortHidl(portManager, pw);
+        }
         return null;
     }
 }
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index bfa60ba..795ecf3 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -1207,13 +1207,8 @@
 
     /**
      * 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) {
+    private void init() {
         if (DBG) Rlog.d(LOG_TAG, "init");
         mVoiceRegState = STATE_OUT_OF_SERVICE;
         mDataRegState = STATE_OUT_OF_SERVICE;
@@ -1245,13 +1240,11 @@
                     .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());
-            }
+            addNetworkRegistrationInfo(new NetworkRegistrationInfo.Builder()
+                    .setDomain(NetworkRegistrationInfo.DOMAIN_PS)
+                    .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WLAN)
+                    .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_UNKNOWN)
+                    .build());
         }
         mOperatorAlphaLongRaw = null;
         mOperatorAlphaShortRaw = null;
@@ -1260,11 +1253,11 @@
     }
 
     public void setStateOutOfService() {
-        init(true);
+        init();
     }
 
     public void setStateOff() {
-        init(true);
+        init();
         mVoiceRegState = STATE_POWER_OFF;
         mDataRegState = STATE_POWER_OFF;
     }
@@ -1272,14 +1265,11 @@
     /**
      * 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);
+    public void setOutOfService(boolean powerOff) {
+        init();
         if (powerOff) {
             mVoiceRegState = STATE_POWER_OFF;
             mDataRegState = STATE_POWER_OFF;
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index a7468a9..7893992 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -6641,7 +6641,7 @@
          * list will be provided. If an error occurs, null will be provided unless the onError
          * callback is overridden.
          *
-         * @param cellInfo a list of {@link CellInfo}, an empty list, or null.
+         * @param cellInfo a list of {@link CellInfo} or an empty list.
          *
          * {@see android.telephony.TelephonyManager#getAllCellInfo getAllCellInfo()}
          */
diff --git a/test-mock/api/test-current.txt b/test-mock/api/test-current.txt
index 531dd7c..35f076f 100644
--- a/test-mock/api/test-current.txt
+++ b/test-mock/api/test-current.txt
@@ -6,6 +6,8 @@
   }
 
   @Deprecated public class MockPackageManager extends android.content.pm.PackageManager {
+    method public void addCrossProfileIntentFilter(android.content.IntentFilter, int, int, int);
+    method public void clearCrossProfileIntentFilters(int);
     method public int getInstallReason(String, android.os.UserHandle);
     method public java.util.List<android.content.pm.ApplicationInfo> getInstalledApplicationsAsUser(int, int);
     method public String[] getNamesForUids(int[]);
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt
index e0ae193..e138d33 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt
@@ -108,7 +108,8 @@
     }
 
     /**
-     * Checks that the [ComponentMatcher.TASK_BAR] layer is visible during the whole transition
+     * Checks that the [ComponentMatcher.TASK_BAR] window is visible at the start and end of the
+     * transition
      *
      * Note: Large screen only
      */
@@ -132,7 +133,8 @@
     }
 
     /**
-     * Checks that the [ComponentMatcher.STATUS_BAR] layer is visible during the whole transition
+     * Checks that the [ComponentMatcher.STATUS_BAR] layer is visible at the start and end
+     * of the transition
      */
     @Presubmit
     @Test
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
index d173b72..5e21252 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
@@ -53,6 +53,16 @@
 }
 
 /**
+ * Checks that [ComponentMatcher.TASK_BAR] window is visible and above the app windows in
+ * all WM trace entries
+ */
+fun FlickerTestParameter.taskBarWindowIsVisibleAtEnd() {
+    assertWmEnd {
+        this.isAboveAppWindowVisible(ComponentMatcher.TASK_BAR)
+    }
+}
+
+/**
  * If [allStates] is true, checks if the stack space of all displays is fully covered
  * by any visible layer, during the whole transitions
  *
@@ -103,9 +113,25 @@
  * trace
  */
 fun FlickerTestParameter.taskBarLayerIsVisibleAtStartAndEnd() {
+    this.taskBarLayerIsVisibleAtStart()
+    this.taskBarLayerIsVisibleAtEnd()
+}
+
+/**
+ * Checks that [ComponentMatcher.TASK_BAR] layer is visible at the start of the SF
+ * trace
+ */
+fun FlickerTestParameter.taskBarLayerIsVisibleAtStart() {
     assertLayersStart {
         this.isVisible(ComponentMatcher.TASK_BAR)
     }
+}
+
+/**
+ * Checks that [ComponentMatcher.TASK_BAR] layer is visible at the end of the SF
+ * trace
+ */
+fun FlickerTestParameter.taskBarLayerIsVisibleAtEnd() {
     assertLayersEnd {
         this.isVisible(ComponentMatcher.TASK_BAR)
     }
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppAfterCameraTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppAfterCameraTest.kt
index 2c2e860..3ff59e9 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppAfterCameraTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppAfterCameraTest.kt
@@ -17,7 +17,7 @@
 package com.android.server.wm.flicker.launch
 
 import android.platform.test.annotations.FlakyTest
-import android.platform.test.annotations.Presubmit
+import android.platform.test.annotations.Postsubmit
 import android.platform.test.annotations.RequiresDevice
 import com.android.server.wm.flicker.FlickerParametersRunnerFactory
 import com.android.server.wm.flicker.FlickerTestParameter
@@ -52,6 +52,9 @@
         get() = {
             super.transition(this)
             setup {
+                test{
+                    tapl.setExpectedRotationCheckEnabled(false)
+                }
                 eachRun {
                     // 1. Open camera - cold -> close it first
                     cameraApp.exit(wmHelper)
@@ -76,10 +79,99 @@
     override fun navBarLayerPositionAtStartAndEnd() = super.navBarLayerPositionAtStartAndEnd()
 
     /** {@inheritDoc} */
-    @Presubmit
+    @Postsubmit
     @Test
     override fun appLayerReplacesLauncher() = super.appLayerReplacesLauncher()
 
+    /** {@inheritDoc} */
+    @Postsubmit
+    @Test
+    override fun focusChanges() = super.focusChanges()
+
+    /** {@inheritDoc} */
+    @Postsubmit
+    @Test
+    override fun appWindowReplacesLauncherAsTopWindow() =
+            super.appWindowReplacesLauncherAsTopWindow()
+
+    /** {@inheritDoc} */
+    @Postsubmit
+    @Test
+    override fun appWindowAsTopWindowAtEnd() = super.appWindowAsTopWindowAtEnd()
+
+    /** {@inheritDoc} */
+    @Postsubmit
+    @Test
+    override fun appLayerBecomesVisible() = super.appLayerBecomesVisible()
+
+    /** {@inheritDoc} */
+    @Postsubmit
+    @Test
+    override fun appWindowBecomesVisible() = super.appWindowBecomesVisible()
+
+    /** {@inheritDoc} */
+    @Postsubmit
+    @Test
+    override fun appWindowBecomesTopWindow() = super.appWindowBecomesTopWindow()
+
+    /** {@inheritDoc} */
+    @Postsubmit
+    @Test
+    override fun appWindowIsTopWindowAtEnd() = super.appWindowIsTopWindowAtEnd()
+
+    /** {@inheritDoc} */
+    @Postsubmit
+    @Test
+    override fun entireScreenCovered() = super.entireScreenCovered()
+
+    /** {@inheritDoc} */
+    @Postsubmit
+    @Test
+    override fun navBarLayerIsVisibleAtStartAndEnd() = super.navBarLayerIsVisibleAtStartAndEnd()
+
+    /** {@inheritDoc} */
+    @Postsubmit
+    @Test
+    override fun navBarWindowIsAlwaysVisible() = super.navBarWindowIsAlwaysVisible()
+
+    /** {@inheritDoc} */
+    @Postsubmit
+    @Test
+    override fun taskBarLayerIsVisibleAtStartAndEnd() = super.taskBarLayerIsVisibleAtStartAndEnd()
+
+    /** {@inheritDoc} */
+    @Postsubmit
+    @Test
+    override fun taskBarWindowIsAlwaysVisible() = super.taskBarWindowIsAlwaysVisible()
+
+    /** {@inheritDoc} */
+    @Postsubmit
+    @Test
+    override fun statusBarLayerIsVisibleAtStartAndEnd() =
+            super.statusBarLayerIsVisibleAtStartAndEnd()
+
+    /** {@inheritDoc} */
+    @Postsubmit
+    @Test
+    override fun statusBarLayerPositionAtStartAndEnd() = super.statusBarLayerPositionAtStartAndEnd()
+
+    /** {@inheritDoc} */
+    @Postsubmit
+    @Test
+    override fun statusBarWindowIsAlwaysVisible() = super.statusBarWindowIsAlwaysVisible()
+
+    /** {@inheritDoc} */
+    @Postsubmit
+    @Test
+    override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+            super.visibleLayersShownMoreThanOneConsecutiveEntry()
+
+    /** {@inheritDoc} */
+    @Postsubmit
+    @Test
+    override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+            super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+
     companion object {
         /**
          * Creates the test configurations.
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarm.kt
index 6e3e74d..866e819 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarm.kt
@@ -32,8 +32,12 @@
 import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
 import com.android.server.wm.flicker.helpers.setRotation
 import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
+import com.android.server.wm.flicker.taskBarLayerIsVisibleAtEnd
+import com.android.server.wm.flicker.taskBarWindowIsVisibleAtEnd
+import com.android.server.wm.traces.common.ComponentMatcher
 import org.junit.Assume
 import org.junit.FixMethodOrder
+import org.junit.Ignore
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.MethodSorters
@@ -231,6 +235,42 @@
         super.appWindowBecomesTopWindow()
     }
 
+    /**
+     * Checks that the [ComponentMatcher.TASK_BAR] window is visible at the end of the transition
+     *
+     * Note: Large screen only
+     */
+    @Postsubmit
+    @Test
+    open fun taskBarWindowIsVisibleAtEnd() {
+        Assume.assumeFalse(testSpec.isTablet)
+        testSpec.taskBarWindowIsVisibleAtEnd()
+    }
+
+    /**
+     * Checks that the [ComponentMatcher.TASK_BAR] layer is visible at the end of the transition
+     *
+     * Note: Large screen only
+     */
+    @Postsubmit
+    @Test
+    open fun taskBarLayerIsVisibleAtEnd() {
+        Assume.assumeFalse(testSpec.isTablet)
+        testSpec.taskBarLayerIsVisibleAtEnd()
+    }
+
+    /** {@inheritDoc} */
+    @Test
+    @Ignore("Display is locked at the start")
+    override fun taskBarWindowIsAlwaysVisible() =
+        super.taskBarWindowIsAlwaysVisible()
+
+    /** {@inheritDoc} */
+    @Test
+    @Ignore("Display is locked at the start")
+    override fun taskBarLayerIsVisibleAtStartAndEnd() =
+        super.taskBarLayerIsVisibleAtStartAndEnd()
+
     companion object {
         /**
          * Creates the test configurations.
diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp
index cbf920ac..035f911 100644
--- a/tools/aapt2/link/ManifestFixer.cpp
+++ b/tools/aapt2/link/ManifestFixer.cpp
@@ -472,6 +472,7 @@
   manifest_action["compatible-screens"]["screen"];
   manifest_action["supports-gl-texture"];
   manifest_action["restrict-update"];
+  manifest_action["install-constraints"]["fingerprint-prefix"];
   manifest_action["package-verifier"];
   manifest_action["meta-data"] = meta_data_action;
   manifest_action["uses-split"].Action(RequiredNameIsJavaPackage);
diff --git a/tools/stringslint/stringslint.py b/tools/stringslint/stringslint.py
deleted file mode 100644
index 15088fc..0000000
--- a/tools/stringslint/stringslint.py
+++ /dev/null
@@ -1,234 +0,0 @@
-#!/usr/bin/env python3
-#-*- coding: 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.
-
-"""
-Enforces common Android string best-practices.  It ignores lint messages from
-a previous strings file, if provided.
-
-Usage: stringslint.py strings.xml
-Usage: stringslint.py strings.xml old_strings.xml
-
-In general:
-* Errors signal issues that must be fixed before submitting, and are only
-  used when there are no false-positives.
-* Warnings signal issues that might need to be fixed, but need manual
-  inspection due to risk of false-positives.
-* Info signal issues that should be fixed to match best-practices, such
-  as providing comments to aid translation.
-"""
-
-import re, sys, codecs
-import lxml.etree as ET
-
-BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = range(8)
-
-def format(fg=None, bg=None, bright=False, bold=False, dim=False, reset=False):
-    # manually derived from http://en.wikipedia.org/wiki/ANSI_escape_code#Codes
-    codes = []
-    if reset: codes.append("0")
-    else:
-        if not fg is None: codes.append("3%d" % (fg))
-        if not bg is None:
-            if not bright: codes.append("4%d" % (bg))
-            else: codes.append("10%d" % (bg))
-        if bold: codes.append("1")
-        elif dim: codes.append("2")
-        else: codes.append("22")
-    return "\033[%sm" % (";".join(codes))
-
-warnings = None
-
-def warn(tag, msg, actual, expected, color=YELLOW):
-    global warnings
-    key = "%s:%d" % (tag.attrib["name"], hash(msg))
-    value = "%sLine %d: '%s':%s %s" % (format(fg=color, bold=True),
-                                       tag.sourceline,
-                                       tag.attrib["name"],
-                                       format(reset=True),
-                                       msg)
-    if not actual is None: value += "\n\tActual: %s%s%s" % (format(dim=True),
-                                                            actual,
-                                                            format(reset=True))
-    if not expected is None: value += "\n\tExample: %s%s%s" % (format(dim=True),
-                                                               expected,
-                                                               format(reset=True))
-    warnings[key] = value
-
-
-def error(tag, msg, actual, expected):
-    warn(tag, msg, actual, expected, RED)
-
-def info(tag, msg, actual, expected):
-    warn(tag, msg, actual, expected, CYAN)
-
-# Escaping logic borrowed from https://stackoverflow.com/a/24519338
-ESCAPE_SEQUENCE_RE = re.compile(r'''
-    ( \\U........      # 8-digit hex escapes
-    | \\u....          # 4-digit hex escapes
-    | \\x..            # 2-digit hex escapes
-    | \\[0-7]{1,3}     # Octal escapes
-    | \\N\{[^}]+\}     # Unicode characters by name
-    | \\[\\'"abfnrtv]  # Single-character escapes
-    )''', re.UNICODE | re.VERBOSE)
-
-def decode_escapes(s):
-    def decode_match(match):
-        return codecs.decode(match.group(0), 'unicode-escape')
-
-    s = re.sub(r"\n\s*", " ", s)
-    s = ESCAPE_SEQUENCE_RE.sub(decode_match, s)
-    s = re.sub(r"%(\d+\$)?[a-z]", "____", s)
-    s = re.sub(r"\^\d+", "____", s)
-    s = re.sub(r"<br/?>", "\n", s)
-    s = re.sub(r"</?[a-z]+>", "", s)
-    return s
-
-def sample_iter(tag):
-    if not isinstance(tag, ET._Comment) and re.match("{.*xliff.*}g", tag.tag) and "example" in tag.attrib:
-        yield tag.attrib["example"]
-    elif tag.text:
-        yield decode_escapes(tag.text)
-    for e in tag:
-        for v in sample_iter(e):
-            yield v
-        if e.tail:
-            yield decode_escapes(e.tail)
-
-def lint(path):
-    global warnings
-    warnings = {}
-
-    with open(path) as f:
-        raw = f.read()
-        if len(raw.strip()) == 0:
-            return warnings
-        tree = ET.fromstring(bytes(raw, encoding='utf-8'))
-        root = tree #tree.getroot()
-
-    last_comment = None
-    for child in root:
-        # TODO: handle plurals
-        if isinstance(child, ET._Comment):
-            last_comment = child
-        elif child.tag == "string":
-            # We always consume comment
-            comment = last_comment
-            last_comment = None
-
-            # Prepare string for analysis
-            text = "".join(child.itertext())
-            sample = "".join(sample_iter(child)).strip().strip("'\"")
-
-            # Validate comment
-            if comment is None:
-                info(child, "Missing string comment to aid translation",
-                     None, None)
-                continue
-            if "do not translate" in comment.text.lower():
-                continue
-            if "translatable" in child.attrib and child.attrib["translatable"].lower() == "false":
-                continue
-
-            misspelled_attributes = [
-              ("translateable", "translatable"),
-            ]
-            for misspelling, expected in misspelled_attributes:
-                if misspelling in child.attrib:
-                    error(child, "Misspelled <string> attribute.", misspelling, expected)
-
-            limit = re.search("CHAR[ _-]LIMIT=(\d+|NONE|none)", comment.text)
-            if limit is None:
-                info(child, "Missing CHAR LIMIT to aid translation",
-                     repr(comment), "<!-- Description of string [CHAR LIMIT=32] -->")
-            elif re.match("\d+", limit.group(1)):
-                limit = int(limit.group(1))
-                if len(sample) > limit:
-                    warn(child, "Expanded string length is larger than CHAR LIMIT",
-                        sample, None)
-
-            # Look for common mistakes/substitutions
-            if "'" in text:
-                error(child, "Turned quotation mark glyphs are more polished",
-                     text, "This doesn\u2019t need to \u2018happen\u2019 today")
-            if '"' in text and not text.startswith('"') and text.endswith('"'):
-                error(child, "Turned quotation mark glyphs are more polished",
-                     text, "This needs to \u201chappen\u201d today")
-            if "..." in text:
-                error(child, "Ellipsis glyph is more polished",
-                     text, "Loading\u2026")
-            if "wi-fi" in text.lower():
-                error(child, "Non-breaking glyph is more polished",
-                     text, "Wi\u2011Fi")
-            if "wifi" in text.lower():
-                error(child, "Using non-standard spelling",
-                     text, "Wi\u2011Fi")
-            if re.search("\d-\d", text):
-                warn(child, "Ranges should use en dash glyph",
-                     text, "You will find this material in chapters 8\u201312")
-            if "--" in text:
-                warn(child, "Phrases should use em dash glyph",
-                     text, "Upon discovering errors\u2014all 124 of them\u2014they recalled.")
-            if ".  " in text:
-                warn(child, "Only use single space between sentences",
-                     text, "First idea. Second idea.")
-            if re.match(r"^[A-Z\s]{5,}$", text):
-                warn(child, "Actions should use android:textAllCaps in layout; ignore if acronym",
-                     text, "Refresh data")
-            if " phone " in text and "product" not in child.attrib:
-                warn(child, "Strings mentioning phones should have variants for tablets",
-                     text, None)
-
-            # When more than one substitution, require indexes
-            if len(re.findall("%[^%]", text)) > 1:
-                if len(re.findall("%[^\d]", text)) > 0:
-                    error(child, "Substitutions must be indexed",
-                         text, "Add %1$s to %2$s")
-
-            # Require xliff substitutions
-            for gc in child.iter():
-                badsub = False
-                if gc.tail and re.search("%[^%]", gc.tail): badsub = True
-                if re.match("{.*xliff.*}g", gc.tag):
-                    if "id" not in gc.attrib:
-                        error(child, "Substitutions must define id attribute",
-                             None, "<xliff:g id=\"domain\" example=\"example.com\">%1$s</xliff:g>")
-                    if "example" not in gc.attrib:
-                        error(child, "Substitutions must define example attribute",
-                             None, "<xliff:g id=\"domain\" example=\"example.com\">%1$s</xliff:g>")
-                else:
-                    if gc.text and re.search("%[^%]", gc.text): badsub = True
-                if badsub:
-                    error(child, "Substitutions must be inside xliff tags",
-                         text, "<xliff:g id=\"domain\" example=\"example.com\">%1$s</xliff:g>")
-
-    return warnings
-
-if len(sys.argv) > 2:
-    before = lint(sys.argv[2])
-else:
-    before = {}
-after = lint(sys.argv[1])
-
-for b in before:
-    if b in after:
-        del after[b]
-
-if len(after) > 0:
-    for a in sorted(after.keys()):
-        print(after[a])
-        print()
-    sys.exit(1)
diff --git a/tools/stringslint/stringslint_sha.sh b/tools/stringslint/stringslint_sha.sh
index bd05698..009a1f2 100755
--- a/tools/stringslint/stringslint_sha.sh
+++ b/tools/stringslint/stringslint_sha.sh
@@ -1,5 +1,3 @@
 #!/bin/bash
-LOCAL_DIR="$( dirname ${BASH_SOURCE} )"
-git show --name-only --pretty=format: $1 | grep values/strings.xml | while read file; do
-    python3 $LOCAL_DIR/stringslint.py <(git show $1:$file) <(git show $1^:$file)
-done
+
+# NOTE: this script has been deprecated and replaced by AyeAye checks directly in Gerrit
diff --git a/tools/xmlpersistence/src/main/kotlin/Generator.kt b/tools/xmlpersistence/src/main/kotlin/Generator.kt
index b2c5f4a..8e62388 100644
--- a/tools/xmlpersistence/src/main/kotlin/Generator.kt
+++ b/tools/xmlpersistence/src/main/kotlin/Generator.kt
@@ -149,6 +149,7 @@
                 when (field) {
                     is ClassFieldInfo -> this += field.allClassFields
                     is ListFieldInfo -> this += field.element.allClassFields
+                    else -> {}
                 }
             }
         }