Merge "Use valid auto-group notification trigger key for sparse groups" into main
diff --git a/core/api/current.txt b/core/api/current.txt
index 520c7d1..6d6f7e3 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -19760,7 +19760,7 @@
 
   public final class CaptureRequest extends android.hardware.camera2.CameraMetadata<android.hardware.camera2.CaptureRequest.Key<?>> implements android.os.Parcelable {
     method public int describeContents();
-    method @FlaggedApi("com.android.internal.camera.flags.surface_leak_fix") protected void finalize();
+    method protected void finalize();
     method @Nullable public <T> T get(android.hardware.camera2.CaptureRequest.Key<T>);
     method @NonNull public java.util.List<android.hardware.camera2.CaptureRequest.Key<?>> getKeys();
     method @Nullable public Object getTag();
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index 1e94c2f..24962a3 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -384,10 +384,6 @@
     field public static final int DEVICE_INITIAL_SDK_INT;
   }
 
-  public class Environment {
-    method @FlaggedApi("android.crashrecovery.flags.enable_crashrecovery") @NonNull public static java.io.File getDataSystemDeDirectory();
-  }
-
   public class IpcDataCache<Query, Result> {
     ctor public IpcDataCache(int, @NonNull String, @NonNull String, @NonNull String, @NonNull android.os.IpcDataCache.QueryHandler<Query,Result>);
     method public void disableForCurrentProcess();
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 9c807af..7a8e829 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -10773,6 +10773,7 @@
   public class Environment {
     method @NonNull public static java.io.File getDataCePackageDirectoryForUser(@NonNull java.util.UUID, @NonNull android.os.UserHandle, @NonNull String);
     method @NonNull public static java.io.File getDataDePackageDirectoryForUser(@NonNull java.util.UUID, @NonNull android.os.UserHandle, @NonNull String);
+    method @FlaggedApi("android.crashrecovery.flags.enable_crashrecovery") @NonNull public static java.io.File getDataSystemDeDirectory();
     method @NonNull public static java.util.Collection<java.io.File> getInternalMediaDirectories();
     method @NonNull public static java.io.File getOdmDirectory();
     method @NonNull public static java.io.File getOemDirectory();
diff --git a/core/java/android/app/admin/flags/flags.aconfig b/core/java/android/app/admin/flags/flags.aconfig
index 081dfe6..d9f886d 100644
--- a/core/java/android/app/admin/flags/flags.aconfig
+++ b/core/java/android/app/admin/flags/flags.aconfig
@@ -134,6 +134,14 @@
  bug: "364338410"
 }
 
+flag {
+ name: "lock_now_coexistence"
+ is_exported: true
+ namespace: "enterprise"
+ description: "Enables coexistence support for lockNow."
+ bug: "366559840"
+}
+
 # Fully rolled out and must not be used.
 flag {
   name: "security_log_v2_enabled"
@@ -323,3 +331,13 @@
         purpose: PURPOSE_BUGFIX
     }
 }
+
+flag {
+  name: "user_provisioning_same_state"
+  namespace: "enterprise"
+  description: "Handle exceptions while setting same provisioning state."
+  bug: "326441417"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
diff --git a/core/java/android/app/appfunctions/TEST_MAPPING b/core/java/android/app/appfunctions/TEST_MAPPING
new file mode 100644
index 0000000..91e82ec
--- /dev/null
+++ b/core/java/android/app/appfunctions/TEST_MAPPING
@@ -0,0 +1,10 @@
+{
+  "postsubmit": [
+    {
+      "name": "FrameworksAppFunctionsTests"
+    },
+    {
+      "name": "CtsAppFunctionTestCases"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index ac72116..3b69aa7 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -612,9 +612,7 @@
             Parcelable[] parcelableArray = in.readParcelableArray(Surface.class.getClassLoader(),
                     Surface.class);
             if (parcelableArray != null) {
-                if (Flags.surfaceLeakFix()) {
-                    mReleaseSurfaces = true;
-                }
+                mReleaseSurfaces = true;
                 for (Parcelable p : parcelableArray) {
                     Surface s = (Surface) p;
                     mSurfaceSet.add(s);
@@ -798,7 +796,6 @@
     }
 
     @SuppressWarnings("Finalize")
-    @FlaggedApi(Flags.FLAG_SURFACE_LEAK_FIX)
     @Override
     protected void finalize() {
         if (mReleaseSurfaces) {
diff --git a/core/java/android/hardware/camera2/extension/SessionProcessor.java b/core/java/android/hardware/camera2/extension/SessionProcessor.java
index 0ec5c0a..eead4ef 100644
--- a/core/java/android/hardware/camera2/extension/SessionProcessor.java
+++ b/core/java/android/hardware/camera2/extension/SessionProcessor.java
@@ -372,11 +372,9 @@
                 Map<String, CameraMetadataNative> charsMap, OutputSurface previewSurface,
                 OutputSurface imageCaptureSurface, OutputSurface postviewSurface)
                 throws RemoteException {
-            if (Flags.surfaceLeakFix()) {
-                mPreviewSurface = previewSurface;
-                mPostviewSurface = postviewSurface;
-                mImageCaptureSurface = imageCaptureSurface;
-            }
+            mPreviewSurface = previewSurface;
+            mPostviewSurface = postviewSurface;
+            mImageCaptureSurface = imageCaptureSurface;
             ExtensionConfiguration config = SessionProcessor.this.initSession(token, cameraId,
                     new CharacteristicsMap(charsMap),
                     new CameraOutputSurface(previewSurface),
@@ -399,16 +397,14 @@
         @Override
         public void deInitSession(IBinder token) throws RemoteException {
             SessionProcessor.this.deInitSession(token);
-            if (Flags.surfaceLeakFix()) {
-                if ((mPreviewSurface != null) && (mPreviewSurface.surface != null)) {
-                    mPreviewSurface.surface.release();
-                }
-                if ((mImageCaptureSurface != null) && (mImageCaptureSurface.surface != null)) {
-                    mImageCaptureSurface.surface.release();
-                }
-                if ((mPostviewSurface != null) && (mPostviewSurface.surface != null)) {
-                    mPostviewSurface.surface.release();
-                }
+            if ((mPreviewSurface != null) && (mPreviewSurface.surface != null)) {
+                mPreviewSurface.surface.release();
+            }
+            if ((mImageCaptureSurface != null) && (mImageCaptureSurface.surface != null)) {
+                mImageCaptureSurface.surface.release();
+            }
+            if ((mPostviewSurface != null) && (mPostviewSurface.surface != null)) {
+                mPostviewSurface.surface.release();
             }
         }
 
diff --git a/core/java/android/hardware/input/KeyGestureEvent.java b/core/java/android/hardware/input/KeyGestureEvent.java
index c7ebc63..0cabc4c 100644
--- a/core/java/android/hardware/input/KeyGestureEvent.java
+++ b/core/java/android/hardware/input/KeyGestureEvent.java
@@ -65,31 +65,34 @@
     public static final int KEY_GESTURE_TYPE_ACCESSIBILITY_ALL_APPS = 23;
     public static final int KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK = 24;
     public static final int KEY_GESTURE_TYPE_SYSTEM_MUTE = 25;
-    public static final int KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION = 26;
-    public static final int KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS = 27;
-    public static final int KEY_GESTURE_TYPE_TRIGGER_BUG_REPORT = 28;
-    public static final int KEY_GESTURE_TYPE_LOCK_SCREEN = 29;
-    public static final int KEY_GESTURE_TYPE_OPEN_NOTES = 30;
-    public static final int KEY_GESTURE_TYPE_TOGGLE_POWER = 31;
-    public static final int KEY_GESTURE_TYPE_SYSTEM_NAVIGATION = 32;
-    public static final int KEY_GESTURE_TYPE_SLEEP = 33;
-    public static final int KEY_GESTURE_TYPE_WAKEUP = 34;
-    public static final int KEY_GESTURE_TYPE_MEDIA_KEY = 35;
-    public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_BROWSER = 36;
-    public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_EMAIL = 37;
-    public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CONTACTS = 38;
-    public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALENDAR = 39;
-    public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALCULATOR = 40;
-    public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MUSIC = 41;
-    public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MAPS = 42;
-    public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MESSAGING = 43;
-    public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_GALLERY = 44;
-    public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FILES = 45;
-    public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_WEATHER = 46;
-    public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FITNESS = 47;
-    public static final int KEY_GESTURE_TYPE_LAUNCH_APPLICATION_BY_PACKAGE_NAME = 48;
-    public static final int KEY_GESTURE_TYPE_DESKTOP_MODE = 49;
-    public static final int KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION = 50;
+    public static final int KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_LEFT = 26;
+    public static final int KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_RIGHT = 27;
+    public static final int KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_LEFT = 28;
+    public static final int KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_RIGHT = 29;
+    public static final int KEY_GESTURE_TYPE_TRIGGER_BUG_REPORT = 30;
+    public static final int KEY_GESTURE_TYPE_LOCK_SCREEN = 31;
+    public static final int KEY_GESTURE_TYPE_OPEN_NOTES = 32;
+    public static final int KEY_GESTURE_TYPE_TOGGLE_POWER = 33;
+    public static final int KEY_GESTURE_TYPE_SYSTEM_NAVIGATION = 34;
+    public static final int KEY_GESTURE_TYPE_SLEEP = 35;
+    public static final int KEY_GESTURE_TYPE_WAKEUP = 36;
+    public static final int KEY_GESTURE_TYPE_MEDIA_KEY = 37;
+    public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_BROWSER = 38;
+    public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_EMAIL = 39;
+    public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CONTACTS = 40;
+    public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALENDAR = 41;
+    public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALCULATOR = 42;
+    public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MUSIC = 43;
+    public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MAPS = 44;
+    public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MESSAGING = 45;
+    public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_GALLERY = 46;
+    public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FILES = 47;
+    public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_WEATHER = 48;
+    public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FITNESS = 49;
+    public static final int KEY_GESTURE_TYPE_LAUNCH_APPLICATION_BY_PACKAGE_NAME = 50;
+    public static final int KEY_GESTURE_TYPE_DESKTOP_MODE = 51;
+    public static final int KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION = 52;
+    public static final int KEY_GESTURE_TYPE_RECENT_APPS_SWITCHER = 53;
 
     public static final int FLAG_CANCELLED = 1;
 
@@ -130,8 +133,10 @@
             KEY_GESTURE_TYPE_ACCESSIBILITY_ALL_APPS,
             KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK,
             KEY_GESTURE_TYPE_SYSTEM_MUTE,
-            KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION,
-            KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS,
+            KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_LEFT,
+            KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_RIGHT,
+            KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_LEFT,
+            KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_RIGHT,
             KEY_GESTURE_TYPE_TRIGGER_BUG_REPORT,
             KEY_GESTURE_TYPE_LOCK_SCREEN,
             KEY_GESTURE_TYPE_OPEN_NOTES,
@@ -155,6 +160,7 @@
             KEY_GESTURE_TYPE_LAUNCH_APPLICATION_BY_PACKAGE_NAME,
             KEY_GESTURE_TYPE_DESKTOP_MODE,
             KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION,
+            KEY_GESTURE_TYPE_RECENT_APPS_SWITCHER
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface KeyGestureType {
@@ -331,6 +337,7 @@
             case KEY_GESTURE_TYPE_HOME:
                 return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__HOME;
             case KEY_GESTURE_TYPE_RECENT_APPS:
+            case KEY_GESTURE_TYPE_RECENT_APPS_SWITCHER:
                 return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__RECENT_APPS;
             case KEY_GESTURE_TYPE_BACK:
                 return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__BACK;
@@ -378,9 +385,11 @@
                 return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__TOGGLE_CAPS_LOCK;
             case KEY_GESTURE_TYPE_SYSTEM_MUTE:
                 return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__SYSTEM_MUTE;
-            case KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION:
+            case KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_LEFT:
+            case KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_RIGHT:
                 return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__SPLIT_SCREEN_NAVIGATION;
-            case KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS:
+            case KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_LEFT:
+            case KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_RIGHT:
                 return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__CHANGE_SPLITSCREEN_FOCUS;
             case KEY_GESTURE_TYPE_TRIGGER_BUG_REPORT:
                 return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__TRIGGER_BUG_REPORT;
@@ -487,10 +496,14 @@
                 return "KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK";
             case KEY_GESTURE_TYPE_SYSTEM_MUTE:
                 return "KEY_GESTURE_TYPE_SYSTEM_MUTE";
-            case KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION:
-                return "KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION";
-            case KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS:
-                return "KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS";
+            case KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_LEFT:
+                return "KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_LEFT";
+            case KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_RIGHT:
+                return "KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_RIGHT";
+            case KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_LEFT:
+                return "KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_LEFT";
+            case KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_RIGHT:
+                return "KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_RIGHT";
             case KEY_GESTURE_TYPE_TRIGGER_BUG_REPORT:
                 return "KEY_GESTURE_TYPE_TRIGGER_BUG_REPORT";
             case KEY_GESTURE_TYPE_LOCK_SCREEN:
@@ -537,6 +550,8 @@
                 return "KEY_GESTURE_TYPE_DESKTOP_MODE";
             case KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION:
                 return "KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION";
+            case KEY_GESTURE_TYPE_RECENT_APPS_SWITCHER:
+                return "KEY_GESTURE_TYPE_RECENT_APPS_SWITCHER";
             default:
                 return Integer.toHexString(value);
         }
diff --git a/core/java/android/hardware/input/input_framework.aconfig b/core/java/android/hardware/input/input_framework.aconfig
index 4478592..1a309c6 100644
--- a/core/java/android/hardware/input/input_framework.aconfig
+++ b/core/java/android/hardware/input/input_framework.aconfig
@@ -112,6 +112,13 @@
 }
 
 flag {
+    namespace: "input_native"
+    name: "use_key_gesture_event_handler"
+    description: "Use KeyGestureEvent handler APIs to control system shortcuts and key gestures"
+    bug: "358569822"
+}
+
+flag {
   name: "keyboard_repeat_keys"
   namespace: "input_native"
   description: "Allow configurable timeout before key repeat and repeat delay rate for key repeats"
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index 2c7b9c0..89a5e5d 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -415,7 +415,7 @@
      * Returns the base directory for per-user system directory, device encrypted.
      * {@hide}
      */
-    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    @SystemApi
     @FlaggedApi(android.crashrecovery.flags.Flags.FLAG_ENABLE_CRASHRECOVERY)
     public static @NonNull File getDataSystemDeDirectory() {
         return buildPath(getDataDirectory(), "system_de");
diff --git a/core/java/android/webkit/WebViewZygote.java b/core/java/android/webkit/WebViewZygote.java
index c7900e4..668cd01 100644
--- a/core/java/android/webkit/WebViewZygote.java
+++ b/core/java/android/webkit/WebViewZygote.java
@@ -22,6 +22,7 @@
 import android.os.Build;
 import android.os.ChildZygoteProcess;
 import android.os.Process;
+import android.os.UserHandle;
 import android.os.ZygoteProcess;
 import android.text.TextUtils;
 import android.util.Log;
@@ -141,12 +142,14 @@
             String abi = sPackage.applicationInfo.primaryCpuAbi;
             int runtimeFlags = Zygote.getMemorySafetyRuntimeFlagsForSecondaryZygote(
                     sPackage.applicationInfo, null);
+            final int[] sharedAppGid = {
+                    UserHandle.getSharedAppGid(UserHandle.getAppId(sPackage.applicationInfo.uid)) };
             sZygote = Process.ZYGOTE_PROCESS.startChildZygote(
                     "com.android.internal.os.WebViewZygoteInit",
                     "webview_zygote",
                     Process.WEBVIEW_ZYGOTE_UID,
                     Process.WEBVIEW_ZYGOTE_UID,
-                    null,  // gids
+                    sharedAppGid,  // Access to shared app GID for ART profiles
                     runtimeFlags,
                     "webview_zygote",  // seInfo
                     abi,  // abi
diff --git a/core/java/android/window/flags/windowing_frontend.aconfig b/core/java/android/window/flags/windowing_frontend.aconfig
index 69b91fd..e1402f8 100644
--- a/core/java/android/window/flags/windowing_frontend.aconfig
+++ b/core/java/android/window/flags/windowing_frontend.aconfig
@@ -246,15 +246,6 @@
 }
 
 flag {
-  name: "custom_animations_behind_translucent"
-  namespace: "windowing_frontend"
-  description: "A change can use its own layer parameters to animate behind a translucent activity"
-  bug: "327332488"
-  metadata {
-    purpose: PURPOSE_BUGFIX
-  }
-}
-flag {
   name: "migrate_predictive_back_transition"
   namespace: "windowing_frontend"
   description: "Create transition when visibility change from predictive back"
@@ -274,4 +265,4 @@
   metadata {
     purpose: PURPOSE_BUGFIX
   }
-}
\ No newline at end of file
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
index 46fe68f..0caa8e9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
@@ -55,6 +55,7 @@
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.SyncTransactionQueue;
 import com.android.wm.shell.shared.annotations.ShellBackgroundThread;
+import com.android.wm.shell.shared.desktopmode.DesktopModeFlags;
 import com.android.wm.shell.windowdecor.extension.TaskInfoKt;
 import com.android.wm.shell.windowdecor.viewhost.WindowDecorViewHostSupplier;
 
@@ -240,7 +241,8 @@
             boolean applyStartTransactionOnDraw, boolean setTaskCropAndPosition) {
         final boolean isFreeform =
                 taskInfo.getWindowingMode() == WindowConfiguration.WINDOWING_MODE_FREEFORM;
-        final boolean isDragResizeable = isFreeform && taskInfo.isResizeable;
+        final boolean isDragResizeable = DesktopModeFlags.SCALED_RESIZING.isEnabled(mContext)
+                ? isFreeform : isFreeform && taskInfo.isResizeable;
 
         final WindowDecorLinearLayout oldRootView = mResult.mRootView;
         final SurfaceControl oldDecorationSurface = mDecorationContainerSurface;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
index 2519ce4..b311359 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
@@ -1603,14 +1603,27 @@
                 Transitions transitions,
                 InteractionJankMonitor interactionJankMonitor,
                 Supplier<SurfaceControl.Transaction> transactionFactory) {
-            if (!DesktopModeStatus.isVeiledResizeEnabled()) {
-                return new FluidResizeTaskPositioner(
-                        taskOrganizer, transitions, windowDecoration, displayController,
-                        dragStartListener, transactionFactory);
+            final TaskPositioner taskPositioner = DesktopModeStatus.isVeiledResizeEnabled()
+                    ? new VeiledResizeTaskPositioner(
+                            taskOrganizer,
+                            windowDecoration,
+                            displayController,
+                            dragStartListener,
+                            transitions,
+                            interactionJankMonitor)
+                    : new FluidResizeTaskPositioner(
+                            taskOrganizer,
+                            transitions,
+                            windowDecoration,
+                            displayController,
+                            dragStartListener,
+                            transactionFactory);
+
+            if (DesktopModeFlags.SCALED_RESIZING.isEnabled(windowDecoration.mContext)) {
+                return new FixedAspectRatioTaskPositionerDecorator(windowDecoration,
+                        taskPositioner);
             }
-            return new VeiledResizeTaskPositioner(
-                    taskOrganizer, windowDecoration, displayController,
-                    dragStartListener, transitions, interactionJankMonitor);
+            return taskPositioner;
         }
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
index 5d16d97..5521c2e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
@@ -455,7 +455,7 @@
     }
 
     private void updateDragResizeListener(SurfaceControl oldDecorationSurface) {
-        if (!isDragResizable(mTaskInfo)) {
+        if (!isDragResizable(mTaskInfo, mContext)) {
             if (!mTaskInfo.positionInParent.equals(mPositionInParent)) {
                 // We still want to track caption bar's exclusion region on a non-resizeable task.
                 updateExclusionRegion();
@@ -497,12 +497,16 @@
         }
     }
 
-    private static boolean isDragResizable(ActivityManager.RunningTaskInfo taskInfo) {
+    private static boolean isDragResizable(ActivityManager.RunningTaskInfo taskInfo,
+            Context context) {
+        if (DesktopModeFlags.SCALED_RESIZING.isEnabled(context)) {
+            return taskInfo.isFreeform();
+        }
         return taskInfo.isFreeform() && taskInfo.isResizeable;
     }
 
     private void updateMaximizeMenu(SurfaceControl.Transaction startT) {
-        if (!isDragResizable(mTaskInfo) || !isMaximizeMenuActive()) {
+        if (!isDragResizable(mTaskInfo, mContext) || !isMaximizeMenuActive()) {
             return;
         }
         if (!mTaskInfo.isVisible()) {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
index 1741fe4..69efdb8 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
@@ -107,6 +107,7 @@
 import org.junit.After;
 import org.junit.Before;
 import org.junit.BeforeClass;
+import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -563,6 +564,7 @@
     }
 
     @Test
+    @Ignore("TODO(b/367235906): Due to MONITOR_INPUT permission error")
     public void relayout_freeformTask_appliesTransactionOnDraw() {
         final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
         final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo));
diff --git a/media/java/android/media/SoundPool.java b/media/java/android/media/SoundPool.java
index f33a744..7b9ff23 100644
--- a/media/java/android/media/SoundPool.java
+++ b/media/java/android/media/SoundPool.java
@@ -26,6 +26,7 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.ParcelFileDescriptor;
+import android.os.Trace;
 import android.util.AndroidRuntimeException;
 import android.util.Log;
 
@@ -314,8 +315,13 @@
     public final int play(int soundID, float leftVolume, float rightVolume,
             int priority, int loop, float rate) {
         // FIXME: b/174876164 implement device id for soundpool
-        baseStart(0);
-        return _play(soundID, leftVolume, rightVolume, priority, loop, rate, getPlayerIId());
+        try {
+            Trace.traceBegin(Trace.TRACE_TAG_AUDIO, "SoundPool.play");
+            baseStart(0);
+            return _play(soundID, leftVolume, rightVolume, priority, loop, rate, getPlayerIId());
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_AUDIO);
+        }
     }
 
     /**
diff --git a/media/jni/soundpool/SoundPool.cpp b/media/jni/soundpool/SoundPool.cpp
index 9f32a83..4c0b8d0 100644
--- a/media/jni/soundpool/SoundPool.cpp
+++ b/media/jni/soundpool/SoundPool.cpp
@@ -21,6 +21,9 @@
 #include <algorithm>
 #include <thread>
 
+#define ATRACE_TAG ATRACE_TAG_AUDIO
+#include <utils/Trace.h>
+
 #include "SoundPool.h"
 
 namespace android
@@ -135,8 +138,10 @@
         return 0;
     }
 
+    ATRACE_BEGIN("SoundPool::play (native)");
     const int32_t streamID = mStreamManager.queueForPlay(
             sound, soundID, leftVolume, rightVolume, priority, loop, rate, playerIId);
+    ATRACE_END();
     ALOGV("%s returned %d", __func__, streamID);
 
     return streamID;
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 02e8cd6..7974f92 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -1313,6 +1313,16 @@
 }
 
 flag {
+   name: "sim_pin_bouncer_reset"
+   namespace: "systemui"
+   description: "The SIM PIN bouncer does not close after unlocking"
+   bug: "297461589"
+   metadata {
+        purpose: PURPOSE_BUGFIX
+   }
+}
+
+flag {
    name: "use_transitions_for_keyguard_occluded"
    namespace: "systemui"
    description: "Use Keyguard Transitions to set Notification Shade occlusion state"
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/windowsizeclass/WindowSizeClass.kt b/packages/SystemUI/compose/core/src/com/android/compose/windowsizeclass/WindowSizeClass.kt
index 4674d6e..c01396a 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/windowsizeclass/WindowSizeClass.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/windowsizeclass/WindowSizeClass.kt
@@ -16,15 +16,16 @@
 
 package com.android.compose.windowsizeclass
 
+import android.view.WindowManager
 import androidx.compose.material3.windowsizeclass.ExperimentalMaterial3WindowSizeClassApi
 import androidx.compose.material3.windowsizeclass.WindowSizeClass
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
 import androidx.compose.runtime.staticCompositionLocalOf
 import androidx.compose.ui.graphics.toComposeRect
 import androidx.compose.ui.platform.LocalConfiguration
 import androidx.compose.ui.platform.LocalContext
 import androidx.compose.ui.platform.LocalDensity
-import androidx.window.layout.WindowMetricsCalculator
 
 val LocalWindowSizeClass =
     staticCompositionLocalOf<WindowSizeClass> {
@@ -41,7 +42,10 @@
     LocalConfiguration.current
     val density = LocalDensity.current
     val context = LocalContext.current
-    val metrics = WindowMetricsCalculator.getOrCreate().computeCurrentWindowMetrics(context)
+    val metrics =
+        remember(context) {
+            context.getSystemService(WindowManager::class.java)!!.currentWindowMetrics
+        }
     val size = with(density) { metrics.bounds.toComposeRect().size.toDpSize() }
     return WindowSizeClass.calculateFromSize(size)
 }
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
index 502dbe3..5ed11ad 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
@@ -34,6 +34,7 @@
 import com.android.systemui.plugins.clocks.ClockFaceController
 import com.android.systemui.plugins.clocks.ClockFaceEvents
 import com.android.systemui.plugins.clocks.ClockMessageBuffers
+import com.android.systemui.plugins.clocks.ClockReactiveSetting
 import com.android.systemui.plugins.clocks.ClockSettings
 import com.android.systemui.plugins.clocks.DefaultClockFaceLayout
 import com.android.systemui.plugins.clocks.WeatherData
@@ -73,7 +74,7 @@
         ClockConfig(
             DEFAULT_CLOCK_ID,
             resources.getString(R.string.clock_default_name),
-            resources.getString(R.string.clock_default_description)
+            resources.getString(R.string.clock_default_description),
         )
     }
 
@@ -84,14 +85,14 @@
                 layoutInflater.inflate(R.layout.clock_default_small, parent, false)
                     as AnimatableClockView,
                 settings?.seedColor,
-                messageBuffers?.smallClockMessageBuffer
+                messageBuffers?.smallClockMessageBuffer,
             )
         largeClock =
             LargeClockFaceController(
                 layoutInflater.inflate(R.layout.clock_default_large, parent, false)
                     as AnimatableClockView,
                 settings?.seedColor,
-                messageBuffers?.largeClockMessageBuffer
+                messageBuffers?.largeClockMessageBuffer,
             )
         clocks = listOf(smallClock.view, largeClock.view)
 
@@ -272,8 +273,12 @@
         }
 
         override fun onWeatherDataChanged(data: WeatherData) {}
+
         override fun onAlarmDataChanged(data: AlarmData) {}
+
         override fun onZenDataChanged(data: ZenData) {}
+
+        override fun onReactiveAxesChanged(axes: List<ClockReactiveSetting>) {}
     }
 
     open inner class DefaultClockAnimations(
@@ -340,10 +345,9 @@
         }
     }
 
-    class AnimationState(
-        var fraction: Float,
-    ) {
+    class AnimationState(var fraction: Float) {
         var isActive: Boolean = fraction > 0.5f
+
         fun update(newFraction: Float): Pair<Boolean, Boolean> {
             if (newFraction == fraction) {
                 return Pair(isActive, false)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/TileUiStateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/TileUiStateTest.kt
new file mode 100644
index 0000000..b144f06
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/TileUiStateTest.kt
@@ -0,0 +1,270 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.qs.panels.ui.viewmodel
+
+import android.content.res.Resources
+import android.content.res.mainResources
+import android.service.quicksettings.Tile
+import android.widget.Button
+import android.widget.Switch
+import androidx.compose.ui.semantics.Role
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.plugins.qs.QSTile
+import com.android.systemui.res.R
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class TileUiStateTest : SysuiTestCase() {
+
+    private val kosmos = testKosmos()
+    private val resources: Resources
+        get() = kosmos.mainResources
+
+    @Test
+    fun stateUnavailable_secondaryLabelNotmodified() {
+        val testString = "TEST STRING"
+        val state =
+            QSTile.State().apply {
+                state = Tile.STATE_UNAVAILABLE
+                secondaryLabel = testString
+            }
+
+        val uiState = state.toUiState()
+
+        assertThat(uiState.state).isEqualTo(Tile.STATE_UNAVAILABLE)
+    }
+
+    @Test
+    fun accessibilityRole_switch() {
+        val stateSwitch =
+            QSTile.State().apply { expandedAccessibilityClassName = Switch::class.java.name }
+        val uiState = stateSwitch.toUiState()
+        assertThat(uiState.accessibilityRole).isEqualTo(Role.Switch)
+    }
+
+    @Test
+    fun accessibilityRole_button() {
+        val stateButton =
+            QSTile.State().apply { expandedAccessibilityClassName = Button::class.java.name }
+        val uiState = stateButton.toUiState()
+        assertThat(uiState.accessibilityRole).isEqualTo(Role.Button)
+    }
+
+    @Test
+    fun accessibilityRole_switchWithSecondaryClick() {
+        val stateSwitchWithSecondaryClick =
+            QSTile.State().apply {
+                expandedAccessibilityClassName = Switch::class.java.name
+                handlesSecondaryClick = true
+            }
+        val uiState = stateSwitchWithSecondaryClick.toUiState()
+        assertThat(uiState.accessibilityRole).isEqualTo(Role.Button)
+    }
+
+    @Test
+    fun switchInactive_secondaryLabelNotModified() {
+        val testString = "TEST STRING"
+        val state =
+            QSTile.State().apply {
+                expandedAccessibilityClassName = Switch::class.java.name
+                state = Tile.STATE_INACTIVE
+                secondaryLabel = testString
+            }
+
+        val uiState = state.toUiState()
+
+        assertThat(uiState.secondaryLabel).isEqualTo(testString)
+    }
+
+    @Test
+    fun switchActive_secondaryLabelNotModified() {
+        val testString = "TEST STRING"
+        val state =
+            QSTile.State().apply {
+                expandedAccessibilityClassName = Switch::class.java.name
+                state = Tile.STATE_ACTIVE
+                secondaryLabel = testString
+            }
+
+        val uiState = state.toUiState()
+
+        assertThat(uiState.secondaryLabel).isEqualTo(testString)
+    }
+
+    @Test
+    fun buttonInactive_secondaryLabelNotModifiedWhenEmpty() {
+        val state =
+            QSTile.State().apply {
+                expandedAccessibilityClassName = Button::class.java.name
+                state = Tile.STATE_INACTIVE
+                secondaryLabel = ""
+            }
+
+        val uiState = state.toUiState()
+
+        assertThat(uiState.secondaryLabel).isEmpty()
+    }
+
+    @Test
+    fun buttonActive_secondaryLabelNotModifiedWhenEmpty() {
+        val state =
+            QSTile.State().apply {
+                expandedAccessibilityClassName = Button::class.java.name
+                state = Tile.STATE_ACTIVE
+                secondaryLabel = ""
+            }
+
+        val uiState = state.toUiState()
+
+        assertThat(uiState.secondaryLabel).isEmpty()
+    }
+
+    @Test
+    fun buttonUnavailable_emptySecondaryLabel_default() {
+        val state =
+            QSTile.State().apply {
+                expandedAccessibilityClassName = Button::class.java.name
+                state = Tile.STATE_UNAVAILABLE
+                secondaryLabel = ""
+            }
+
+        val uiState = state.toUiState()
+
+        assertThat(uiState.secondaryLabel).isEqualTo(resources.getString(R.string.tile_unavailable))
+    }
+
+    @Test
+    fun switchUnavailable_emptySecondaryLabel_defaultUnavailable() {
+        val state =
+            QSTile.State().apply {
+                expandedAccessibilityClassName = Switch::class.java.name
+                state = Tile.STATE_UNAVAILABLE
+                secondaryLabel = ""
+            }
+
+        val uiState = state.toUiState()
+
+        assertThat(uiState.secondaryLabel).isEqualTo(resources.getString(R.string.tile_unavailable))
+    }
+
+    @Test
+    fun switchInactive_emptySecondaryLabel_defaultOff() {
+        val state =
+            QSTile.State().apply {
+                expandedAccessibilityClassName = Switch::class.java.name
+                state = Tile.STATE_INACTIVE
+                secondaryLabel = ""
+            }
+
+        val uiState = state.toUiState()
+
+        assertThat(uiState.secondaryLabel).isEqualTo(resources.getString(R.string.switch_bar_off))
+    }
+
+    @Test
+    fun switchActive_emptySecondaryLabel_defaultOn() {
+        val state =
+            QSTile.State().apply {
+                expandedAccessibilityClassName = Switch::class.java.name
+                state = Tile.STATE_ACTIVE
+                secondaryLabel = ""
+            }
+
+        val uiState = state.toUiState()
+
+        assertThat(uiState.secondaryLabel).isEqualTo(resources.getString(R.string.switch_bar_on))
+    }
+
+    @Test
+    fun disabledByPolicy_inactive_appearsAsUnavailable() {
+        val stateDisabledByPolicy =
+            QSTile.State().apply {
+                state = Tile.STATE_INACTIVE
+                disabledByPolicy = true
+            }
+
+        val uiState = stateDisabledByPolicy.toUiState()
+
+        assertThat(uiState.state).isEqualTo(Tile.STATE_UNAVAILABLE)
+    }
+
+    @Test
+    fun disabledByPolicy_active_appearsAsUnavailable() {
+        val stateDisabledByPolicy =
+            QSTile.State().apply {
+                state = Tile.STATE_ACTIVE
+                disabledByPolicy = true
+            }
+
+        val uiState = stateDisabledByPolicy.toUiState()
+
+        assertThat(uiState.state).isEqualTo(Tile.STATE_UNAVAILABLE)
+    }
+
+    @Test
+    fun disabledByPolicy_clickLabel() {
+        val stateDisabledByPolicy =
+            QSTile.State().apply {
+                state = Tile.STATE_INACTIVE
+                disabledByPolicy = true
+            }
+
+        val uiState = stateDisabledByPolicy.toUiState()
+        assertThat(uiState.accessibilityUiState.clickLabel)
+            .isEqualTo(
+                resources.getString(
+                    R.string.accessibility_tile_disabled_by_policy_action_description
+                )
+            )
+    }
+
+    @Test
+    fun notDisabledByPolicy_clickLabel_null() {
+        val stateDisabledByPolicy =
+            QSTile.State().apply {
+                state = Tile.STATE_INACTIVE
+                disabledByPolicy = false
+            }
+
+        val uiState = stateDisabledByPolicy.toUiState()
+        assertThat(uiState.accessibilityUiState.clickLabel).isNull()
+    }
+
+    @Test
+    fun disabledByPolicy_unavailableInStateDescription() {
+        val state =
+            QSTile.State().apply {
+                disabledByPolicy = true
+                state = Tile.STATE_INACTIVE
+            }
+
+        val uiState = state.toUiState()
+        assertThat(uiState.accessibilityUiState.stateDescription)
+            .contains(resources.getString(R.string.tile_unavailable))
+    }
+
+    private fun QSTile.State.toUiState() = toUiState(resources)
+}
+
+private val TileUiState.accessibilityRole: Role
+    get() = accessibilityUiState.accessibilityRole
diff --git a/packages/SystemUI/tests/src/com/android/systemui/telephony/TelephonyCallbackTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/telephony/TelephonyCallbackTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/telephony/TelephonyCallbackTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/telephony/TelephonyCallbackTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/telephony/TelephonyListenerManagerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/telephony/TelephonyListenerManagerTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/telephony/TelephonyListenerManagerTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/telephony/TelephonyListenerManagerTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewLoggerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/temporarydisplay/TemporaryViewLoggerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewLoggerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/temporarydisplay/TemporaryViewLoggerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewUiEventLoggerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/temporarydisplay/TemporaryViewUiEventLoggerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewUiEventLoggerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/temporarydisplay/TemporaryViewUiEventLoggerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/touchpad/data/repository/TouchpadRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/data/repository/TouchpadRepositoryTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/touchpad/data/repository/TouchpadRepositoryTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/data/repository/TouchpadRepositoryTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/touchpad/tutorial/ui/gesture/BackGestureMonitorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/BackGestureMonitorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/touchpad/tutorial/ui/gesture/BackGestureMonitorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/BackGestureMonitorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/touchpad/tutorial/ui/gesture/FakeMotionEvent.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/FakeMotionEvent.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/touchpad/tutorial/ui/gesture/FakeMotionEvent.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/FakeMotionEvent.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureMonitorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureMonitorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureMonitorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureMonitorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureMonitorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureMonitorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureMonitorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureMonitorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureBuilder.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureBuilder.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureBuilder.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureBuilder.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureBuilderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureBuilderTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureBuilderTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureBuilderTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureHandlerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureHandlerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureHandlerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureHandlerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/TouchpadTutorialViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/TouchpadTutorialViewModelTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/TouchpadTutorialViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/TouchpadTutorialViewModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/tracing/TraceUtilsTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/tracing/TraceUtilsTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/tracing/TraceUtilsTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/tracing/TraceUtilsTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/DisplaySwitchLatencyTrackerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/DisplaySwitchLatencyTrackerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/unfold/DisplaySwitchLatencyTrackerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/DisplaySwitchLatencyTrackerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldLightRevealOverlayAnimationTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/FoldLightRevealOverlayAnimationTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/unfold/FoldLightRevealOverlayAnimationTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/FoldLightRevealOverlayAnimationTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldStateLoggingProviderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/FoldStateLoggingProviderTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/unfold/FoldStateLoggingProviderTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/FoldStateLoggingProviderTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldStateRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/FoldStateRepositoryTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/unfold/FoldStateRepositoryTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/FoldStateRepositoryTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldHapticsPlayerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/UnfoldHapticsPlayerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldHapticsPlayerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/UnfoldHapticsPlayerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldTestUtilsKosmos.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/UnfoldTestUtilsKosmos.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldTestUtilsKosmos.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/UnfoldTestUtilsKosmos.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldTransitionWallpaperControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/UnfoldTransitionWallpaperControllerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldTransitionWallpaperControllerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/UnfoldTransitionWallpaperControllerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfigTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfigTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfigTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfigTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/config/TestUnfoldTransitionConfig.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/config/TestUnfoldTransitionConfig.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/unfold/config/TestUnfoldTransitionConfig.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/config/TestUnfoldTransitionConfig.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/MainThreadUnfoldTransitionProgressProviderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/progress/MainThreadUnfoldTransitionProgressProviderTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/unfold/progress/MainThreadUnfoldTransitionProgressProviderTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/progress/MainThreadUnfoldTransitionProgressProviderTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/RemoteUnfoldTransitionReceiverTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/progress/RemoteUnfoldTransitionReceiverTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/unfold/progress/RemoteUnfoldTransitionReceiverTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/progress/RemoteUnfoldTransitionReceiverTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/TestUnfoldProgressListener.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/progress/TestUnfoldProgressListener.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/unfold/progress/TestUnfoldProgressListener.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/progress/TestUnfoldProgressListener.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceStateRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/updates/DeviceStateRepositoryTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceStateRepositoryTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/updates/DeviceStateRepositoryTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/RotationChangeProviderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/updates/RotationChangeProviderTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/unfold/updates/RotationChangeProviderTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/updates/RotationChangeProviderTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/FoldableTestUtils.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/util/FoldableTestUtils.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/unfold/util/FoldableTestUtils.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/util/FoldableTestUtils.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProviderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProviderTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProviderTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProviderTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScopedUnfoldTransitionProgressProviderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/util/ScopedUnfoldTransitionProgressProviderTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScopedUnfoldTransitionProgressProviderTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/util/ScopedUnfoldTransitionProgressProviderTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/TestFoldProvider.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/util/TestFoldProvider.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/unfold/util/TestFoldProvider.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/util/TestFoldProvider.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/TestFoldStateProvider.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/util/TestFoldStateProvider.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/unfold/util/TestFoldStateProvider.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/util/TestFoldStateProvider.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/UnfoldOnlyProgressProviderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/util/UnfoldOnlyProgressProviderTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/unfold/util/UnfoldOnlyProgressProviderTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/util/UnfoldOnlyProgressProviderTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/SelectedUserInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/user/domain/interactor/SelectedUserInteractorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/SelectedUserInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/user/domain/interactor/SelectedUserInteractorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/DeviceConfigProxyFakeTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/DeviceConfigProxyFakeTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/util/DeviceConfigProxyFakeTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/util/DeviceConfigProxyFakeTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/FakeSharedPreferencesTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/FakeSharedPreferencesTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/util/FakeSharedPreferencesTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/util/FakeSharedPreferencesTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/FloatingContentCoordinatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/FloatingContentCoordinatorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/util/FloatingContentCoordinatorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/util/FloatingContentCoordinatorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/RingerModeLiveDataTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/RingerModeLiveDataTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/util/RingerModeLiveDataTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/util/RingerModeLiveDataTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/WallpaperControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/WallpaperControllerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/util/WallpaperControllerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/util/WallpaperControllerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/animation/AnimationUtilTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/animation/AnimationUtilTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/util/animation/AnimationUtilTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/util/animation/AnimationUtilTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeExecutorTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/concurrency/FakeExecutorTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeExecutorTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/util/concurrency/FakeExecutorTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/MessageRouterImplTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/concurrency/MessageRouterImplTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/util/concurrency/MessageRouterImplTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/util/concurrency/MessageRouterImplTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/MockExecutorHandlerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/concurrency/MockExecutorHandlerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/util/concurrency/MockExecutorHandlerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/util/concurrency/MockExecutorHandlerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/RepeatableExecutorTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/concurrency/RepeatableExecutorTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/util/concurrency/RepeatableExecutorTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/util/concurrency/RepeatableExecutorTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionalCoreStartableTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/condition/ConditionalCoreStartableTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionalCoreStartableTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/util/condition/ConditionalCoreStartableTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/drawable/DrawableSizeTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/drawable/DrawableSizeTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/util/drawable/DrawableSizeTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/util/drawable/DrawableSizeTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/drawable/LoopedAnimatable2DrawableWrapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/drawable/LoopedAnimatable2DrawableWrapperTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/util/drawable/LoopedAnimatable2DrawableWrapperTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/util/drawable/LoopedAnimatable2DrawableWrapperTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/icons/AppCategoryIconProviderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/icons/AppCategoryIconProviderTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/util/icons/AppCategoryIconProviderTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/util/icons/AppCategoryIconProviderTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/FlowProvider.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/kotlin/FlowProvider.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/util/kotlin/FlowProvider.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/util/kotlin/FlowProvider.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/IpcSerializerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/kotlin/IpcSerializerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/util/kotlin/IpcSerializerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/util/kotlin/IpcSerializerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/SuspendUtilTests.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/kotlin/SuspendUtilTests.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/util/kotlin/SuspendUtilTests.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/util/kotlin/SuspendUtilTests.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/leak/LeakReporterTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/leak/LeakReporterTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/util/leak/LeakReporterTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/util/leak/LeakReporterTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/ref/GcWeakReference.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/ref/GcWeakReference.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/util/ref/GcWeakReference.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/util/ref/GcWeakReference.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/PostureDependentProximitySensorTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/sensors/PostureDependentProximitySensorTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/util/sensors/PostureDependentProximitySensorTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/util/sensors/PostureDependentProximitySensorTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorImplDualTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/sensors/ProximitySensorImplDualTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorImplDualTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/util/sensors/ProximitySensorImplDualTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorImplSingleTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/sensors/ProximitySensorImplSingleTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorImplSingleTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/util/sensors/ProximitySensorImplSingleTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/service/ObservableServiceConnectionTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/service/ObservableServiceConnectionTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/util/service/ObservableServiceConnectionTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/util/service/ObservableServiceConnectionTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/service/PackageObserverTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/service/PackageObserverTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/util/service/PackageObserverTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/util/service/PackageObserverTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettingsTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/settings/FakeSettingsTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettingsTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/util/settings/FakeSettingsTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/settings/SettingsProxyTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/settings/SettingsProxyTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/util/settings/SettingsProxyTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/util/settings/SettingsProxyTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/settings/UserSettingsProxyTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/settings/UserSettingsProxyTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/util/settings/UserSettingsProxyTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/util/settings/UserSettingsProxyTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/settings/repository/UserAwareSecureSettingsRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/settings/repository/UserAwareSecureSettingsRepositoryTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/util/settings/repository/UserAwareSecureSettingsRepositoryTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/util/settings/repository/UserAwareSecureSettingsRepositoryTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/ui/AnimatedValueTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/ui/AnimatedValueTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/util/ui/AnimatedValueTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/util/ui/AnimatedValueTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/view/ViewUtilTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/view/ViewUtilTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/util/view/ViewUtilTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/util/view/ViewUtilTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/wakelock/SettableWakeLockTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/wakelock/SettableWakeLockTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/util/wakelock/SettableWakeLockTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/util/wakelock/SettableWakeLockTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/wakelock/WakeLockTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/wakelock/WakeLockTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/util/wakelock/WakeLockTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/util/wakelock/WakeLockTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeControllerAdapterTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/VolumeControllerAdapterTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/volume/VolumeControllerAdapterTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/volume/VolumeControllerAdapterTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/data/repository/VolumeDialogRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/data/repository/VolumeDialogRepositoryTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/volume/data/repository/VolumeDialogRepositoryTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/volume/data/repository/VolumeDialogRepositoryTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/domain/interactor/VolumeDialogInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/interactor/VolumeDialogInteractorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/volume/domain/interactor/VolumeDialogInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/interactor/VolumeDialogInteractorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/WalletContextualLocationsServiceTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/wallet/controller/WalletContextualLocationsServiceTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/wallet/controller/WalletContextualLocationsServiceTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/wallet/controller/WalletContextualLocationsServiceTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/WalletContextualSuggestionsControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/wallet/controller/WalletContextualSuggestionsControllerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/wallet/controller/WalletContextualSuggestionsControllerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/wallet/controller/WalletContextualSuggestionsControllerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallet/util/WalletCardUtilsTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/wallet/util/WalletCardUtilsTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/wallet/util/WalletCardUtilsTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/wallet/util/WalletCardUtilsTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallpapers/ImageWallpaperTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/ImageWallpaperTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/wallpapers/ImageWallpaperTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/ImageWallpaperTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallpapers/WallpaperLocalColorExtractorTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/WallpaperLocalColorExtractorTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/wallpapers/WallpaperLocalColorExtractorTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/WallpaperLocalColorExtractorTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallpapers/data/repository/FakeWallpaperRepository.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/data/repository/FakeWallpaperRepository.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/wallpapers/data/repository/FakeWallpaperRepository.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/data/repository/FakeWallpaperRepository.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTestActivity.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/wmshell/BubblesTestActivity.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTestActivity.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/wmshell/BubblesTestActivity.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/SyncExecutor.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/wmshell/SyncExecutor.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/wmshell/SyncExecutor.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/wmshell/SyncExecutor.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/wmshell/TestableBubbleController.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/wmshell/TestableBubbleController.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubblePositioner.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/wmshell/TestableBubblePositioner.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubblePositioner.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/wmshell/TestableBubblePositioner.java
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockProviderPlugin.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockProviderPlugin.kt
index 4812ff0..8dc4815 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockProviderPlugin.kt
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockProviderPlugin.kt
@@ -69,11 +69,7 @@
     val events: ClockEvents
 
     /** Initializes various rendering parameters. If never called, provides reasonable defaults. */
-    fun initialize(
-        resources: Resources,
-        dozeFraction: Float,
-        foldFraction: Float,
-    )
+    fun initialize(resources: Resources, dozeFraction: Float, foldFraction: Float)
 
     /** Optional method for dumping debug information */
     fun dump(pw: PrintWriter)
@@ -109,11 +105,7 @@
     val largeClockMessageBuffer: MessageBuffer,
 )
 
-data class AodClockBurnInModel(
-    val scale: Float,
-    val translationX: Float,
-    val translationY: Float,
-)
+data class AodClockBurnInModel(val scale: Float, val translationX: Float, val translationY: Float)
 
 /** Specifies layout information for the */
 interface ClockFaceLayout {
@@ -180,8 +172,20 @@
 
     /** Call with zen/dnd information */
     fun onZenDataChanged(data: ZenData)
+
+    /** Update reactive axes for this clock */
+    fun onReactiveAxesChanged(axes: List<ClockReactiveSetting>)
 }
 
+/** Axis setting value for a clock */
+data class ClockReactiveSetting(
+    /** Axis key; matches ClockReactiveAxis.key */
+    val key: String,
+
+    /** Value to set this axis to */
+    val value: Float,
+)
+
 /** Methods which trigger various clock animations */
 interface ClockAnimations {
     /** Runs an enter animation (if any) */
@@ -264,9 +268,7 @@
 }
 
 /** Some data about a clock design */
-data class ClockMetadata(
-    val clockId: ClockId,
-)
+data class ClockMetadata(val clockId: ClockId)
 
 data class ClockPickerConfig(
     val id: String,
@@ -283,10 +285,46 @@
     /** True if the clock will react to tone changes in the seed color */
     val isReactiveToTone: Boolean = true,
 
-    /** True if the clock is capable of chagning style in reaction to touches */
+    /** True if the clock is capable of changing style in reaction to touches */
     val isReactiveToTouch: Boolean = false,
+
+    /** Font axes that can be modified on this clock */
+    val axes: List<ClockReactiveAxis> = listOf(),
 )
 
+/** Represents an Axis that can be modified */
+data class ClockReactiveAxis(
+    /** Axis key, not user renderable */
+    val key: String,
+
+    /** Intended mode of user interaction */
+    val type: AxisType,
+
+    /** Maximum value the axis supports */
+    val maxValue: Float,
+
+    /** Minimum value the axis supports */
+    val minValue: Float,
+
+    /** Current value the axis is set to */
+    val currentValue: Float,
+
+    /** User-renderable name of the axis */
+    val name: String,
+
+    /** Description of the axis */
+    val description: String,
+)
+
+/** Axis user interaction modes */
+enum class AxisType {
+    /** Boolean toggle. Swaps between minValue & maxValue */
+    Toggle,
+
+    /** Continuous slider between minValue & maxValue */
+    Slider,
+}
+
 /** Render configuration for the full clock. Modifies the way systemUI behaves with this clock. */
 data class ClockConfig(
     val id: String,
@@ -300,7 +338,8 @@
     /** Transition to AOD should move smartspace like large clock instead of small clock */
     val useAlternateSmartspaceAODTransition: Boolean = false,
 
-    @Deprecated("TODO(b/352049256): Remove")
+    /** Use ClockPickerConfig.isReactiveToTone instead */
+    @Deprecated("TODO(b/352049256): Remove in favor of ClockPickerConfig.isReactiveToTone")
     val isReactiveToTone: Boolean = true,
 
     /** True if the clock is large frame clock, which will use weather in compose. */
@@ -331,6 +370,7 @@
 data class ClockSettings(
     val clockId: ClockId? = null,
     val seedColor: Int? = null,
+    val axes: List<ClockReactiveSetting>? = null,
 ) {
     // Exclude metadata from equality checks
     var metadata: JSONObject = JSONObject()
@@ -345,6 +385,8 @@
                 return ""
             }
 
+            // TODO(b/364673977): Serialize axes
+
             return JSONObject()
                 .put(KEY_CLOCK_ID, setting.clockId)
                 .put(KEY_SEED_COLOR, setting.seedColor)
@@ -357,11 +399,13 @@
                 return null
             }
 
+            // TODO(b/364673977): Deserialize axes
+
             val json = JSONObject(jsonStr)
             val result =
                 ClockSettings(
                     if (!json.isNull(KEY_CLOCK_ID)) json.getString(KEY_CLOCK_ID) else null,
-                    if (!json.isNull(KEY_SEED_COLOR)) json.getInt(KEY_SEED_COLOR) else null
+                    if (!json.isNull(KEY_SEED_COLOR)) json.getInt(KEY_SEED_COLOR) else null,
                 )
             if (!json.isNull(KEY_METADATA)) {
                 result.metadata = json.getJSONObject(KEY_METADATA)
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardConstants.java b/packages/SystemUI/src/com/android/keyguard/KeyguardConstants.java
index a5b62b6..b16c683 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardConstants.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardConstants.java
@@ -28,5 +28,4 @@
      * be used temporarily for debugging.
      */
     public static final boolean DEBUG = Log.isLoggable("Keyguard", Log.DEBUG);
-    public static final boolean DEBUG_SIM_STATES = true;
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
index 1c1acf8..52c93f7 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
@@ -53,7 +53,6 @@
         extends KeyguardPinBasedInputViewController<KeyguardSimPinView> {
     public static final String TAG = "KeyguardSimPinView";
     private static final String LOG_TAG = "KeyguardSimPinView";
-    private static final boolean DEBUG = KeyguardConstants.DEBUG_SIM_STATES;
     private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
     private final TelephonyManager mTelephonyManager;
 
@@ -71,7 +70,7 @@
     KeyguardUpdateMonitorCallback mUpdateMonitorCallback = new KeyguardUpdateMonitorCallback() {
         @Override
         public void onSimStateChanged(int subId, int slotId, int simState) {
-            if (DEBUG) Log.v(TAG, "onSimStateChanged(subId=" + subId + ",state=" + simState + ")");
+            Log.v(TAG, "onSimStateChanged(subId=" + subId + ",state=" + simState + ")");
             // If subId has gone to PUK required then we need to go to the PUK screen.
             if (subId == mSubId && simState == TelephonyManager.SIM_STATE_PUK_REQUIRED) {
                 getKeyguardSecurityCallback().showCurrentSecurityScreen();
@@ -129,7 +128,7 @@
     @Override
     void resetState() {
         super.resetState();
-        if (DEBUG) Log.v(TAG, "Resetting state");
+        Log.v(TAG, "Resetting state");
         handleSubInfoChangeIfNeeded();
         mMessageAreaController.setMessage("");
         if (mShowDefaultMessage) {
@@ -216,11 +215,9 @@
                                 mMessageAreaController.setMessage(mView.getResources().getString(
                                         R.string.kg_password_pin_failed));
                             }
-                            if (DEBUG) {
-                                Log.d(LOG_TAG, "verifyPasswordAndUnlock "
-                                        + " CheckSimPin.onSimCheckResponse: " + result
-                                        + " attemptsRemaining=" + result.getAttemptsRemaining());
-                            }
+                            Log.d(LOG_TAG, "verifyPasswordAndUnlock "
+                                    + " CheckSimPin.onSimCheckResponse: " + result
+                                    + " attemptsRemaining=" + result.getAttemptsRemaining());
                         }
                         getKeyguardSecurityCallback().userActivity();
                         mCheckSimPinThread = null;
@@ -280,10 +277,8 @@
             displayMessage = mView.getResources()
                     .getString(R.string.kg_sim_lock_esim_instructions, displayMessage);
         }
-        if (DEBUG) {
-            Log.d(LOG_TAG, "getPinPasswordErrorMessage: attemptsRemaining="
-                    + attemptsRemaining + " displayMessage=" + displayMessage);
-        }
+        Log.d(LOG_TAG, "getPinPasswordErrorMessage: attemptsRemaining="
+                + attemptsRemaining + " displayMessage=" + displayMessage);
         return displayMessage;
     }
 
@@ -323,14 +318,10 @@
 
         @Override
         public void run() {
-            if (DEBUG) {
-                Log.v(TAG, "call supplyIccLockPin(subid=" + mSubId + ")");
-            }
+            Log.v(TAG, "call supplyIccLockPin(subid=" + mSubId + ")");
             TelephonyManager telephonyManager = mTelephonyManager.createForSubscriptionId(mSubId);
             final PinResult result = telephonyManager.supplyIccLockPin(mPin);
-            if (DEBUG) {
-                Log.v(TAG, "supplyIccLockPin returned: " + result.toString());
-            }
+            Log.v(TAG, "supplyIccLockPin returned: " + result.toString());
             mView.post(() -> onSimCheckResponse(result));
         }
     }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index f731186..22130f8 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -43,6 +43,7 @@
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW;
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT;
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN;
+import static com.android.systemui.Flags.simPinBouncerReset;
 import static com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_OPENED;
 
 import android.annotation.AnyThread;
@@ -1703,6 +1704,9 @@
                         intent.getStringExtra(Intent.EXTRA_SIM_STATE),
                         args.slotId,
                         args.subId);
+                if (args.slotId == SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
+                    return;
+                }
                 mHandler.obtainMessage(MSG_SIM_STATE_CHANGE, args.subId, args.slotId, args.simState)
                         .sendToTarget();
             } else if (TelephonyManager.ACTION_PHONE_STATE_CHANGED.equals(action)) {
@@ -1940,7 +1944,13 @@
             }
             int state = TelephonyManager.SIM_STATE_UNKNOWN;
             String stateExtra = intent.getStringExtra(Intent.EXTRA_SIM_STATE);
-            int slotId = intent.getIntExtra(SubscriptionManager.EXTRA_SLOT_INDEX, 0);
+
+            int defaultSlotId = 0;
+            if (simPinBouncerReset()) {
+                defaultSlotId = SubscriptionManager.INVALID_SIM_SLOT_INDEX;
+            }
+            int slotId = intent.getIntExtra(SubscriptionManager.EXTRA_SLOT_INDEX,
+                    defaultSlotId);
             int subId = intent.getIntExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX,
                     SubscriptionManager.INVALID_SUBSCRIPTION_ID);
             if (Intent.SIM_STATE_ABSENT.equals(stateExtra)) {
@@ -2479,6 +2489,12 @@
                     this::onTransitionStateChanged
             );
         }
+
+        // start() can be invoked in the middle of user switching, so check for this state and issue
+        // the call manually as that important event was missed.
+        if (mUserTracker.isUserSwitching()) {
+            handleUserSwitching(mUserTracker.getUserId(), () -> {});
+        }
     }
 
     @VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/display/ui/viewmodel/ConnectingDisplayViewModel.kt b/packages/SystemUI/src/com/android/systemui/display/ui/viewmodel/ConnectingDisplayViewModel.kt
index 81ea2e7..62720a5 100644
--- a/packages/SystemUI/src/com/android/systemui/display/ui/viewmodel/ConnectingDisplayViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/ui/viewmodel/ConnectingDisplayViewModel.kt
@@ -79,7 +79,7 @@
             .combine(concurrentDisplaysInProgessFlow) { pendingDisplay, concurrentDisplaysInProgress
                 ->
                 if (pendingDisplay == null) {
-                    hideDialog()
+                    dismissDialog()
                 } else {
                     showDialog(pendingDisplay, concurrentDisplaysInProgress)
                 }
@@ -88,17 +88,17 @@
     }
 
     private fun showDialog(pendingDisplay: PendingDisplay, concurrentDisplaysInProgess: Boolean) {
-        hideDialog()
+        dismissDialog()
         dialog =
             bottomSheetFactory
                 .createDialog(
                     onStartMirroringClickListener = {
                         scope.launch(bgDispatcher) { pendingDisplay.enable() }
-                        hideDialog()
+                        dismissDialog()
                     },
                     onCancelMirroring = {
                         scope.launch(bgDispatcher) { pendingDisplay.ignore() }
-                        hideDialog()
+                        dismissDialog()
                     },
                     navbarBottomInsetsProvider = { Utils.getNavbarInsets(context).bottom },
                     showConcurrentDisplayInfo = concurrentDisplaysInProgess
@@ -106,8 +106,8 @@
                 .apply { show() }
     }
 
-    private fun hideDialog() {
-        dialog?.hide()
+    private fun dismissDialog() {
+        dialog?.dismiss()
         dialog = null
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/CustomizationProvider.kt b/packages/SystemUI/src/com/android/systemui/keyguard/CustomizationProvider.kt
index 342325f..6df8355 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/CustomizationProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/CustomizationProvider.kt
@@ -54,29 +54,25 @@
             addURI(
                 Contract.AUTHORITY,
                 Contract.LockScreenQuickAffordances.qualifiedTablePath(
-                    Contract.LockScreenQuickAffordances.SlotTable.TABLE_NAME,
+                    Contract.LockScreenQuickAffordances.SlotTable.TABLE_NAME
                 ),
                 MATCH_CODE_ALL_SLOTS,
             )
             addURI(
                 Contract.AUTHORITY,
                 Contract.LockScreenQuickAffordances.qualifiedTablePath(
-                    Contract.LockScreenQuickAffordances.AffordanceTable.TABLE_NAME,
+                    Contract.LockScreenQuickAffordances.AffordanceTable.TABLE_NAME
                 ),
                 MATCH_CODE_ALL_AFFORDANCES,
             )
             addURI(
                 Contract.AUTHORITY,
                 Contract.LockScreenQuickAffordances.qualifiedTablePath(
-                    Contract.LockScreenQuickAffordances.SelectionTable.TABLE_NAME,
+                    Contract.LockScreenQuickAffordances.SelectionTable.TABLE_NAME
                 ),
                 MATCH_CODE_ALL_SELECTIONS,
             )
-            addURI(
-                Contract.AUTHORITY,
-                Contract.FlagsTable.TABLE_NAME,
-                MATCH_CODE_ALL_FLAGS,
-            )
+            addURI(Contract.AUTHORITY, Contract.FlagsTable.TABLE_NAME, MATCH_CODE_ALL_FLAGS)
         }
 
     override fun onCreate(): Boolean {
@@ -106,15 +102,15 @@
             when (uriMatcher.match(uri)) {
                 MATCH_CODE_ALL_SLOTS ->
                     Contract.LockScreenQuickAffordances.qualifiedTablePath(
-                        Contract.LockScreenQuickAffordances.SlotTable.TABLE_NAME,
+                        Contract.LockScreenQuickAffordances.SlotTable.TABLE_NAME
                     )
                 MATCH_CODE_ALL_AFFORDANCES ->
                     Contract.LockScreenQuickAffordances.qualifiedTablePath(
-                        Contract.LockScreenQuickAffordances.AffordanceTable.TABLE_NAME,
+                        Contract.LockScreenQuickAffordances.AffordanceTable.TABLE_NAME
                     )
                 MATCH_CODE_ALL_SELECTIONS ->
                     Contract.LockScreenQuickAffordances.qualifiedTablePath(
-                        Contract.LockScreenQuickAffordances.SelectionTable.TABLE_NAME,
+                        Contract.LockScreenQuickAffordances.SelectionTable.TABLE_NAME
                     )
                 MATCH_CODE_ALL_FLAGS -> Contract.FlagsTable.TABLE_NAME
                 else -> null
@@ -128,6 +124,7 @@
     }
 
     override fun insert(uri: Uri, values: ContentValues?): Uri? {
+        if (!::mainDispatcher.isInitialized) return null
         if (uriMatcher.match(uri) != MATCH_CODE_ALL_SELECTIONS) {
             throw UnsupportedOperationException()
         }
@@ -142,6 +139,7 @@
         selectionArgs: Array<out String>?,
         sortOrder: String?,
     ): Cursor? {
+        if (!::mainDispatcher.isInitialized) return null
         return runBlocking("$TAG#query", mainDispatcher) {
             when (uriMatcher.match(uri)) {
                 MATCH_CODE_ALL_AFFORDANCES -> queryAffordances()
@@ -163,11 +161,8 @@
         return 0
     }
 
-    override fun delete(
-        uri: Uri,
-        selection: String?,
-        selectionArgs: Array<out String>?,
-    ): Int {
+    override fun delete(uri: Uri, selection: String?, selectionArgs: Array<out String>?): Int {
+        if (!::mainDispatcher.isInitialized) return 0
         if (uriMatcher.match(uri) != MATCH_CODE_ALL_SELECTIONS) {
             throw UnsupportedOperationException()
         }
@@ -232,11 +227,7 @@
             throw IllegalArgumentException("Cannot insert selection, affordance ID was empty!")
         }
 
-        val success =
-            interactor.select(
-                slotId = slotId,
-                affordanceId = affordanceId,
-            )
+        val success = interactor.select(slotId = slotId, affordanceId = affordanceId)
 
         return if (success) {
             Log.d(TAG, "Successfully selected $affordanceId for slot $slotId")
@@ -318,22 +309,14 @@
             )
             .apply {
                 interactor.getSlotPickerRepresentations().forEach { representation ->
-                    addRow(
-                        arrayOf(
-                            representation.id,
-                            representation.maxSelectedAffordances,
-                        )
-                    )
+                    addRow(arrayOf(representation.id, representation.maxSelectedAffordances))
                 }
             }
     }
 
     private suspend fun queryFlags(): Cursor {
         return MatrixCursor(
-                arrayOf(
-                    Contract.FlagsTable.Columns.NAME,
-                    Contract.FlagsTable.Columns.VALUE,
-                )
+                arrayOf(Contract.FlagsTable.Columns.NAME, Contract.FlagsTable.Columns.VALUE)
             )
             .apply {
                 interactor.getPickerFlags().forEach { flag ->
@@ -351,10 +334,7 @@
             }
     }
 
-    private suspend fun deleteSelection(
-        uri: Uri,
-        selectionArgs: Array<out String>?,
-    ): Int {
+    private suspend fun deleteSelection(uri: Uri, selectionArgs: Array<out String>?): Int {
         if (selectionArgs == null) {
             throw IllegalArgumentException(
                 "Cannot delete selection, selection arguments not included!"
@@ -372,11 +352,7 @@
                     )
             }
 
-        val deleted =
-            interactor.unselect(
-                slotId = slotId,
-                affordanceId = affordanceId,
-            )
+        val deleted = interactor.unselect(slotId = slotId, affordanceId = affordanceId)
 
         return if (deleted) {
             Log.d(TAG, "Successfully unselected $affordanceId for slot $slotId")
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 3b1569d..1a0525d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -42,6 +42,7 @@
 import static com.android.systemui.DejankUtils.whitelistIpcs;
 import static com.android.systemui.Flags.notifyPowerManagerUserActivityBackground;
 import static com.android.systemui.Flags.relockWithPowerButtonImmediately;
+import static com.android.systemui.Flags.simPinBouncerReset;
 import static com.android.systemui.Flags.translucentOccludingActivityFix;
 import static com.android.systemui.keyguard.ui.viewmodel.LockscreenToDreamingTransitionViewModel.DREAMING_ANIMATION_DURATION_MS;
 
@@ -238,7 +239,6 @@
     private static final long KEYGUARD_DONE_PENDING_TIMEOUT_MS = 3000;
 
     private static final boolean DEBUG = KeyguardConstants.DEBUG;
-    private static final boolean DEBUG_SIM_STATES = KeyguardConstants.DEBUG_SIM_STATES;
 
     private final static String TAG = "KeyguardViewMediator";
 
@@ -649,11 +649,8 @@
 
         @Override
         public void onSimStateChanged(int subId, int slotId, int simState) {
-
-            if (DEBUG_SIM_STATES) {
-                Log.d(TAG, "onSimStateChanged(subId=" + subId + ", slotId=" + slotId
-                        + ",state=" + simState + ")");
-            }
+            Log.d(TAG, "onSimStateChanged(subId=" + subId + ", slotId=" + slotId
+                    + ",state=" +  TelephonyManager.simStateToString(simState) + ")");
 
             int size = mKeyguardStateCallbacks.size();
             boolean simPinSecure = mUpdateMonitor.isSimPinSecure();
@@ -686,7 +683,7 @@
                     synchronized (KeyguardViewMediator.this) {
                         if (shouldWaitForProvisioning()) {
                             if (!mShowing) {
-                                if (DEBUG_SIM_STATES) Log.d(TAG, "ICC_ABSENT isn't showing,"
+                                Log.d(TAG, "ICC_ABSENT isn't showing,"
                                         + " we need to show the keyguard since the "
                                         + "device isn't provisioned yet.");
                                 doKeyguardLocked(null);
@@ -698,11 +695,21 @@
                             // MVNO SIMs can become transiently NOT_READY when switching networks,
                             // so we should only lock when they are ABSENT.
                             if (lastSimStateWasLocked) {
-                                if (DEBUG_SIM_STATES) Log.d(TAG, "SIM moved to ABSENT when the "
+                                Log.d(TAG, "SIM moved to ABSENT when the "
                                         + "previous state was locked. Reset the state.");
                                 resetStateLocked();
                             }
                             mSimWasLocked.append(slotId, false);
+                        } else if (simState == TelephonyManager.SIM_STATE_NOT_READY) {
+                            if (simPinBouncerReset()) {
+                                // Support eSIM disablement, and do not clear `mSimWasLocked`.
+                                // NOT_READY could just be a temporary state
+                                if (lastSimStateWasLocked) {
+                                    Log.d(TAG, "SIM moved to NOT_READY when the "
+                                            + "previous state was locked. Reset the state.");
+                                    resetStateLocked();
+                                }
+                            }
                         }
                     }
                     break;
@@ -712,7 +719,7 @@
                         mSimWasLocked.append(slotId, true);
                         mPendingPinLock = true;
                         if (!mShowing) {
-                            if (DEBUG_SIM_STATES) Log.d(TAG,
+                            Log.d(TAG,
                                     "INTENT_VALUE_ICC_LOCKED and keygaurd isn't "
                                     + "showing; need to show keyguard so user can enter sim pin");
                             doKeyguardLocked(null);
@@ -724,11 +731,11 @@
                 case TelephonyManager.SIM_STATE_PERM_DISABLED:
                     synchronized (KeyguardViewMediator.this) {
                         if (!mShowing) {
-                            if (DEBUG_SIM_STATES) Log.d(TAG, "PERM_DISABLED and "
+                            Log.d(TAG, "PERM_DISABLED and "
                                   + "keygaurd isn't showing.");
                             doKeyguardLocked(null);
                         } else {
-                            if (DEBUG_SIM_STATES) Log.d(TAG, "PERM_DISABLED, resetStateLocked to"
+                            Log.d(TAG, "PERM_DISABLED, resetStateLocked to"
                                   + "show permanently disabled message in lockscreen.");
                             resetStateLocked();
                         }
@@ -736,9 +743,9 @@
                     break;
                 case TelephonyManager.SIM_STATE_READY:
                     synchronized (KeyguardViewMediator.this) {
-                        if (DEBUG_SIM_STATES) Log.d(TAG, "READY, reset state? " + mShowing);
+                        Log.d(TAG, "READY, reset state? " + mShowing);
                         if (mShowing && mSimWasLocked.get(slotId, false)) {
-                            if (DEBUG_SIM_STATES) Log.d(TAG, "SIM moved to READY when the "
+                            Log.d(TAG, "SIM moved to READY when the "
                                     + "previously was locked. Reset the state.");
                             mSimWasLocked.append(slotId, false);
                             resetStateLocked();
@@ -746,7 +753,7 @@
                     }
                     break;
                 default:
-                    if (DEBUG_SIM_STATES) Log.v(TAG, "Unspecific state: " + simState);
+                    Log.v(TAG, "Unspecific state: " + simState);
                     break;
             }
         }
@@ -1682,6 +1689,12 @@
                     });
             mJavaAdapter.alwaysCollectFlow(communalViewModel.getTransitionFromOccludedEnded(),
                     getFinishedCallbackConsumer());
+
+            // System ready can be invoked in the middle of user switching, so check for this state
+            // and issue the call manually as that important event was missed.
+            if (mUserTracker.isUserSwitching()) {
+                mUpdateCallback.onUserSwitching(mUserTracker.getUserId());
+            }
         }
         // Most services aren't available until the system reaches the ready state, so we
         // send it here when the device first boots.
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractor.kt
index d7e6bdb..4457f1d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractor.kt
@@ -23,10 +23,12 @@
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.keyguard.DismissCallbackRegistry
+import com.android.systemui.keyguard.KeyguardWmStateRefactor
 import com.android.systemui.keyguard.data.repository.KeyguardRepository
 import com.android.systemui.keyguard.data.repository.TrustRepository
 import com.android.systemui.keyguard.shared.model.DismissAction
 import com.android.systemui.keyguard.shared.model.KeyguardDone
+import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.power.domain.interactor.PowerInteractor
 import com.android.systemui.user.domain.interactor.SelectedUserInteractor
 import com.android.systemui.util.kotlin.Utils.Companion.toQuad
@@ -35,6 +37,7 @@
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.collect
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.filter
 import kotlinx.coroutines.flow.map
@@ -56,6 +59,7 @@
     trustRepository: TrustRepository,
     alternateBouncerInteractor: AlternateBouncerInteractor,
     powerInteractor: PowerInteractor,
+    keyguardTransitionInteractor: KeyguardTransitionInteractor,
 ) {
     /*
      * Updates when a biometric has authenticated the device and is requesting to dismiss
@@ -76,9 +80,9 @@
                     primaryBouncerInteractor.isShowing,
                     alternateBouncerInteractor.isVisible,
                     powerInteractor.isInteractive,
-                    ::Triple
+                    ::Triple,
                 ),
-                ::toQuad
+                ::toQuad,
             )
             .filter { (trustModel, primaryBouncerShowing, altBouncerShowing, interactive) ->
                 val bouncerShowing = primaryBouncerShowing || altBouncerShowing
@@ -144,9 +148,7 @@
      *
      * TODO(b/358412565): Support dismiss messages.
      */
-    fun dismissKeyguardWithCallback(
-        callback: IKeyguardDismissCallback?,
-    ) {
+    fun dismissKeyguardWithCallback(callback: IKeyguardDismissCallback?) {
         scope.launch {
             withContext(mainDispatcher) {
                 if (callback != null) {
@@ -163,4 +165,14 @@
             }
         }
     }
+
+    init {
+        if (KeyguardWmStateRefactor.isEnabled) {
+            scope.launch {
+                keyguardTransitionInteractor.currentKeyguardState
+                    .filter { it == KeyguardState.GONE }
+                    .collect { dismissCallbackRegistry.notifyDismissSucceeded() }
+            }
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt b/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt
index c2f1c3d..af167d4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt
@@ -158,7 +158,7 @@
     override fun onCreateView(
         inflater: LayoutInflater,
         container: ViewGroup?,
-        savedInstanceState: Bundle?
+        savedInstanceState: Bundle?,
     ): View {
         val context = inflater.context
         return ComposeView(context).apply {
@@ -181,7 +181,7 @@
                                     notificationScrimClippingParams.bottom,
                                     notificationScrimClippingParams.radius,
                                 )
-                            }
+                            },
                     ) {
                         AnimatedContent(targetState = qsState) {
                             when (it) {
@@ -272,7 +272,7 @@
         qsExpansionFraction: Float,
         panelExpansionFraction: Float,
         headerTranslation: Float,
-        squishinessFraction: Float
+        squishinessFraction: Float,
     ) {
         viewModel.qsExpansionValue = qsExpansionFraction
         viewModel.panelExpansionFractionValue = panelExpansionFraction
@@ -318,12 +318,12 @@
     override fun setTransitionToFullShadeProgress(
         isTransitioningToFullShade: Boolean,
         qsTransitionFraction: Float,
-        qsSquishinessFraction: Float
+        qsSquishinessFraction: Float,
     ) {
         super.setTransitionToFullShadeProgress(
             isTransitioningToFullShade,
             qsTransitionFraction,
-            qsSquishinessFraction
+            qsSquishinessFraction,
         )
     }
 
@@ -334,7 +334,7 @@
         bottom: Int,
         cornerRadius: Int,
         visible: Boolean,
-        fullWidth: Boolean
+        fullWidth: Boolean,
     ) {
         notificationScrimClippingParams.isEnabled = visible
         notificationScrimClippingParams.top = top
@@ -402,7 +402,7 @@
                 launch {
                     setListenerJob(
                         heightListener,
-                        viewModel.containerViewModel.editModeViewModel.isEditing
+                        viewModel.containerViewModel.editModeViewModel.isEditing,
                     ) {
                         onQsHeightChanged()
                     }
@@ -410,7 +410,7 @@
                 launch {
                     setListenerJob(
                         qsContainerController,
-                        viewModel.containerViewModel.editModeViewModel.isEditing
+                        viewModel.containerViewModel.editModeViewModel.isEditing,
                     ) {
                         setCustomizerShowing(it)
                     }
@@ -422,6 +422,7 @@
     @Composable
     private fun QuickQuickSettingsElement() {
         val qqsPadding by viewModel.qqsHeaderHeight.collectAsStateWithLifecycle()
+        val bottomPadding = dimensionResource(id = R.dimen.qqs_layout_padding_bottom)
         DisposableEffect(Unit) {
             qqsVisible.value = true
 
@@ -441,7 +442,7 @@
                             )
                         }
                         .onSizeChanged { size -> qqsHeight.value = size.height }
-                        .padding(top = { qqsPadding })
+                        .padding(top = { qqsPadding }, bottom = { bottomPadding.roundToPx() })
             ) {
                 val qsEnabled by viewModel.qsEnabled.collectAsStateWithLifecycle()
                 if (qsEnabled) {
@@ -450,7 +451,7 @@
                         modifier =
                             Modifier.collapseExpandSemanticAction(
                                 stringResource(id = R.string.accessibility_quick_settings_expand)
-                            )
+                            ),
                     )
                 }
             }
@@ -482,7 +483,7 @@
                     FooterActions(
                         viewModel = viewModel.footerActionsViewModel,
                         qsVisibilityLifecycleOwner = this@QSFragmentCompose,
-                        modifier = Modifier.sysuiResTag("qs_footer_actions")
+                        modifier = Modifier.sysuiResTag("qs_footer_actions"),
                     )
                 }
             }
@@ -562,7 +563,7 @@
 private suspend inline fun <Listener : Any, Data> setListenerJob(
     listenerFlow: MutableStateFlow<Listener?>,
     dataFlow: Flow<Data>,
-    crossinline onCollect: suspend Listener.(Data) -> Unit
+    crossinline onCollect: suspend Listener.(Data) -> Unit,
 ) {
     coroutineScope {
         try {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt
index eeb55ca..fde40da 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt
@@ -22,18 +22,16 @@
 import androidx.compose.runtime.DisposableEffect
 import androidx.compose.runtime.getValue
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.util.fastMap
 import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.systemui.compose.modifiers.sysuiResTag
 import com.android.systemui.qs.panels.ui.viewmodel.QuickQuickSettingsViewModel
 
 @Composable
-fun QuickQuickSettings(
-    viewModel: QuickQuickSettingsViewModel,
-    modifier: Modifier = Modifier,
-) {
+fun QuickQuickSettings(viewModel: QuickQuickSettingsViewModel, modifier: Modifier = Modifier) {
     val sizedTiles by
         viewModel.tileViewModels.collectAsStateWithLifecycle(initialValue = emptyList())
-    val tiles = sizedTiles.map { it.tile }
+    val tiles = sizedTiles.fastMap { it.tile }
 
     DisposableEffect(tiles) {
         val token = Any()
@@ -44,14 +42,18 @@
 
     TileLazyGrid(
         modifier = modifier.sysuiResTag("qqs_tile_layout"),
-        columns = GridCells.Fixed(columns)
+        columns = GridCells.Fixed(columns),
     ) {
         items(
-            tiles.size,
+            sizedTiles.size,
             key = { index -> sizedTiles[index].tile.spec.spec },
-            span = { index -> GridItemSpan(sizedTiles[index].width) }
+            span = { index -> GridItemSpan(sizedTiles[index].width) },
         ) { index ->
-            Tile(tile = tiles[index], iconOnly = sizedTiles[index].isIcon, modifier = Modifier)
+            Tile(
+                tile = sizedTiles[index].tile,
+                iconOnly = sizedTiles[index].isIcon,
+                modifier = Modifier,
+            )
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/Tile.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/Tile.kt
index 93037d1..afd47a7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/Tile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/Tile.kt
@@ -18,6 +18,7 @@
 
 package com.android.systemui.qs.panels.ui.compose
 
+import android.content.res.Resources
 import android.graphics.drawable.Animatable
 import android.service.quicksettings.Tile.STATE_ACTIVE
 import android.service.quicksettings.Tile.STATE_INACTIVE
@@ -71,6 +72,7 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.ReadOnlyComposable
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
@@ -85,13 +87,19 @@
 import androidx.compose.ui.graphics.Shape
 import androidx.compose.ui.layout.onGloballyPositioned
 import androidx.compose.ui.layout.positionInRoot
+import androidx.compose.ui.platform.LocalConfiguration
 import androidx.compose.ui.platform.LocalContext
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.res.dimensionResource
 import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.semantics.Role
+import androidx.compose.ui.semantics.clearAndSetSemantics
+import androidx.compose.ui.semantics.contentDescription
 import androidx.compose.ui.semantics.onClick
+import androidx.compose.ui.semantics.role
 import androidx.compose.ui.semantics.semantics
 import androidx.compose.ui.semantics.stateDescription
+import androidx.compose.ui.semantics.toggleableState
 import androidx.compose.ui.text.style.TextAlign
 import androidx.compose.ui.text.style.TextOverflow
 import androidx.compose.ui.unit.Dp
@@ -106,12 +114,15 @@
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.common.ui.compose.Icon
 import com.android.systemui.common.ui.compose.load
+import com.android.systemui.compose.modifiers.sysuiResTag
 import com.android.systemui.plugins.qs.QSTile
 import com.android.systemui.qs.panels.shared.model.SizedTile
 import com.android.systemui.qs.panels.shared.model.SizedTileImpl
+import com.android.systemui.qs.panels.ui.compose.TileDefaults.longPressLabel
 import com.android.systemui.qs.panels.ui.model.GridCell
 import com.android.systemui.qs.panels.ui.model.SpacerGridCell
 import com.android.systemui.qs.panels.ui.model.TileGridCell
+import com.android.systemui.qs.panels.ui.viewmodel.AccessibilityUiState
 import com.android.systemui.qs.panels.ui.viewmodel.EditTileViewModel
 import com.android.systemui.qs.panels.ui.viewmodel.TileUiState
 import com.android.systemui.qs.panels.ui.viewmodel.TileViewModel
@@ -126,15 +137,15 @@
 
 object TileType
 
+private const val TEST_TAG_SMALL = "qs_tile_small"
+private const val TEST_TAG_LARGE = "qs_tile_large"
+private const val TEST_TAG_TOGGLE = "qs_tile_toggle_target"
+
 @Composable
-fun Tile(
-    tile: TileViewModel,
-    iconOnly: Boolean,
-    showLabels: Boolean = false,
-    modifier: Modifier,
-) {
+fun Tile(tile: TileViewModel, iconOnly: Boolean, showLabels: Boolean = false, modifier: Modifier) {
     val state by tile.state.collectAsStateWithLifecycle(tile.currentState)
-    val uiState = remember(state) { state.toUiState() }
+    val resources = resources()
+    val uiState = remember(state, resources) { state.toUiState(resources) }
     val colors = TileDefaults.getColorForState(uiState)
 
     // TODO(b/361789146): Draw the shapes instead of clipping
@@ -150,6 +161,7 @@
         onClick = tile::onClick,
         onLongClick = tile::onLongClick,
         modifier = modifier.height(tileHeight()),
+        uiState = uiState,
     ) {
         val icon = getTileIcon(icon = uiState.icon)
         if (iconOnly) {
@@ -169,6 +181,7 @@
                     }
                 },
                 onLongClick = { tile.onLongClick(it) },
+                accessibilityUiState = uiState.accessibilityUiState,
             )
         }
     }
@@ -185,6 +198,7 @@
     onClick: (Expandable) -> Unit = {},
     onLongClick: (Expandable) -> Unit = {},
     modifier: Modifier = Modifier,
+    uiState: TileUiState? = null,
     content: @Composable BoxScope.(Expandable) -> Unit,
 ) {
     Column(
@@ -194,7 +208,7 @@
         modifier = modifier,
     ) {
         val backgroundColor =
-            if (iconOnly) {
+            if (iconOnly || uiState?.handlesSecondaryClick != true) {
                 colors.iconBackground
             } else {
                 colors.background
@@ -202,18 +216,43 @@
         Expandable(
             color = backgroundColor,
             shape = shape,
-            modifier = Modifier.height(tileHeight()).clip(shape)
+            modifier = Modifier.height(tileHeight()).clip(shape),
         ) {
+            val longPressLabel = longPressLabel()
             Box(
                 modifier =
                     Modifier.fillMaxSize()
                         .thenIf(clickEnabled) {
                             Modifier.combinedClickable(
                                 onClick = { onClick(it) },
-                                onLongClick = { onLongClick(it) }
+                                onLongClick = { onLongClick(it) },
+                                onClickLabel = uiState?.accessibilityUiState?.clickLabel,
+                                onLongClickLabel = longPressLabel,
                             )
                         }
-                        .tilePadding(),
+                        .thenIf(uiState != null) {
+                            uiState as TileUiState
+                            Modifier.semantics {
+                                    role = uiState.accessibilityUiState.accessibilityRole
+                                    if (
+                                        uiState.accessibilityUiState.accessibilityRole ==
+                                            Role.Switch
+                                    ) {
+                                        uiState.accessibilityUiState.toggleableState?.let {
+                                            toggleableState = it
+                                        }
+                                    }
+                                    stateDescription = uiState.accessibilityUiState.stateDescription
+                                }
+                                .sysuiResTag(if (iconOnly) TEST_TAG_SMALL else TEST_TAG_LARGE)
+                                .thenIf(iconOnly) {
+                                    Modifier.semantics {
+                                        contentDescription =
+                                            uiState.accessibilityUiState.contentDescription
+                                    }
+                                }
+                        }
+                        .tilePadding()
             ) {
                 content(it)
             }
@@ -238,21 +277,39 @@
     icon: Icon,
     colors: TileColors,
     iconShape: Shape,
+    accessibilityUiState: AccessibilityUiState? = null,
     toggleClickSupported: Boolean = false,
     onClick: () -> Unit = {},
     onLongClick: () -> Unit = {},
 ) {
     Row(
         verticalAlignment = Alignment.CenterVertically,
-        horizontalArrangement = tileHorizontalArrangement()
+        horizontalArrangement = tileHorizontalArrangement(),
     ) {
         // Icon
+        val longPressLabel = longPressLabel()
         Box(
             modifier =
                 Modifier.size(TileDefaults.ToggleTargetSize).thenIf(toggleClickSupported) {
                     Modifier.clip(iconShape)
                         .background(colors.iconBackground, { 1f })
-                        .combinedClickable(onClick = onClick, onLongClick = onLongClick)
+                        .combinedClickable(
+                            onClick = onClick,
+                            onLongClick = onLongClick,
+                            onLongClickLabel = longPressLabel,
+                        )
+                        .thenIf(accessibilityUiState != null) {
+                            accessibilityUiState as AccessibilityUiState
+                            Modifier.semantics {
+                                    contentDescription = accessibilityUiState.contentDescription
+                                    stateDescription = accessibilityUiState.stateDescription
+                                    accessibilityUiState.toggleableState?.let {
+                                        toggleableState = it
+                                    }
+                                    role = Role.Switch
+                                }
+                                .sysuiResTag(TEST_TAG_TOGGLE)
+                        }
                 }
         ) {
             TileIcon(icon = icon, color = colors.icon, modifier = Modifier.align(Alignment.Center))
@@ -260,16 +317,19 @@
 
         // Labels
         Column(verticalArrangement = Arrangement.Center, modifier = Modifier.fillMaxHeight()) {
-            Text(
-                label,
-                color = colors.label,
-                modifier = Modifier.tileMarquee(),
-            )
+            Text(label, color = colors.label, modifier = Modifier.tileMarquee())
             if (!TextUtils.isEmpty(secondaryLabel)) {
                 Text(
                     secondaryLabel ?: "",
                     color = colors.secondaryLabel,
-                    modifier = Modifier.tileMarquee(),
+                    modifier =
+                        Modifier.tileMarquee().thenIf(
+                            accessibilityUiState
+                                ?.stateDescription
+                                ?.contains(secondaryLabel ?: "") == true
+                        ) {
+                            Modifier.clearAndSetSemantics {}
+                        },
                 )
             }
         }
@@ -277,10 +337,7 @@
 }
 
 private fun Modifier.tileMarquee(): Modifier {
-    return basicMarquee(
-        iterations = 1,
-        initialDelayMillis = 200,
-    )
+    return basicMarquee(iterations = 1, initialDelayMillis = 200)
 }
 
 @Composable
@@ -320,11 +377,11 @@
         Column(
             verticalArrangement =
                 spacedBy(dimensionResource(id = R.dimen.qs_label_container_margin)),
-            modifier = modifier.fillMaxSize().verticalScroll(rememberScrollState())
+            modifier = modifier.fillMaxSize().verticalScroll(rememberScrollState()),
         ) {
             AnimatedContent(
                 targetState = currentListState.dragInProgress,
-                modifier = Modifier.wrapContentSize()
+                modifier = Modifier.wrapContentSize(),
             ) { dragIsInProgress ->
                 EditGridHeader(Modifier.dragAndDropRemoveZone(currentListState, onRemoveTile)) {
                     if (dragIsInProgress) {
@@ -348,12 +405,12 @@
             AnimatedVisibility(
                 visible = !currentListState.dragInProgress,
                 enter = fadeIn(),
-                exit = fadeOut()
+                exit = fadeOut(),
             ) {
                 Column(
                     verticalArrangement =
                         spacedBy(dimensionResource(id = R.dimen.qs_label_container_margin)),
-                    modifier = modifier.fillMaxSize()
+                    modifier = modifier.fillMaxSize(),
                 ) {
                     EditGridHeader { Text(text = "Hold and drag to add tiles.") }
 
@@ -381,14 +438,14 @@
 @Composable
 private fun EditGridHeader(
     modifier: Modifier = Modifier,
-    content: @Composable BoxScope.() -> Unit
+    content: @Composable BoxScope.() -> Unit,
 ) {
     CompositionLocalProvider(
         LocalContentColor provides MaterialTheme.colorScheme.onBackground.copy(alpha = .5f)
     ) {
         Box(
             contentAlignment = Alignment.Center,
-            modifier = modifier.fillMaxWidth().height(EditModeTileDefaults.EditGridHeaderHeight)
+            modifier = modifier.fillMaxWidth().height(EditModeTileDefaults.EditGridHeaderHeight),
         ) {
             content()
         }
@@ -403,7 +460,7 @@
         modifier =
             Modifier.fillMaxHeight()
                 .border(1.dp, LocalContentColor.current, shape = CircleShape)
-                .padding(10.dp)
+                .padding(10.dp),
     ) {
         Icon(imageVector = Icons.Default.Clear, contentDescription = null)
         Text(text = "Remove")
@@ -454,7 +511,7 @@
                         gridContentOffset = coordinates.positionInRoot()
                     }
                     .testTag(CURRENT_TILES_GRID_TEST_TAG),
-            columns = GridCells.Fixed(columns)
+            columns = GridCells.Fixed(columns),
         ) {
             editTiles(
                 listState.tiles,
@@ -488,7 +545,7 @@
     // Available tiles
     TileLazyGrid(
         modifier = Modifier.height(availableGridHeight).testTag(AVAILABLE_TILES_GRID_TEST_TAG),
-        columns = GridCells.Fixed(columns)
+        columns = GridCells.Fixed(columns),
     ) {
         groupedTiles.forEach { category, tiles ->
             stickyHeader {
@@ -498,7 +555,7 @@
                     color = labelColors.label,
                     modifier =
                         Modifier.background(Color.Black)
-                            .padding(start = 16.dp, bottom = 8.dp, top = 8.dp)
+                            .padding(start = 16.dp, bottom = 8.dp, top = 8.dp),
                 )
             }
             editTiles(
@@ -542,7 +599,7 @@
         count = cells.size,
         key = { cells[it].key(it, dragAndDropState) },
         span = { cells[it].span },
-        contentType = { TileType }
+        contentType = { TileType },
     ) { index ->
         when (val cell = cells[index]) {
             is TileGridCell ->
@@ -552,7 +609,7 @@
                         Modifier.background(
                                 color = MaterialTheme.colorScheme.secondary,
                                 alpha = { EditModeTileDefaults.PLACEHOLDER_ALPHA },
-                                shape = RoundedCornerShape(TileDefaults.InactiveCornerRadius)
+                                shape = RoundedCornerShape(TileDefaults.InactiveCornerRadius),
                             )
                             .animateItem()
                     )
@@ -565,7 +622,7 @@
                         onClick = onClick,
                         onResize = onResize,
                         showLabels = showLabels,
-                        indicatePosition = indicatePosition
+                        indicatePosition = indicatePosition,
                     )
                 }
             is SpacerGridCell -> SpacerGridCell()
@@ -604,7 +661,7 @@
         modifier =
             Modifier.height(tileHeight)
                 .animateItem()
-                .semantics {
+                .semantics(mergeDescendants = true) {
                     onClick(onClickActionName) { false }
                     this.stateDescription = stateDescription
                 }
@@ -613,7 +670,7 @@
                     onClick,
                     onResize,
                     dragAndDropState,
-                )
+                ),
     )
 }
 
@@ -645,7 +702,7 @@
             TileIcon(
                 icon = tileViewModel.icon,
                 color = colors.icon,
-                modifier = Modifier.align(Alignment.Center)
+                modifier = Modifier.align(Alignment.Center),
             )
         } else {
             LargeTileContent(
@@ -694,11 +751,7 @@
             }
         }
     if (loadedDrawable !is Animatable) {
-        Icon(
-            icon = icon,
-            tint = color,
-            modifier = iconModifier,
-        )
+        Icon(icon = icon, tint = color, modifier = iconModifier)
     } else if (icon is Icon.Resource) {
         val image = AnimatedImageVector.animatedVectorResource(id = icon.res)
         val painter =
@@ -716,7 +769,7 @@
             painter = painter,
             contentDescription = icon.contentDescription?.load(),
             colorFilter = ColorFilter.tint(color = color),
-            modifier = iconModifier
+            modifier = iconModifier,
         )
     }
 }
@@ -765,6 +818,8 @@
     val TileHeight = 72.dp
     val IconTileWithLabelHeight = 140.dp
 
+    @Composable fun longPressLabel() = stringResource(id = R.string.accessibility_long_click_tile)
+
     /** An active tile without dual target uses the active color as background */
     @Composable
     fun activeTileColors(): TileColors =
@@ -850,7 +905,7 @@
                     } else {
                         InactiveCornerRadius
                     },
-                label = label
+                label = label,
             )
         return RoundedCornerShape(animatedCornerRadius)
     }
@@ -858,3 +913,14 @@
 
 private const val CURRENT_TILES_GRID_TEST_TAG = "CurrentTilesGrid"
 private const val AVAILABLE_TILES_GRID_TEST_TAG = "AvailableTilesGrid"
+
+/**
+ * A composable function that returns the [Resources]. It will be recomposed when [Configuration]
+ * gets updated.
+ */
+@Composable
+@ReadOnlyComposable
+private fun resources(): Resources {
+    LocalConfiguration.current
+    return LocalContext.current.resources
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileUiState.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileUiState.kt
index 45051fe..aa42080 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileUiState.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileUiState.kt
@@ -16,8 +16,16 @@
 
 package com.android.systemui.qs.panels.ui.viewmodel
 
+import android.content.res.Resources
+import android.service.quicksettings.Tile
+import android.text.TextUtils
+import android.widget.Switch
 import androidx.compose.runtime.Immutable
+import androidx.compose.ui.semantics.Role
+import androidx.compose.ui.state.ToggleableState
 import com.android.systemui.plugins.qs.QSTile
+import com.android.systemui.qs.tileimpl.SubtitleArrayMapping
+import com.android.systemui.res.R
 import java.util.function.Supplier
 
 @Immutable
@@ -27,14 +35,78 @@
     val state: Int,
     val handlesSecondaryClick: Boolean,
     val icon: Supplier<QSTile.Icon?>,
+    val accessibilityUiState: AccessibilityUiState,
 )
 
-fun QSTile.State.toUiState(): TileUiState {
+data class AccessibilityUiState(
+    val contentDescription: String,
+    val stateDescription: String,
+    val accessibilityRole: Role,
+    val toggleableState: ToggleableState? = null,
+    val clickLabel: String? = null,
+)
+
+fun QSTile.State.toUiState(resources: Resources): TileUiState {
+    val accessibilityRole =
+        if (expandedAccessibilityClassName == Switch::class.java.name && !handlesSecondaryClick) {
+            Role.Switch
+        } else {
+            Role.Button
+        }
+    // State handling and description
+    val stateDescription = StringBuilder()
+    val stateText =
+        if (accessibilityRole == Role.Switch || state == Tile.STATE_UNAVAILABLE) {
+            getStateText(resources)
+        } else {
+            ""
+        }
+    val secondaryLabel = getSecondaryLabel(stateText)
+    if (!TextUtils.isEmpty(stateText)) {
+        stateDescription.append(stateText)
+    }
+    if (disabledByPolicy && state != Tile.STATE_UNAVAILABLE) {
+        stateDescription.append(", ")
+        stateDescription.append(getUnavailableText(spec, resources))
+    }
+    if (
+        !TextUtils.isEmpty(this.stateDescription) &&
+            !stateDescription.contains(this.stateDescription!!)
+    ) {
+        stateDescription.append(", ")
+        stateDescription.append(this.stateDescription)
+    }
+    val toggleableState =
+        if (accessibilityRole == Role.Switch || handlesSecondaryClick) {
+            ToggleableState(state == Tile.STATE_ACTIVE)
+        } else {
+            null
+        }
     return TileUiState(
-        label?.toString() ?: "",
-        secondaryLabel?.toString() ?: "",
-        state,
-        handlesSecondaryClick,
-        icon?.let { Supplier { icon } } ?: iconSupplier ?: Supplier { null },
+        label = label?.toString() ?: "",
+        secondaryLabel = secondaryLabel?.toString() ?: "",
+        state = if (disabledByPolicy) Tile.STATE_UNAVAILABLE else state,
+        handlesSecondaryClick = handlesSecondaryClick,
+        icon = icon?.let { Supplier { icon } } ?: iconSupplier ?: Supplier { null },
+        AccessibilityUiState(
+            contentDescription?.toString() ?: "",
+            stateDescription.toString(),
+            accessibilityRole,
+            toggleableState,
+            resources
+                .getString(R.string.accessibility_tile_disabled_by_policy_action_description)
+                .takeIf { disabledByPolicy },
+        ),
     )
 }
+
+private fun QSTile.State.getStateText(resources: Resources): CharSequence {
+    val arrayResId = SubtitleArrayMapping.getSubtitleId(spec)
+    val array = resources.getStringArray(arrayResId)
+    return array[state]
+}
+
+private fun getUnavailableText(spec: String?, resources: Resources): String {
+    val arrayResId = SubtitleArrayMapping.getSubtitleId(spec)
+    return resources.getStringArray(arrayResId)[Tile.STATE_UNAVAILABLE]
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
index 7ceb786..7bff827 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
@@ -32,7 +32,7 @@
 import android.service.quicksettings.Tile;
 import android.text.TextUtils;
 import android.util.Log;
-import android.widget.Switch;
+import android.widget.Button;
 
 import androidx.annotation.VisibleForTesting;
 
@@ -59,13 +59,13 @@
 import com.android.systemui.res.R;
 import com.android.systemui.statusbar.policy.BluetoothController;
 
-import kotlinx.coroutines.Job;
-
 import java.util.List;
 import java.util.concurrent.Executor;
 
 import javax.inject.Inject;
 
+import kotlinx.coroutines.Job;
+
 /** Quick settings tile: Bluetooth **/
 public class BluetoothTile extends QSTileImpl<BooleanState> {
 
@@ -147,6 +147,8 @@
         }
     }
 
+
+
     @Override
     public Intent getLongClickIntent() {
         return new Intent(Settings.ACTION_BLUETOOTH_SETTINGS);
@@ -221,7 +223,7 @@
             state.state = Tile.STATE_INACTIVE;
         }
 
-        state.expandedAccessibilityClassName = Switch.class.getName();
+        state.expandedAccessibilityClassName = Button.class.getName();
         state.forceExpandIcon = mFeatureFlags.isEnabled(Flags.BLUETOOTH_QS_TILE_DIALOG);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/FontScalingTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/FontScalingTile.kt
index 078698c..7606293 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/FontScalingTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/FontScalingTile.kt
@@ -55,7 +55,7 @@
     qsLogger: QSLogger,
     private val keyguardStateController: KeyguardStateController,
     private val dialogTransitionAnimator: DialogTransitionAnimator,
-    private val fontScalingDialogDelegateProvider: Provider<FontScalingDialogDelegate>
+    private val fontScalingDialogDelegateProvider: Provider<FontScalingDialogDelegate>,
 ) :
     QSTileImpl<QSTile.State?>(
         host,
@@ -66,7 +66,7 @@
         metricsLogger,
         statusBarStateController,
         activityStarter,
-        qsLogger
+        qsLogger,
     ) {
     private val icon = ResourceIcon.get(R.drawable.ic_qs_font_scaling)
 
@@ -86,7 +86,7 @@
                     expandable?.dialogTransitionController(
                         DialogCuj(
                             InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN,
-                            INTERACTION_JANK_TAG
+                            INTERACTION_JANK_TAG,
                         )
                     )
                 controller?.let { dialogTransitionAnimator.show(dialog, controller) }
@@ -102,7 +102,7 @@
                 /* cancelAction= */ null,
                 /* dismissShade= */ true,
                 /* afterKeyguardGone= */ true,
-                /* deferred= */ false
+                /* deferred= */ false,
             )
         }
     }
@@ -110,6 +110,7 @@
     override fun handleUpdateState(state: QSTile.State?, arg: Any?) {
         state?.label = mContext.getString(R.string.quick_settings_font_scaling_label)
         state?.icon = icon
+        state?.contentDescription = state?.label
     }
 
     override fun getLongClickIntent(): Intent? {
diff --git a/packages/SystemUI/src/com/android/systemui/settings/MultiUserUtilsModule.java b/packages/SystemUI/src/com/android/systemui/settings/MultiUserUtilsModule.java
index b9f9b92..05f19ef 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/MultiUserUtilsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/MultiUserUtilsModule.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.settings;
 
+import android.app.ActivityManager;
 import android.app.IActivityManager;
 import android.content.Context;
 import android.hardware.display.DisplayManager;
@@ -66,7 +67,7 @@
             @Background CoroutineDispatcher backgroundDispatcher,
             @Background Handler handler
     ) {
-        int startingUser = userManager.getBootUser().getIdentifier();
+        int startingUser = ActivityManager.getCurrentUser();
         UserTrackerImpl tracker = new UserTrackerImpl(context, featureFlagsProvider, userManager,
                 iActivityManager, dumpManager, appScope, backgroundDispatcher, handler);
         tracker.initialize(startingUser);
diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt b/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt
index 1a997a7..e1631cc 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt
+++ b/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt
@@ -16,11 +16,10 @@
 
 package com.android.systemui.settings
 
-import com.android.systemui.util.annotations.WeaklyReferencedCallback
-
 import android.content.Context
 import android.content.pm.UserInfo
 import android.os.UserHandle
+import com.android.systemui.util.annotations.WeaklyReferencedCallback
 import java.util.concurrent.Executor
 
 /**
@@ -31,19 +30,13 @@
  */
 interface UserTracker : UserContentResolverProvider, UserContextProvider {
 
-    /**
-     * Current user's id.
-     */
+    /** Current user's id. */
     val userId: Int
 
-    /**
-     * [UserHandle] for current user
-     */
+    /** [UserHandle] for current user */
     val userHandle: UserHandle
 
-    /**
-     * [UserInfo] for current user
-     */
+    /** [UserInfo] for current user */
     val userInfo: UserInfo
 
     /**
@@ -56,39 +49,33 @@
      */
     val userProfiles: List<UserInfo>
 
-    /**
-     * Add a [Callback] to be notified of chances, on a particular [Executor]
-     */
+    /** Is the system in the process of switching users? */
+    val isUserSwitching: Boolean
+
+    /** Add a [Callback] to be notified of chances, on a particular [Executor] */
     fun addCallback(callback: Callback, executor: Executor)
 
-    /**
-     * Remove a [Callback] previously added.
-     */
+    /** Remove a [Callback] previously added. */
     fun removeCallback(callback: Callback)
 
-    /**
-     * Callback for notifying of changes.
-     */
+    /** Callback for notifying of changes. */
     @WeaklyReferencedCallback
     interface Callback {
-        /**
-         * Notifies that the current user will be changed.
-         */
+        /** Notifies that the current user will be changed. */
         fun onBeforeUserSwitching(newUser: Int) {}
 
         /**
-         * Same as {@link onUserChanging(Int, Context, Runnable)} but the callback will be
-         * called automatically after the completion of this method.
+         * Same as {@link onUserChanging(Int, Context, Runnable)} but the callback will be called
+         * automatically after the completion of this method.
          */
         fun onUserChanging(newUser: Int, userContext: Context) {}
 
         /**
-         * Notifies that the current user is being changed.
-         * Override this method to run things while the screen is frozen for the user switch.
-         * Please use {@link #onUserChanged} if the task doesn't need to push the unfreezing of the
-         * screen further. Please be aware that code executed in this callback will lengthen the
-         * user switch duration. When overriding this method, resultCallback#run() MUST be called
-         * once the  execution is complete.
+         * Notifies that the current user is being changed. Override this method to run things while
+         * the screen is frozen for the user switch. Please use {@link #onUserChanged} if the task
+         * doesn't need to push the unfreezing of the screen further. Please be aware that code
+         * executed in this callback will lengthen the user switch duration. When overriding this
+         * method, resultCallback#run() MUST be called once the execution is complete.
          */
         fun onUserChanging(newUser: Int, userContext: Context, resultCallback: Runnable) {
             onUserChanging(newUser, userContext)
@@ -96,15 +83,13 @@
         }
 
         /**
-         * Notifies that the current user has changed.
-         * Override this method to run things after the screen is unfrozen for the user switch.
-         * Please see {@link #onUserChanging} if you need to hide jank.
+         * Notifies that the current user has changed. Override this method to run things after the
+         * screen is unfrozen for the user switch. Please see {@link #onUserChanging} if you need to
+         * hide jank.
          */
         fun onUserChanged(newUser: Int, userContext: Context) {}
 
-        /**
-         * Notifies that the current user's profiles have changed.
-         */
+        /** Notifies that the current user's profiles have changed. */
         fun onProfilesChanged(profiles: List<@JvmSuppressWildcards UserInfo>) {}
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt b/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
index 553d1f5..1863e12 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
@@ -106,6 +106,9 @@
     override var userInfo: UserInfo by SynchronizedDelegate(UserInfo(context.userId, "", 0))
         protected set
 
+    override var isUserSwitching = false
+        protected set
+
     /**
      * Returns a [List<UserInfo>] of all profiles associated with the current user.
      *
@@ -197,6 +200,7 @@
                 }
 
                 override fun onUserSwitching(newUserId: Int, reply: IRemoteCallback?) {
+                    isUserSwitching = true
                     if (isBackgroundUserSwitchEnabled) {
                         userSwitchingJob?.cancel()
                         userSwitchingJob =
@@ -210,6 +214,7 @@
                 }
 
                 override fun onUserSwitchComplete(newUserId: Int) {
+                    isUserSwitching = false
                     if (isBackgroundUserSwitchEnabled) {
                         afterUserSwitchingJob?.cancel()
                         afterUserSwitchingJob =
@@ -221,7 +226,7 @@
                     }
                 }
             },
-            TAG
+            TAG,
         )
     }
 
@@ -349,7 +354,7 @@
 
 private data class DataItem(
     val callback: WeakReference<UserTracker.Callback>,
-    val executor: Executor
+    val executor: Executor,
 ) {
     fun sameOrEmpty(other: UserTracker.Callback): Boolean {
         return callback.get()?.equals(other) ?: true
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 52fde7e..4005e10 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -494,7 +494,7 @@
 
     @Test
     public void testIgnoresSimStateCallback_rebroadcast() {
-        Intent intent = new Intent(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
+        Intent intent = defaultSimStateChangedIntent();
 
         mKeyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext(), intent);
         mTestableLooper.processAllMessages();
@@ -515,7 +515,8 @@
 
     @Test
     public void testTelephonyCapable_SimState_Absent() {
-        Intent intent = new Intent(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
+
+        Intent intent = defaultSimStateChangedIntent();
         intent.putExtra(Intent.EXTRA_SIM_STATE,
                 Intent.SIM_STATE_ABSENT);
         mKeyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext(),
@@ -526,7 +527,7 @@
 
     @Test
     public void testTelephonyCapable_SimState_CardIOError() {
-        Intent intent = new Intent(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
+        Intent intent = defaultSimStateChangedIntent();
         intent.putExtra(Intent.EXTRA_SIM_STATE,
                 Intent.SIM_STATE_CARD_IO_ERROR);
         mKeyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext(),
@@ -593,7 +594,7 @@
         ServiceState state = new ServiceState();
         state.setState(ServiceState.STATE_OUT_OF_SERVICE);
         state.fillInNotifierBundle(data);
-        Intent intent = new Intent(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
+        Intent intent = defaultSimStateChangedIntent();
         intent.putExtra(Intent.EXTRA_SIM_STATE
                 , Intent.SIM_STATE_NOT_READY);
         mKeyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext()
@@ -608,7 +609,7 @@
         ServiceState state = new ServiceState();
         state.setState(ServiceState.STATE_OUT_OF_SERVICE);
         state.fillInNotifierBundle(data);
-        Intent intent = new Intent(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
+        Intent intent = defaultSimStateChangedIntent();
         intent.putExtra(Intent.EXTRA_SIM_STATE
                 , Intent.SIM_STATE_READY);
         mKeyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext()
@@ -649,7 +650,7 @@
         ServiceState state = new ServiceState();
         state.setState(ServiceState.STATE_IN_SERVICE);
         state.fillInNotifierBundle(data);
-        Intent intentSimState = new Intent(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
+        Intent intentSimState = defaultSimStateChangedIntent();
         intentSimState.putExtra(Intent.EXTRA_SIM_STATE
                 , Intent.SIM_STATE_LOADED);
         mKeyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext()
@@ -2256,6 +2257,12 @@
         Assert.assertFalse(mKeyguardUpdateMonitor.forceIsDismissibleIsKeepingDeviceUnlocked());
     }
 
+    private Intent defaultSimStateChangedIntent() {
+        Intent intent = new Intent(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
+        intent.putExtra(SubscriptionManager.EXTRA_SLOT_INDEX, 0);
+        return intent;
+    }
+
     private void verifyFingerprintAuthenticateNeverCalled() {
         verify(mFingerprintManager, never()).authenticate(any(), any(), any(), any(), any());
         verify(mFingerprintManager, never()).authenticate(any(), any(), any(), any(), anyInt(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
index 597ffef..9e0d358 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -26,6 +26,7 @@
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT;
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN;
 import static com.android.systemui.Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR;
+import static com.android.systemui.Flags.FLAG_SIM_PIN_BOUNCER_RESET;
 import static com.android.systemui.keyguard.KeyguardViewMediator.DELAYED_KEYGUARD_ACTION;
 import static com.android.systemui.keyguard.KeyguardViewMediator.KEYGUARD_LOCK_AFTER_DELAY_DEFAULT;
 import static com.android.systemui.keyguard.KeyguardViewMediator.REBOOT_MAINLINE_UPDATE;
@@ -62,6 +63,7 @@
 import android.os.PowerManager;
 import android.os.PowerManager.WakeLock;
 import android.os.RemoteException;
+import android.platform.test.annotations.EnableFlags;
 import android.telephony.TelephonyManager;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
@@ -588,6 +590,35 @@
 
     @Test
     @TestableLooper.RunWithLooper(setAsMainLooper = true)
+    @EnableFlags(FLAG_SIM_PIN_BOUNCER_RESET)
+    public void resetStateLocked_whenSimNotReadyAndWasLockedPrior() {
+        // When showing and provisioned
+        mViewMediator.onSystemReady();
+        when(mUpdateMonitor.isDeviceProvisioned()).thenReturn(true);
+        mViewMediator.setShowingLocked(true, "");
+
+        // and a SIM becomes locked and requires a PIN
+        mViewMediator.mUpdateCallback.onSimStateChanged(
+                1 /* subId */,
+                0 /* slotId */,
+                TelephonyManager.SIM_STATE_PIN_REQUIRED);
+        TestableLooper.get(this).processAllMessages();
+
+        reset(mStatusBarKeyguardViewManager);
+
+        // but then disabled by a NOT_READY
+        mViewMediator.mUpdateCallback.onSimStateChanged(
+                1 /* subId */,
+                0 /* slotId */,
+                TelephonyManager.SIM_STATE_NOT_READY);
+        TestableLooper.get(this).processAllMessages();
+
+        // A call to reset the keyguard and bouncer was invoked
+        verify(mStatusBarKeyguardViewManager).reset(true);
+    }
+
+    @Test
+    @TestableLooper.RunWithLooper(setAsMainLooper = true)
     public void restoreBouncerWhenSimLockedAndKeyguardIsGoingAway_initiallyNotShowing() {
         // When showing and provisioned
         mViewMediator.onSystemReady();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt
index 2e2ac3e..a0ecb80 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt
@@ -231,7 +231,7 @@
                         "",
                         "",
                         UserInfo.FLAG_MANAGED_PROFILE,
-                        UserManager.USER_TYPE_PROFILE_MANAGED
+                        UserManager.USER_TYPE_PROFILE_MANAGED,
                     )
                 infoProfile.profileGroupId = id
                 listOf(info, infoProfile)
@@ -261,7 +261,7 @@
                         "",
                         "",
                         UserInfo.FLAG_MANAGED_PROFILE or UserInfo.FLAG_QUIET_MODE,
-                        UserManager.USER_TYPE_PROFILE_MANAGED
+                        UserManager.USER_TYPE_PROFILE_MANAGED,
                     )
                 infoProfile.profileGroupId = id
                 listOf(info, infoProfile)
@@ -291,7 +291,7 @@
                         "",
                         "",
                         UserInfo.FLAG_MANAGED_PROFILE,
-                        UserManager.USER_TYPE_PROFILE_MANAGED
+                        UserManager.USER_TYPE_PROFILE_MANAGED,
                     )
                 infoProfile.profileGroupId = id
                 listOf(info, infoProfile)
@@ -423,7 +423,7 @@
                         "",
                         "",
                         UserInfo.FLAG_MANAGED_PROFILE,
-                        UserManager.USER_TYPE_PROFILE_MANAGED
+                        UserManager.USER_TYPE_PROFILE_MANAGED,
                     )
                 infoProfile.profileGroupId = id
                 listOf(info, infoProfile)
@@ -469,6 +469,24 @@
             assertThat(callback.calledOnProfilesChanged).isEqualTo(0)
         }
 
+    @Test
+    fun testisUserSwitching() =
+        testScope.runTest {
+            tracker.initialize(0)
+            val newID = 5
+            val profileID = newID + 10
+
+            val captor = ArgumentCaptor.forClass(IUserSwitchObserver::class.java)
+            verify(iActivityManager).registerUserSwitchObserver(capture(captor), anyString())
+            assertThat(tracker.isUserSwitching).isFalse()
+
+            captor.value.onUserSwitching(newID, userSwitchingReply)
+            assertThat(tracker.isUserSwitching).isTrue()
+
+            captor.value.onUserSwitchComplete(newID)
+            assertThat(tracker.isUserSwitching).isFalse()
+        }
+
     private class TestCallback : UserTracker.Callback {
         var calledOnUserChanging = 0
         var calledOnUserChanged = 0
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorKosmos.kt
index ace1157..52416ba 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorKosmos.kt
@@ -41,5 +41,6 @@
             trustRepository = trustRepository,
             alternateBouncerInteractor = alternateBouncerInteractor,
             powerInteractor = powerInteractor,
+            keyguardTransitionInteractor = keyguardTransitionInteractor,
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeUserTracker.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeUserTracker.kt
index f3d5b7d..cd76a74 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeUserTracker.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeUserTracker.kt
@@ -33,6 +33,7 @@
     private var _userHandle: UserHandle = UserHandle.of(_userId),
     private var _userInfo: UserInfo = mock(),
     private var _userProfiles: List<UserInfo> = emptyList(),
+    private var _isUserSwitching: Boolean = false,
     userContentResolverProvider: () -> ContentResolver = { MockContentResolver() },
     userContext: Context = mock(),
     private val onCreateCurrentUserContext: (Context) -> Context = { mock() },
@@ -51,6 +52,9 @@
     override val userProfiles: List<UserInfo>
         get() = _userProfiles
 
+    override val isUserSwitching: Boolean
+        get() = _isUserSwitching
+
     // userContentResolver is lazy because Ravenwood doesn't support MockContentResolver()
     // and we still want to allow people use this class for tests that don't use it.
     override val userContentResolver: ContentResolver by lazy { userContentResolverProvider() }
@@ -86,11 +90,13 @@
     }
 
     fun onUserChanging(userId: Int = _userId) {
+        _isUserSwitching = true
         val copy = callbacks.toList()
         copy.forEach { it.onUserChanging(userId, userContext) {} }
     }
 
     fun onUserChanged(userId: Int = _userId) {
+        _isUserSwitching = false
         val copy = callbacks.toList()
         copy.forEach { it.onUserChanged(userId, userContext) }
     }
diff --git a/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java b/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
index acb74d3..c3d68cf 100644
--- a/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
+++ b/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
@@ -1643,16 +1643,14 @@
             CameraExtensionsProxyService.unregisterDeathRecipient(mToken, this);
             mSessionProcessor.deInitSession();
 
-            if (Flags.surfaceLeakFix()) {
-                if (mOutputImageCaptureSurfaceImpl.mSurface != null) {
-                    mOutputImageCaptureSurfaceImpl.mSurface.release();
-                }
-                if (mOutputPreviewSurfaceImpl.mSurface != null) {
-                    mOutputPreviewSurfaceImpl.mSurface.release();
-                }
-                if (mOutputPostviewSurfaceImpl.mSurface != null) {
-                    mOutputPostviewSurfaceImpl.mSurface.release();
-                }
+            if (mOutputImageCaptureSurfaceImpl.mSurface != null) {
+                mOutputImageCaptureSurfaceImpl.mSurface.release();
+            }
+            if (mOutputPreviewSurfaceImpl.mSurface != null) {
+                mOutputPreviewSurfaceImpl.mSurface.release();
+            }
+            if (mOutputPostviewSurfaceImpl.mSurface != null) {
+                mOutputPostviewSurfaceImpl.mSurface.release();
             }
         }
 
diff --git a/ravenwood/TEST_MAPPING b/ravenwood/TEST_MAPPING
index 3583b96..d4d188d 100644
--- a/ravenwood/TEST_MAPPING
+++ b/ravenwood/TEST_MAPPING
@@ -159,11 +159,13 @@
     {
       "name": "RavenwoodServicesTest",
       "host": true
-    },
+    }
+    // AUTO-GENERATED-END
+  ],
+  "ravenwood-postsubmit": [
     {
       "name": "SystemUiRavenTests",
       "host": true
     }
-    // AUTO-GENERATED-END
   ]
 }
diff --git a/ravenwood/scripts/update-test-mapping.sh b/ravenwood/scripts/update-test-mapping.sh
index b6cf5b8..e478b50 100755
--- a/ravenwood/scripts/update-test-mapping.sh
+++ b/ravenwood/scripts/update-test-mapping.sh
@@ -20,6 +20,9 @@
 
 set -e
 
+# Tests that shouldn't be in presubmit.
+EXEMPT='^(SystemUiRavenTests)$'
+
 main() {
     local script_name="${0##*/}"
     local script_dir="${0%/*}"
@@ -30,7 +33,7 @@
     local footer="$(sed -ne '/AUTO-GENERATED-END/,$p' "$test_mapping")"
 
     echo "Getting all tests"
-    local tests=( $("$script_dir/list-ravenwood-tests.sh") )
+    local tests=( $("$script_dir/list-ravenwood-tests.sh" | grep -vP "$EXEMPT") )
 
     local num_tests="${#tests[@]}"
 
diff --git a/services/appfunctions/TEST_MAPPING b/services/appfunctions/TEST_MAPPING
index 91e82ec..91cfa06 100644
--- a/services/appfunctions/TEST_MAPPING
+++ b/services/appfunctions/TEST_MAPPING
@@ -1,4 +1,9 @@
 {
+  "presubmit": [
+    {
+      "name": "FrameworksAppFunctionsTests"
+    }
+  ],
   "postsubmit": [
     {
       "name": "FrameworksAppFunctionsTests"
diff --git a/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java
index 269419f..2362b91 100644
--- a/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java
+++ b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java
@@ -38,6 +38,7 @@
 import com.android.server.appfunctions.RemoteServiceCaller.ServiceUsageCompleteListener;
 
 import java.util.Objects;
+import java.util.concurrent.CompletionException;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.ThreadPoolExecutor;
 import java.util.concurrent.TimeUnit;
@@ -255,6 +256,10 @@
     }
 
     private ExecuteAppFunctionResponse mapExceptionToExecuteAppFunctionResponse(Throwable e) {
+        if(e instanceof CompletionException) {
+            e = e.getCause();
+        }
+
         if (e instanceof AppSearchException) {
             AppSearchException appSearchException = (AppSearchException) e;
             return ExecuteAppFunctionResponse.newFailure(
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index ca8ae6e..72f840d 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -21,6 +21,7 @@
 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
 
 import static com.android.hardware.input.Flags.touchpadVisualizer;
+import static com.android.hardware.input.Flags.useKeyGestureEventHandler;
 
 import android.Manifest;
 import android.annotation.EnforcePermission;
@@ -2116,6 +2117,7 @@
         mKeyboardBacklightController.dump(ipw);
         mKeyboardLedController.dump(ipw);
         mKeyboardGlyphManager.dump(ipw);
+        mKeyGestureController.dump(ipw);
     }
 
     private void dumpAssociations(IndentingPrintWriter pw) {
@@ -2457,13 +2459,18 @@
 
     // Native callback.
     @SuppressWarnings("unused")
-    private long interceptKeyBeforeDispatching(IBinder focus, KeyEvent event, int policyFlags) {
-        // TODO(b/358569822): Move shortcut trigger logic from PWM to KeyGestureController
-        long value = mKeyGestureController.interceptKeyBeforeDispatching(focus, event, policyFlags);
-        if (value != 0) { // If key is consumed (i.e. non-zero value)
-            return value;
+    @VisibleForTesting
+    long interceptKeyBeforeDispatching(IBinder focus, KeyEvent event, int policyFlags) {
+        final long keyNotConsumed = 0;
+        long value = keyNotConsumed;
+        if (useKeyGestureEventHandler()) {
+            value = mKeyGestureController.interceptKeyBeforeDispatching(focus, event, policyFlags);
         }
-        return mWindowManagerCallbacks.interceptKeyBeforeDispatching(focus, event, policyFlags);
+        if (value == keyNotConsumed) {
+            value = mWindowManagerCallbacks.interceptKeyBeforeDispatching(focus, event,
+                    policyFlags);
+        }
+        return value;
     }
 
     // Native callback.
@@ -2748,12 +2755,14 @@
     private void enforceManageKeyGesturePermission() {
         // TODO(b/361567988): Use @EnforcePermission to enforce permission once flag guarding the
         //  permission is rolled out
-        String systemUIPackage = mContext.getString(R.string.config_systemUi);
-        int systemUIAppId = UserHandle.getAppId(mPackageManagerInternal
-                .getPackageUid(systemUIPackage, PackageManager.MATCH_SYSTEM_ONLY,
-                        UserHandle.USER_SYSTEM));
-        if (UserHandle.getCallingAppId() == systemUIAppId) {
-            return;
+        if (mSystemReady) {
+            String systemUIPackage = mContext.getString(R.string.config_systemUi);
+            int systemUIAppId = UserHandle.getAppId(mPackageManagerInternal
+                    .getPackageUid(systemUIPackage, PackageManager.MATCH_SYSTEM_ONLY,
+                            UserHandle.USER_SYSTEM));
+            if (UserHandle.getCallingAppId() == systemUIAppId) {
+                return;
+            }
         }
         if (mContext.checkCallingOrSelfPermission(
                 Manifest.permission.MANAGE_KEY_GESTURES) == PackageManager.PERMISSION_GRANTED) {
diff --git a/services/core/java/com/android/server/input/KeyGestureController.java b/services/core/java/com/android/server/input/KeyGestureController.java
index bfdb1c1..760566b 100644
--- a/services/core/java/com/android/server/input/KeyGestureController.java
+++ b/services/core/java/com/android/server/input/KeyGestureController.java
@@ -16,10 +16,15 @@
 
 package com.android.server.input;
 
+import static com.android.server.flags.Flags.newBugreportKeyboardShortcut;
+
 import android.annotation.BinderThread;
 import android.annotation.MainThread;
 import android.annotation.Nullable;
+import android.annotation.SuppressLint;
 import android.content.Context;
+import android.content.res.Resources;
+
 import android.hardware.input.AidlKeyGestureEvent;
 import android.hardware.input.IKeyGestureEventListener;
 import android.hardware.input.IKeyGestureHandler;
@@ -31,6 +36,8 @@
 import android.os.Message;
 import android.os.Process;
 import android.os.RemoteException;
+import android.os.SystemProperties;
+import android.util.IndentingPrintWriter;
 import android.util.Log;
 import android.util.Slog;
 import android.util.SparseArray;
@@ -38,10 +45,14 @@
 import android.view.InputDevice;
 import android.view.KeyEvent;
 
+import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 
+import java.util.ArrayDeque;
+import java.util.HashSet;
 import java.util.Objects;
+import java.util.Set;
 import java.util.TreeMap;
 
 /**
@@ -56,12 +67,36 @@
     // 'adb shell setprop log.tag.KeyGestureController DEBUG' (requires restart)
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
+    // Maximum key gesture events that are tracked and will be available in input dump.
+    private static final int MAX_TRACKED_EVENTS = 10;
+
     private static final int MSG_NOTIFY_KEY_GESTURE_EVENT = 1;
 
+    // must match: config_settingsKeyBehavior in config.xml
+    private static final int SETTINGS_KEY_BEHAVIOR_SETTINGS_ACTIVITY = 0;
+    private static final int SETTINGS_KEY_BEHAVIOR_NOTIFICATION_PANEL = 1;
+    private static final int SETTINGS_KEY_BEHAVIOR_NOTHING = 2;
+    private static final int LAST_SETTINGS_KEY_BEHAVIOR = SETTINGS_KEY_BEHAVIOR_NOTHING;
+
+    // Must match: config_searchKeyBehavior in config.xml
+    private static final int SEARCH_KEY_BEHAVIOR_DEFAULT_SEARCH = 0;
+    private static final int SEARCH_KEY_BEHAVIOR_TARGET_ACTIVITY = 1;
+    private static final int LAST_SEARCH_KEY_BEHAVIOR = SEARCH_KEY_BEHAVIOR_TARGET_ACTIVITY;
+
     private final Context mContext;
     private final Handler mHandler;
     private final int mSystemPid;
 
+    // Pending actions
+    private boolean mPendingMetaAction;
+    private boolean mPendingCapsLockToggle;
+    private boolean mPendingHideRecentSwitcher;
+
+    // Key behaviors
+    private boolean mEnableBugReportKeyboardShortcut;
+    private int mSearchKeyBehavior;
+    private int mSettingsKeyBehavior;
+
     // List of currently registered key gesture event listeners keyed by process pid
     @GuardedBy("mKeyGestureEventListenerRecords")
     private final SparseArray<KeyGestureEventListenerRecord>
@@ -73,6 +108,11 @@
     @GuardedBy("mKeyGestureHandlerRecords")
     private final TreeMap<Integer, KeyGestureHandlerRecord> mKeyGestureHandlerRecords;
 
+    private final ArrayDeque<KeyGestureEvent> mLastHandledEvents = new ArrayDeque<>();
+
+    /** Currently fully consumed key codes per device */
+    private final SparseArray<Set<Integer>> mConsumedKeysForDevice = new SparseArray<>();
+
     KeyGestureController(Context context, Looper looper) {
         mContext = context;
         mHandler = new Handler(looper, this::handleMessage);
@@ -89,12 +129,449 @@
                 return Integer.compare(p1, p2);
             }
         });
+        initBehaviors();
     }
 
-    public int interceptKeyBeforeDispatching(IBinder focus, KeyEvent event, int policyFlags) {
+    private void initBehaviors() {
+        mEnableBugReportKeyboardShortcut = "1".equals(SystemProperties.get("ro.debuggable"));
+
+        Resources res = mContext.getResources();
+        mSearchKeyBehavior = res.getInteger(R.integer.config_searchKeyBehavior);
+        if (mSearchKeyBehavior < SEARCH_KEY_BEHAVIOR_DEFAULT_SEARCH
+                || mSearchKeyBehavior > LAST_SEARCH_KEY_BEHAVIOR) {
+            mSearchKeyBehavior = SEARCH_KEY_BEHAVIOR_DEFAULT_SEARCH;
+        }
+        mSettingsKeyBehavior = res.getInteger(R.integer.config_settingsKeyBehavior);
+        if (mSettingsKeyBehavior < SETTINGS_KEY_BEHAVIOR_SETTINGS_ACTIVITY
+                || mSettingsKeyBehavior > LAST_SETTINGS_KEY_BEHAVIOR) {
+            mSettingsKeyBehavior = SETTINGS_KEY_BEHAVIOR_SETTINGS_ACTIVITY;
+        }
+    }
+
+    public long interceptKeyBeforeDispatching(IBinder focusedToken, KeyEvent event,
+            int policyFlags) {
         // TODO(b/358569822): Handle shortcuts trigger logic here and pass it to appropriate
         //  KeyGestureHandler (PWM is one of the handlers)
-        return 0;
+        final int keyCode = event.getKeyCode();
+        final int deviceId = event.getDeviceId();
+        final long keyConsumed = -1;
+        final long keyNotConsumed = 0;
+
+        Set<Integer> consumedKeys = mConsumedKeysForDevice.get(deviceId);
+        if (consumedKeys == null) {
+            consumedKeys = new HashSet<>();
+            mConsumedKeysForDevice.put(deviceId, consumedKeys);
+        }
+
+        if (interceptSystemKeysAndShortcuts(focusedToken, event)
+                && event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) {
+            consumedKeys.add(keyCode);
+            return keyConsumed;
+        }
+
+        boolean needToConsumeKey = consumedKeys.contains(keyCode);
+        if (event.getAction() == KeyEvent.ACTION_UP || event.isCanceled()) {
+            consumedKeys.remove(keyCode);
+            if (consumedKeys.isEmpty()) {
+                mConsumedKeysForDevice.remove(deviceId);
+            }
+        }
+
+        return needToConsumeKey ? keyConsumed : keyNotConsumed;
+    }
+
+    @SuppressLint("MissingPermission")
+    private boolean interceptSystemKeysAndShortcuts(IBinder focusedToken, KeyEvent event) {
+        final int keyCode = event.getKeyCode();
+        final int repeatCount = event.getRepeatCount();
+        final int metaState = event.getMetaState();
+        final boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
+        final boolean canceled = event.isCanceled();
+        final int displayId = event.getDisplayId();
+        final int deviceId = event.getDeviceId();
+        final boolean firstDown = down && repeatCount == 0;
+
+        // Cancel any pending meta actions if we see any other keys being pressed between the
+        // down of the meta key and its corresponding up.
+        if (mPendingMetaAction && !KeyEvent.isMetaKey(keyCode)) {
+            mPendingMetaAction = false;
+        }
+        // Any key that is not Alt or Meta cancels Caps Lock combo tracking.
+        if (mPendingCapsLockToggle && !KeyEvent.isMetaKey(keyCode) && !KeyEvent.isAltKey(keyCode)) {
+            mPendingCapsLockToggle = false;
+        }
+
+        switch (keyCode) {
+            case KeyEvent.KEYCODE_A:
+                if (firstDown && event.isMetaPressed()) {
+                    return handleKeyGesture(deviceId, new int[]{keyCode}, KeyEvent.META_META_ON,
+                            KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_ASSISTANT,
+                            KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
+                            focusedToken, /* flags = */0);
+                }
+                break;
+            case KeyEvent.KEYCODE_RECENT_APPS:
+                if (firstDown) {
+                    handleKeyGesture(deviceId, new int[]{keyCode}, /* modifierState = */0,
+                            KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS,
+                            KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
+                            focusedToken, /* flags = */0);
+                }
+                return true;
+            case KeyEvent.KEYCODE_APP_SWITCH:
+                if (firstDown) {
+                    handleKeyGesture(deviceId, new int[]{keyCode}, /* modifierState = */0,
+                            KeyGestureEvent.KEY_GESTURE_TYPE_APP_SWITCH,
+                            KeyGestureEvent.ACTION_GESTURE_START, displayId,
+                            focusedToken, /* flags = */0);
+                } else if (!down) {
+                    handleKeyGesture(deviceId, new int[]{keyCode}, /* modifierState = */0,
+                            KeyGestureEvent.KEY_GESTURE_TYPE_APP_SWITCH,
+                            KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
+                            focusedToken, canceled ? KeyGestureEvent.FLAG_CANCELLED : 0);
+                }
+                return true;
+            case KeyEvent.KEYCODE_H:
+            case KeyEvent.KEYCODE_ENTER:
+                if (firstDown && event.isMetaPressed()) {
+                    return handleKeyGesture(deviceId, new int[]{keyCode}, KeyEvent.META_META_ON,
+                            KeyGestureEvent.KEY_GESTURE_TYPE_HOME,
+                            KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
+                            focusedToken, /* flags = */0);
+                }
+                break;
+            case KeyEvent.KEYCODE_I:
+                if (firstDown && event.isMetaPressed()) {
+                    return handleKeyGesture(deviceId, new int[]{keyCode}, KeyEvent.META_META_ON,
+                            KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS,
+                            KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
+                            focusedToken, /* flags = */0);
+                }
+                break;
+            case KeyEvent.KEYCODE_L:
+                if (firstDown && event.isMetaPressed()) {
+                    return handleKeyGesture(deviceId, new int[]{keyCode}, KeyEvent.META_META_ON,
+                            KeyGestureEvent.KEY_GESTURE_TYPE_LOCK_SCREEN,
+                            KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
+                            focusedToken, /* flags = */0);
+                }
+                break;
+            case KeyEvent.KEYCODE_N:
+                if (firstDown && event.isMetaPressed()) {
+                    if (event.isCtrlPressed()) {
+                        // TODO(b/358569822): Move open notes handling in System UI instead of PWM
+                    } else {
+                        return handleKeyGesture(deviceId, new int[]{keyCode},
+                                KeyEvent.META_META_ON,
+                                KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL,
+                                KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
+                                focusedToken, /* flags = */0);
+                    }
+                }
+                break;
+            case KeyEvent.KEYCODE_S:
+                if (firstDown && event.isMetaPressed() && event.isCtrlPressed()) {
+                    return handleKeyGesture(deviceId, new int[]{keyCode},
+                            KeyEvent.META_META_ON | KeyEvent.META_CTRL_ON,
+                            KeyGestureEvent.KEY_GESTURE_TYPE_TAKE_SCREENSHOT,
+                            KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
+                            focusedToken, /* flags = */0);
+                }
+                break;
+            case KeyEvent.KEYCODE_DEL:
+                if (newBugreportKeyboardShortcut()) {
+                    if (firstDown && mEnableBugReportKeyboardShortcut && event.isMetaPressed()
+                            && event.isCtrlPressed()) {
+                        return handleKeyGesture(deviceId, new int[]{keyCode},
+                                KeyEvent.META_META_ON | KeyEvent.META_CTRL_ON,
+                                KeyGestureEvent.KEY_GESTURE_TYPE_TRIGGER_BUG_REPORT,
+                                KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
+                                focusedToken, /* flags = */0);
+                    }
+                }
+                // fall through
+            case KeyEvent.KEYCODE_ESCAPE:
+                if (firstDown && event.isMetaPressed()) {
+                    return handleKeyGesture(deviceId, new int[]{keyCode}, KeyEvent.META_META_ON,
+                            KeyGestureEvent.KEY_GESTURE_TYPE_BACK,
+                            KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
+                            focusedToken, /* flags = */0);
+                }
+                break;
+            case KeyEvent.KEYCODE_DPAD_UP:
+                if (firstDown && event.isMetaPressed() && event.isCtrlPressed()) {
+                    return handleKeyGesture(deviceId, new int[]{keyCode},
+                            KeyEvent.META_META_ON | KeyEvent.META_CTRL_ON,
+                            KeyGestureEvent.KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION,
+                            KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
+                            focusedToken, /* flags = */0);
+                }
+                break;
+            case KeyEvent.KEYCODE_DPAD_DOWN:
+                if (firstDown && event.isMetaPressed() && event.isCtrlPressed()) {
+                    return handleKeyGesture(deviceId, new int[]{keyCode},
+                            KeyEvent.META_META_ON | KeyEvent.META_CTRL_ON,
+                            KeyGestureEvent.KEY_GESTURE_TYPE_DESKTOP_MODE,
+                            KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
+                            focusedToken, /* flags = */0);
+                }
+                break;
+            case KeyEvent.KEYCODE_DPAD_LEFT:
+                if (firstDown && event.isMetaPressed()) {
+                    if (event.isCtrlPressed()) {
+                        return handleKeyGesture(deviceId, new int[]{keyCode},
+                                KeyEvent.META_META_ON | KeyEvent.META_CTRL_ON,
+                                KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_LEFT,
+                                KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
+                                focusedToken, /* flags = */0);
+                    } else if (event.isAltPressed()) {
+                        return handleKeyGesture(deviceId, new int[]{keyCode},
+                                KeyEvent.META_META_ON | KeyEvent.META_ALT_ON,
+                                KeyGestureEvent.KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_LEFT,
+                                KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
+                                focusedToken, /* flags = */0);
+                    } else {
+                        return handleKeyGesture(deviceId, new int[]{keyCode},
+                                KeyEvent.META_META_ON,
+                                KeyGestureEvent.KEY_GESTURE_TYPE_BACK,
+                                KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
+                                focusedToken, /* flags = */0);
+                    }
+                }
+                break;
+            case KeyEvent.KEYCODE_DPAD_RIGHT:
+                if (firstDown && event.isMetaPressed()) {
+                    if (event.isCtrlPressed()) {
+                        return handleKeyGesture(deviceId, new int[]{keyCode},
+                                KeyEvent.META_META_ON | KeyEvent.META_CTRL_ON,
+                                KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_RIGHT,
+                                KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
+                                focusedToken, /* flags = */0);
+                    } else if (event.isAltPressed()) {
+                        return handleKeyGesture(deviceId, new int[]{keyCode},
+                                KeyEvent.META_META_ON | KeyEvent.META_ALT_ON,
+                                KeyGestureEvent.KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_RIGHT,
+                                KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
+                                focusedToken, /* flags = */0);
+                    }
+                }
+                break;
+            case KeyEvent.KEYCODE_SLASH:
+                if (firstDown && event.isMetaPressed()) {
+                    return handleKeyGesture(deviceId, new int[]{keyCode}, KeyEvent.META_META_ON,
+                            KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER,
+                            KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
+                            focusedToken, /* flags = */0);
+                }
+                break;
+            case KeyEvent.KEYCODE_BRIGHTNESS_UP:
+            case KeyEvent.KEYCODE_BRIGHTNESS_DOWN:
+                if (down) {
+                    handleKeyGesture(deviceId, new int[]{keyCode}, /* modifierState = */0,
+                            keyCode == KeyEvent.KEYCODE_BRIGHTNESS_UP
+                                    ? KeyGestureEvent.KEY_GESTURE_TYPE_BRIGHTNESS_UP
+                                    : KeyGestureEvent.KEY_GESTURE_TYPE_BRIGHTNESS_DOWN,
+                            KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
+                            focusedToken, /* flags = */0);
+                }
+                return true;
+            case KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_DOWN:
+                if (down) {
+                    handleKeyGesture(deviceId, new int[]{keyCode}, /* modifierState = */0,
+                            KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_DOWN,
+                            KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
+                            focusedToken, /* flags = */0);
+                }
+                return true;
+            case KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_UP:
+                if (down) {
+                    handleKeyGesture(deviceId, new int[]{keyCode}, /* modifierState = */0,
+                            KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_UP,
+                            KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
+                            focusedToken, /* flags = */0);
+                }
+                return true;
+            case KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_TOGGLE:
+                // TODO: Add logic
+                if (!down) {
+                    handleKeyGesture(deviceId, new int[]{keyCode}, /* modifierState = */0,
+                            KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_TOGGLE,
+                            KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
+                            focusedToken, /* flags = */0);
+                }
+                return true;
+            case KeyEvent.KEYCODE_ALL_APPS:
+                if (firstDown) {
+                    handleKeyGesture(deviceId, new int[]{keyCode}, /* modifierState = */0,
+                            KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS,
+                            KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
+                            focusedToken, /* flags = */0);
+                }
+                return true;
+            case KeyEvent.KEYCODE_NOTIFICATION:
+                if (!down) {
+                    handleKeyGesture(deviceId, new int[]{keyCode}, /* modifierState = */0,
+                            KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL,
+                            KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
+                            focusedToken, /* flags = */0);
+                }
+                return true;
+            case KeyEvent.KEYCODE_SEARCH:
+                if (firstDown && mSearchKeyBehavior == SEARCH_KEY_BEHAVIOR_TARGET_ACTIVITY) {
+                    return handleKeyGesture(deviceId, new int[]{keyCode}, /* modifierState = */0,
+                            KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SEARCH,
+                            KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
+                            focusedToken, /* flags = */0);
+
+                }
+                break;
+            case KeyEvent.KEYCODE_SETTINGS:
+                if (firstDown) {
+                    if (mSettingsKeyBehavior == SETTINGS_KEY_BEHAVIOR_SETTINGS_ACTIVITY) {
+                        handleKeyGesture(deviceId,
+                                new int[]{keyCode}, /* modifierState = */0,
+                                KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS,
+                                KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
+                                focusedToken, /* flags = */0);
+                    } else if (mSettingsKeyBehavior == SETTINGS_KEY_BEHAVIOR_NOTIFICATION_PANEL) {
+                        handleKeyGesture(deviceId,
+                                new int[]{keyCode}, /* modifierState = */0,
+                                KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL,
+                                KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
+                                focusedToken, /* flags = */0);
+                    }
+                }
+                return true;
+            case KeyEvent.KEYCODE_LANGUAGE_SWITCH:
+                if (firstDown) {
+                    handleKeyGesture(deviceId, new int[]{keyCode},
+                            event.isShiftPressed() ? KeyEvent.META_SHIFT_ON : 0,
+                            KeyGestureEvent.KEY_GESTURE_TYPE_LANGUAGE_SWITCH,
+                            KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
+                            focusedToken, /* flags = */0);
+                }
+                return true;
+            case KeyEvent.KEYCODE_CAPS_LOCK:
+                // Just logging/notifying purposes
+                // Caps lock is already handled in inputflinger native
+                if (!down) {
+                    AidlKeyGestureEvent eventToNotify = createKeyGestureEvent(deviceId,
+                            new int[]{keyCode}, metaState,
+                            KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK,
+                            KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId, /* flags = */0);
+                    Message msg = Message.obtain(mHandler, MSG_NOTIFY_KEY_GESTURE_EVENT,
+                            eventToNotify);
+                    mHandler.sendMessage(msg);
+                }
+                break;
+            case KeyEvent.KEYCODE_SCREENSHOT:
+                if (firstDown) {
+                    handleKeyGesture(deviceId, new int[]{keyCode}, /* modifierState = */0,
+                            KeyGestureEvent.KEY_GESTURE_TYPE_TAKE_SCREENSHOT,
+                            KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
+                            focusedToken, /* flags = */0);
+                }
+                return true;
+            case KeyEvent.KEYCODE_META_LEFT:
+            case KeyEvent.KEYCODE_META_RIGHT:
+                if (down) {
+                    if (event.isAltPressed()) {
+                        mPendingCapsLockToggle = true;
+                        mPendingMetaAction = false;
+                    } else {
+                        mPendingCapsLockToggle = false;
+                        mPendingMetaAction = true;
+                    }
+                } else {
+                    // Toggle Caps Lock on META-ALT.
+                    if (mPendingCapsLockToggle) {
+                        mPendingCapsLockToggle = false;
+                        handleKeyGesture(deviceId, new int[]{KeyEvent.KEYCODE_META_LEFT,
+                                        KeyEvent.KEYCODE_ALT_LEFT}, /* modifierState = */0,
+                                KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK,
+                                KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
+                                focusedToken, /* flags = */0);
+
+                    } else if (mPendingMetaAction) {
+                        mPendingMetaAction = false;
+                        if (!canceled) {
+                            handleKeyGesture(deviceId, new int[]{keyCode},
+                                    /* modifierState = */0,
+                                    KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_ALL_APPS,
+                                    KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
+                                    focusedToken, /* flags = */0);
+                        }
+                    }
+                }
+                return true;
+            case KeyEvent.KEYCODE_TAB:
+                if (firstDown) {
+                    if (event.isMetaPressed()) {
+                        return handleKeyGesture(deviceId, new int[]{keyCode}, KeyEvent.META_META_ON,
+                                KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS,
+                                KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
+                                focusedToken, /* flags = */0);
+                    } else if (!mPendingHideRecentSwitcher) {
+                        final int shiftlessModifiers =
+                                event.getModifiers() & ~KeyEvent.META_SHIFT_MASK;
+                        if (KeyEvent.metaStateHasModifiers(
+                                shiftlessModifiers, KeyEvent.META_ALT_ON)) {
+                            mPendingHideRecentSwitcher = true;
+                            return handleKeyGesture(deviceId, new int[]{keyCode},
+                                    KeyEvent.META_ALT_ON,
+                                    KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS_SWITCHER,
+                                    KeyGestureEvent.ACTION_GESTURE_START, displayId,
+                                    focusedToken, /* flags = */0);
+                        }
+                    }
+                }
+                break;
+            case KeyEvent.KEYCODE_ALT_LEFT:
+            case KeyEvent.KEYCODE_ALT_RIGHT:
+                if (down) {
+                    if (event.isMetaPressed()) {
+                        mPendingCapsLockToggle = true;
+                        mPendingMetaAction = false;
+                    } else {
+                        mPendingCapsLockToggle = false;
+                    }
+                } else {
+                    if (mPendingHideRecentSwitcher) {
+                        mPendingHideRecentSwitcher = false;
+                        return handleKeyGesture(deviceId, new int[]{KeyEvent.KEYCODE_TAB},
+                                KeyEvent.META_ALT_ON,
+                                KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS_SWITCHER,
+                                KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
+                                focusedToken, /* flags = */0);
+                    }
+
+                    // Toggle Caps Lock on META-ALT.
+                    if (mPendingCapsLockToggle) {
+                        mPendingCapsLockToggle = false;
+                        return handleKeyGesture(deviceId, new int[]{KeyEvent.KEYCODE_META_LEFT,
+                                        KeyEvent.KEYCODE_ALT_LEFT}, /* modifierState = */0,
+                                KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK,
+                                KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
+                                focusedToken, /* flags = */0);
+                    }
+                }
+                break;
+            case KeyEvent.KEYCODE_ASSIST:
+                Slog.wtf(TAG, "KEYCODE_ASSIST should be handled in interceptKeyBeforeQueueing");
+                return true;
+            case KeyEvent.KEYCODE_VOICE_ASSIST:
+                Slog.wtf(TAG, "KEYCODE_VOICE_ASSIST should be handled in"
+                        + " interceptKeyBeforeQueueing");
+                return true;
+            case KeyEvent.KEYCODE_STYLUS_BUTTON_PRIMARY:
+            case KeyEvent.KEYCODE_STYLUS_BUTTON_SECONDARY:
+            case KeyEvent.KEYCODE_STYLUS_BUTTON_TERTIARY:
+            case KeyEvent.KEYCODE_STYLUS_BUTTON_TAIL:
+                Slog.wtf(TAG, "KEYCODE_STYLUS_BUTTON_* should be handled in"
+                        + " interceptKeyBeforeQueueing");
+                return true;
+        }
+        return false;
     }
 
     @VisibleForTesting
@@ -147,6 +624,10 @@
                     KeyGestureEvent.keyGestureTypeToLogEvent(event.gestureType));
         }
         notifyAllListeners(event);
+        while (mLastHandledEvents.size() >= MAX_TRACKED_EVENTS) {
+            mLastHandledEvents.removeFirst();
+        }
+        mLastHandledEvents.addLast(new KeyGestureEvent(event));
     }
 
     @MainThread
@@ -347,4 +828,45 @@
         event.flags = flags;
         return event;
     }
+
+    public void dump(IndentingPrintWriter ipw) {
+        ipw.println("KeyGestureController:");
+        ipw.increaseIndent();
+        ipw.println("mSystemPid = " + mSystemPid);
+        ipw.println("mPendingMetaAction = " + mPendingMetaAction);
+        ipw.println("mPendingCapsLockToggle = " + mPendingCapsLockToggle);
+        ipw.println("mPendingHideRecentSwitcher = " + mPendingHideRecentSwitcher);
+        ipw.println("mSearchKeyBehavior = " + mSearchKeyBehavior);
+        ipw.println("mSettingsKeyBehavior = " + mSettingsKeyBehavior);
+        ipw.print("mKeyGestureEventListenerRecords = {");
+        synchronized (mKeyGestureEventListenerRecords) {
+            int size = mKeyGestureEventListenerRecords.size();
+            for (int i = 0; i < size; i++) {
+                ipw.print(mKeyGestureEventListenerRecords.keyAt(i));
+                if (i < size - 1) {
+                    ipw.print(", ");
+                }
+            }
+        }
+        ipw.println("}");
+        ipw.print("mKeyGestureHandlerRecords = {");
+        synchronized (mKeyGestureHandlerRecords) {
+            int i = mKeyGestureHandlerRecords.size() - 1;
+            for (int processId : mKeyGestureHandlerRecords.keySet()) {
+                ipw.print(processId);
+                if (i > 0) {
+                    ipw.print(", ");
+                }
+                i--;
+            }
+        }
+        ipw.println("}");
+        ipw.decreaseIndent();
+        ipw.println("Last handled KeyGestureEvents: ");
+        ipw.increaseIndent();
+        for (KeyGestureEvent ev : mLastHandledEvents) {
+            ipw.println(ev);
+        }
+        ipw.decreaseIndent();
+    }
 }
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index ed9dcfa..98091b1 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -83,6 +83,7 @@
 
 import static com.android.hardware.input.Flags.emojiAndScreenshotKeycodesAvailable;
 import static com.android.hardware.input.Flags.modifierShortcutDump;
+import static com.android.hardware.input.Flags.useKeyGestureEventHandler;
 import static com.android.server.flags.Flags.modifierShortcutManagerMultiuser;
 import static com.android.server.flags.Flags.newBugreportKeyboardShortcut;
 import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.SCREENSHOT_KEYCHORD_DELAY;
@@ -809,7 +810,7 @@
                     event.recycle();
                     break;
                 case MSG_HANDLE_ALL_APPS:
-                    launchAllAppsAction((KeyEvent) msg.obj);
+                    launchAllAppsAction();
                     break;
                 case MSG_RINGER_TOGGLE_CHORD:
                     handleRingerChordGesture();
@@ -820,7 +821,7 @@
                 case MSG_SWITCH_KEYBOARD_LAYOUT:
                     SwitchKeyboardLayoutMessageObject object =
                             (SwitchKeyboardLayoutMessageObject) msg.obj;
-                    handleSwitchKeyboardLayout(object.keyEvent, object.direction,
+                    handleSwitchKeyboardLayout(object.displayId, object.direction,
                             object.focusedToken);
                     break;
                 case MSG_SET_DEFERRED_KEY_ACTIONS_EXECUTABLE:
@@ -937,7 +938,7 @@
         }
     }
 
-    private record SwitchKeyboardLayoutMessageObject(KeyEvent keyEvent, IBinder focusedToken,
+    private record SwitchKeyboardLayoutMessageObject(int displayId, IBinder focusedToken,
                                                      int direction) {
     }
 
@@ -1813,9 +1814,7 @@
                 Settings.Secure.TV_USER_SETUP_COMPLETE, 0, UserHandle.USER_CURRENT) != 0;
     }
 
-    private void handleShortPressOnHome(KeyEvent event) {
-        notifyKeyGestureCompleted(event, KeyGestureEvent.KEY_GESTURE_TYPE_HOME);
-
+    private void handleShortPressOnHome(int displayId) {
         // Turn on the connected TV and switch HDMI input if we're a HDMI playback device.
         final HdmiControl hdmiControl = getHdmiControl();
         if (hdmiControl != null) {
@@ -1831,7 +1830,7 @@
         }
 
         // Go home!
-        launchHomeFromHotKey(event.getDisplayId());
+        launchHomeFromHotKey(displayId);
     }
 
     /**
@@ -1878,11 +1877,9 @@
         }
     }
 
-    private void launchAllAppsAction(KeyEvent event) {
+    private void launchAllAppsAction() {
         if (mHasFeatureLeanback || mHasFeatureWatch) {
             // TV and watch support the all apps intent
-            notifyKeyGestureCompleted(event,
-                                KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS);
             Intent intent = new Intent(Intent.ACTION_ALL_APPS);
             if (mHasFeatureLeanback) {
                 Intent intentLauncher = new Intent(Intent.ACTION_MAIN);
@@ -1896,8 +1893,6 @@
             }
             startActivityAsUser(intent, UserHandle.CURRENT);
         } else {
-            notifyKeyGestureCompleted(event,
-                                KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_ALL_APPS);
             AccessibilityManagerInternal accessibilityManager = getAccessibilityManagerInternal();
             if (accessibilityManager != null) {
                 accessibilityManager.performSystemAction(
@@ -1949,7 +1944,9 @@
             @Override
             public void run() {
                 if (mPendingHomeKeyEvent != null) {
-                    handleShortPressOnHome(mPendingHomeKeyEvent);
+                    notifyKeyGestureCompleted(mPendingHomeKeyEvent,
+                            KeyGestureEvent.KEY_GESTURE_TYPE_HOME);
+                    handleShortPressOnHome(mPendingHomeKeyEvent.getDisplayId());
                     mPendingHomeKeyEvent = null;
                 }
             }
@@ -2002,7 +1999,10 @@
                 }
 
                 // Post to main thread to avoid blocking input pipeline.
-                mHandler.post(() -> handleShortPressOnHome(event));
+                mHandler.post(() -> {
+                    notifyKeyGestureCompleted(event, KeyGestureEvent.KEY_GESTURE_TYPE_HOME);
+                    handleShortPressOnHome(event.getDisplayId());
+                });
                 return true;
             }
 
@@ -2080,7 +2080,8 @@
             performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, "Home - Long Press");
             switch (mLongPressOnHomeBehavior) {
                 case LONG_PRESS_HOME_ALL_APPS:
-                    launchAllAppsAction(event);
+                    notifyKeyGestureCompleted(event, KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS);
+                    launchAllAppsAction();
                     break;
                 case LONG_PRESS_HOME_ASSIST:
                     notifyKeyGestureCompleted(event,
@@ -2430,6 +2431,7 @@
         mWindowWakeUpPolicy = injector.getWindowWakeUpPolicy();
         initKeyCombinationRules();
         initSingleKeyGestureRules(injector.getLooper());
+        initKeyGestures();
         mButtonOverridePermissionChecker = injector.getButtonOverridePermissionChecker();
         mSideFpsEventHandler = new SideFpsEventHandler(mContext, mHandler, mPowerManager);
     }
@@ -3348,18 +3350,18 @@
             }
         }
 
-        Set<Integer> consumedKeys = mConsumedKeysForDevice.get(deviceId);
-        if (consumedKeys == null) {
-            consumedKeys = new HashSet<>();
-            mConsumedKeysForDevice.put(deviceId, consumedKeys);
-        }
-
         // TODO(b/358569822) Remove below once we have nicer API for listening to shortcuts
         if ((event.isMetaPressed() || KeyEvent.isMetaKey(keyCode))
                 && shouldInterceptShortcuts(focusedToken)) {
             return keyNotConsumed;
         }
 
+        Set<Integer> consumedKeys = mConsumedKeysForDevice.get(deviceId);
+        if (consumedKeys == null) {
+            consumedKeys = new HashSet<>();
+            mConsumedKeysForDevice.put(deviceId, consumedKeys);
+        }
+
         if (interceptSystemKeysAndShortcuts(focusedToken, event)
                 && event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) {
             consumedKeys.add(keyCode);
@@ -3388,6 +3390,10 @@
     // conflicting events to application, make sure to consume the event on
     // ACTION_DOWN even if you want to do something on ACTION_UP. This is essential
     // to maintain event parity and to not have incomplete key gestures.
+    //
+    // NOTE: Please try not to add new Shortcut combinations here and instead use KeyGestureEvents.
+    // Add shortcut trigger logic in {@code KeyGestureController} and add handling logic in
+    // {@link handleKeyGesture()}
     @SuppressLint("MissingPermission")
     private boolean interceptSystemKeysAndShortcuts(IBinder focusedToken, KeyEvent event) {
         final boolean keyguardOn = keyguardOn();
@@ -3516,6 +3522,7 @@
                     injectBackGesture(event.getDownTime());
                     return true;
                 }
+                break;
             case KeyEvent.KEYCODE_DPAD_UP:
                 if (firstDown && event.isMetaPressed() && event.isCtrlPressed()) {
                     StatusBarManagerInternal statusbar = getStatusBarManagerInternal();
@@ -3544,11 +3551,11 @@
                         moveFocusedTaskToStageSplit(getTargetDisplayIdForKeyEvent(event),
                                 true /* leftOrTop */);
                         notifyKeyGestureCompleted(event,
-                                KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION);
+                                KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_LEFT);
                     } else if (event.isAltPressed()) {
                         setSplitscreenFocus(true /* leftOrTop */);
                         notifyKeyGestureCompleted(event,
-                                KeyGestureEvent.KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS);
+                                KeyGestureEvent.KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_LEFT);
                     } else {
                         notifyKeyGestureCompleted(event,
                                 KeyGestureEvent.KEY_GESTURE_TYPE_BACK);
@@ -3563,12 +3570,12 @@
                         moveFocusedTaskToStageSplit(getTargetDisplayIdForKeyEvent(event),
                                 false /* leftOrTop */);
                         notifyKeyGestureCompleted(event,
-                                KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION);
+                                KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_RIGHT);
                         return true;
                     } else if (event.isAltPressed()) {
                         setSplitscreenFocus(false /* leftOrTop */);
                         notifyKeyGestureCompleted(event,
-                                KeyGestureEvent.KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS);
+                                KeyGestureEvent.KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_RIGHT);
                         return true;
                     }
                 }
@@ -3593,30 +3600,7 @@
                 if (down) {
                     int direction = keyCode == KeyEvent.KEYCODE_BRIGHTNESS_UP ? 1 : -1;
 
-                    int screenDisplayId = displayId < 0 ? DEFAULT_DISPLAY : displayId;
-
-                    float minLinearBrightness = mPowerManager.getBrightnessConstraint(
-                            PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM);
-                    float maxLinearBrightness = mPowerManager.getBrightnessConstraint(
-                            PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MAXIMUM);
-                    float linearBrightness = mDisplayManager.getBrightness(screenDisplayId);
-
-                    float gammaBrightness = BrightnessUtils.convertLinearToGamma(linearBrightness);
-                    float adjustedGammaBrightness =
-                            gammaBrightness + 1f / BRIGHTNESS_STEPS * direction;
-                    adjustedGammaBrightness = MathUtils.constrain(adjustedGammaBrightness, 0f,
-                            1f);
-                    float adjustedLinearBrightness = BrightnessUtils.convertGammaToLinear(
-                            adjustedGammaBrightness);
-                    adjustedLinearBrightness = MathUtils.constrain(adjustedLinearBrightness,
-                            minLinearBrightness, maxLinearBrightness);
-                    mDisplayManager.setBrightness(screenDisplayId, adjustedLinearBrightness);
-
-                    Intent intent = new Intent(Intent.ACTION_SHOW_BRIGHTNESS_DIALOG);
-                    intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION
-                            | Intent.FLAG_ACTIVITY_NO_USER_ACTION);
-                    intent.putExtra(EXTRA_FROM_BRIGHTNESS_KEY, true);
-                    startActivityAsUser(intent, UserHandle.CURRENT_OR_SELF);
+                    changeDisplayBrightnessValue(displayId, direction);
 
                     int gestureType = keyCode == KeyEvent.KEYCODE_BRIGHTNESS_DOWN
                             ? KeyGestureEvent.KEY_GESTURE_TYPE_BRIGHTNESS_DOWN
@@ -3689,10 +3673,11 @@
             case KeyEvent.KEYCODE_ALL_APPS:
                 if (firstDown) {
                     mHandler.removeMessages(MSG_HANDLE_ALL_APPS);
-
                     Message msg = mHandler.obtainMessage(MSG_HANDLE_ALL_APPS, new KeyEvent(event));
                     msg.setAsynchronous(true);
                     msg.sendToTarget();
+
+                    notifyKeyGestureCompleted(event, KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS);
                 }
                 return true;
             case KeyEvent.KEYCODE_NOTIFICATION:
@@ -3720,7 +3705,7 @@
             case KeyEvent.KEYCODE_LANGUAGE_SWITCH:
                 if (firstDown) {
                     int direction = (metaState & KeyEvent.META_SHIFT_MASK) != 0 ? -1 : 1;
-                    sendSwitchKeyboardLayout(event, focusedToken, direction);
+                    sendSwitchKeyboardLayout(displayId, focusedToken, direction);
                     notifyKeyGestureCompleted(event,
                             KeyGestureEvent.KEY_GESTURE_TYPE_LANGUAGE_SWITCH);
                     return true;
@@ -3745,7 +3730,9 @@
                                 KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK);
                     } else if (mPendingMetaAction) {
                         if (!canceled) {
-                            launchAllAppsAction(event);
+                            launchAllAppsAction();
+                            notifyKeyGestureCompleted(event,
+                                    KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS);
                         }
                         mPendingMetaAction = false;
                     }
@@ -3829,6 +3816,254 @@
         return (metaState & KeyEvent.META_META_ON) != 0;
     }
 
+    @SuppressLint("MissingPermission")
+    private void initKeyGestures() {
+        if (!useKeyGestureEventHandler()) {
+            return;
+        }
+        mInputManager.registerKeyGestureEventHandler(new InputManager.KeyGestureEventHandler() {
+            @Override
+            public boolean handleKeyGestureEvent(@NonNull KeyGestureEvent event,
+                    @Nullable IBinder focusedToken) {
+                return PhoneWindowManager.this.handleKeyGestureEvent(event, focusedToken);
+            }
+
+            @Override
+            public boolean isKeyGestureSupported(int gestureType) {
+                switch (gestureType) {
+                    case KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS:
+                    case KeyGestureEvent.KEY_GESTURE_TYPE_APP_SWITCH:
+                    case KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_ASSISTANT:
+                    case KeyGestureEvent.KEY_GESTURE_TYPE_HOME:
+                    case KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS:
+                    case KeyGestureEvent.KEY_GESTURE_TYPE_LOCK_SCREEN:
+                    case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL:
+                    case KeyGestureEvent.KEY_GESTURE_TYPE_TAKE_SCREENSHOT:
+                    case KeyGestureEvent.KEY_GESTURE_TYPE_TRIGGER_BUG_REPORT:
+                    case KeyGestureEvent.KEY_GESTURE_TYPE_BACK:
+                    case KeyGestureEvent.KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION:
+                    case KeyGestureEvent.KEY_GESTURE_TYPE_DESKTOP_MODE:
+                    case KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_LEFT:
+                    case KeyGestureEvent.KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_LEFT:
+                    case KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_RIGHT:
+                    case KeyGestureEvent.KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_RIGHT:
+                    case KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER:
+                    case KeyGestureEvent.KEY_GESTURE_TYPE_BRIGHTNESS_UP:
+                    case KeyGestureEvent.KEY_GESTURE_TYPE_BRIGHTNESS_DOWN:
+                    case KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS_SWITCHER:
+                    case KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS:
+                    case KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_ALL_APPS:
+                    case KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SEARCH:
+                    case KeyGestureEvent.KEY_GESTURE_TYPE_LANGUAGE_SWITCH:
+                    case KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_UP:
+                    case KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_DOWN:
+                    case KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_TOGGLE:
+                    case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK:
+                        return true;
+                    default:
+                        return false;
+                }
+            }
+        });
+    }
+
+    @VisibleForTesting
+    boolean handleKeyGestureEvent(KeyGestureEvent event, IBinder focusedToken) {
+        boolean start = event.getAction() == KeyGestureEvent.ACTION_GESTURE_START;
+        boolean complete = event.getAction() == KeyGestureEvent.ACTION_GESTURE_COMPLETE
+                && !event.isCancelled();
+        int deviceId = event.getDeviceId();
+        int gestureType = event.getKeyGestureType();
+        int displayId = event.getDisplayId();
+        int modifierState = event.getModifierState();
+        boolean keyguardOn = keyguardOn();
+        switch (gestureType) {
+            case KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS:
+                if (complete) {
+                    showRecentApps(false);
+                }
+                return true;
+            case KeyGestureEvent.KEY_GESTURE_TYPE_APP_SWITCH:
+                if (!keyguardOn) {
+                    if (start) {
+                        preloadRecentApps();
+                    } else if (complete) {
+                        toggleRecentApps();
+                    }
+                }
+                return true;
+            case KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_ASSISTANT:
+                if (complete) {
+                    launchAssistAction(Intent.EXTRA_ASSIST_INPUT_HINT_KEYBOARD,
+                            deviceId, SystemClock.uptimeMillis(),
+                            AssistUtils.INVOCATION_TYPE_UNKNOWN);
+                }
+                return true;
+            case KeyGestureEvent.KEY_GESTURE_TYPE_HOME:
+                if (complete) {
+                    // Post to main thread to avoid blocking input pipeline.
+                    mHandler.post(() -> handleShortPressOnHome(displayId));
+                }
+                return true;
+            case KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS:
+                if (complete) {
+                    showSystemSettings();
+                }
+                return true;
+            case KeyGestureEvent.KEY_GESTURE_TYPE_LOCK_SCREEN:
+                if (complete) {
+                    lockNow(null /* options */);
+                }
+                return true;
+            case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL:
+                if (complete) {
+                    toggleNotificationPanel();
+                }
+                return true;
+            case KeyGestureEvent.KEY_GESTURE_TYPE_TAKE_SCREENSHOT:
+                if (complete) {
+                    interceptScreenshotChord(SCREENSHOT_KEY_OTHER, 0 /*pressDelay*/);
+                }
+                return true;
+            case KeyGestureEvent.KEY_GESTURE_TYPE_TRIGGER_BUG_REPORT:
+                if (complete && mEnableBugReportKeyboardShortcut) {
+                    try {
+                        mActivityManagerService.requestInteractiveBugReport();
+                    } catch (RemoteException e) {
+                        Slog.d(TAG, "Error taking bugreport", e);
+                    }
+                }
+                return true;
+            case KeyGestureEvent.KEY_GESTURE_TYPE_BACK:
+                if (complete) {
+                    injectBackGesture(SystemClock.uptimeMillis());
+                }
+                return true;
+            case KeyGestureEvent.KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION:
+                if (complete) {
+                    StatusBarManagerInternal statusbar = getStatusBarManagerInternal();
+                    if (statusbar != null) {
+                        statusbar.moveFocusedTaskToFullscreen(
+                                getTargetDisplayIdForKeyGestureEvent(event));
+                    }
+                }
+                return true;
+            case KeyGestureEvent.KEY_GESTURE_TYPE_DESKTOP_MODE:
+                if (complete) {
+                    StatusBarManagerInternal statusbar = getStatusBarManagerInternal();
+                    if (statusbar != null) {
+                        statusbar.moveFocusedTaskToDesktop(
+                                getTargetDisplayIdForKeyGestureEvent(event));
+                    }
+                }
+                return true;
+            case KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_LEFT:
+                if (complete) {
+                    moveFocusedTaskToStageSplit(getTargetDisplayIdForKeyGestureEvent(event),
+                            true /* leftOrTop */);
+                }
+                return true;
+            case KeyGestureEvent.KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_LEFT:
+                if (complete) {
+                    setSplitscreenFocus(true /* leftOrTop */);
+                }
+                return true;
+            case KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_RIGHT:
+                if (complete) {
+                    moveFocusedTaskToStageSplit(getTargetDisplayIdForKeyGestureEvent(event),
+                            false /* leftOrTop */);
+                }
+                return true;
+            case KeyGestureEvent.KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_RIGHT:
+                if (complete) {
+                    setSplitscreenFocus(false /* leftOrTop */);
+                }
+                return true;
+            case KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER:
+                if (complete) {
+                    toggleKeyboardShortcutsMenu(deviceId);
+                }
+                return true;
+            case KeyGestureEvent.KEY_GESTURE_TYPE_BRIGHTNESS_UP:
+            case KeyGestureEvent.KEY_GESTURE_TYPE_BRIGHTNESS_DOWN:
+                if (complete) {
+                    int direction =
+                            gestureType == KeyGestureEvent.KEY_GESTURE_TYPE_BRIGHTNESS_UP ? 1 : -1;
+                    changeDisplayBrightnessValue(displayId, direction);
+                }
+                return true;
+            case KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS_SWITCHER:
+                if (start) {
+                    showRecentApps(true);
+                } else {
+                    hideRecentApps(true, false);
+                }
+                return true;
+            case KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS:
+            case KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_ALL_APPS:
+                if (complete) {
+                    launchAllAppsAction();
+                }
+                return true;
+            case KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SEARCH:
+                if (complete) {
+                    launchTargetSearchActivity();
+                }
+                return true;
+            case KeyGestureEvent.KEY_GESTURE_TYPE_LANGUAGE_SWITCH:
+                if (complete) {
+                    int direction = (modifierState & KeyEvent.META_SHIFT_MASK) != 0 ? -1 : 1;
+                    sendSwitchKeyboardLayout(displayId, focusedToken, direction);
+                }
+                return true;
+            // TODO (b/358569822): Move these input specific gesture handling to IMS since we
+            //  are calling into InputManager through internal API anyways
+            case KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_UP:
+                if (complete) {
+                    mInputManagerInternal.incrementKeyboardBacklight(deviceId);
+                }
+                return true;
+            case KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_DOWN:
+                if (complete) {
+                    mInputManagerInternal.decrementKeyboardBacklight(deviceId);
+                }
+                return true;
+            case KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_TOGGLE:
+                return true;
+            case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK:
+                if (complete) {
+                    mInputManagerInternal.toggleCapsLock(deviceId);
+                }
+                return true;
+        }
+        return false;
+    }
+
+    private void changeDisplayBrightnessValue(int displayId, int direction) {
+        int screenDisplayId = displayId < 0 ? DEFAULT_DISPLAY : displayId;
+
+        float minLinearBrightness = mPowerManager.getBrightnessConstraint(
+                PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM);
+        float maxLinearBrightness = mPowerManager.getBrightnessConstraint(
+                PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MAXIMUM);
+        float linearBrightness = mDisplayManager.getBrightness(screenDisplayId);
+
+        float gammaBrightness = BrightnessUtils.convertLinearToGamma(linearBrightness);
+        float adjustedGammaBrightness = gammaBrightness + 1f / BRIGHTNESS_STEPS * direction;
+        adjustedGammaBrightness = MathUtils.constrain(adjustedGammaBrightness, 0f, 1f);
+        float adjustedLinearBrightness = BrightnessUtils.convertGammaToLinear(
+                adjustedGammaBrightness);
+        adjustedLinearBrightness = MathUtils.constrain(adjustedLinearBrightness,
+                minLinearBrightness, maxLinearBrightness);
+        mDisplayManager.setBrightness(screenDisplayId, adjustedLinearBrightness);
+
+        Intent intent = new Intent(Intent.ACTION_SHOW_BRIGHTNESS_DIALOG);
+        intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION
+                | Intent.FLAG_ACTIVITY_NO_USER_ACTION);
+        intent.putExtra(EXTRA_FROM_BRIGHTNESS_KEY, true);
+        startActivityAsUser(intent, UserHandle.CURRENT_OR_SELF);
+    }
+
     private boolean shouldInterceptShortcuts(IBinder focusedToken) {
         KeyInterceptionInfo info =
                 mWindowManagerInternal.getKeyInterceptionInfoFromToken(focusedToken);
@@ -4081,7 +4316,7 @@
                     if (KeyEvent.metaStateHasModifiers(metaState & ~KeyEvent.META_SHIFT_MASK,
                             KeyEvent.META_CTRL_ON)) {
                         int direction = (metaState & KeyEvent.META_SHIFT_MASK) != 0 ? -1 : 1;
-                        sendSwitchKeyboardLayout(event, focusedToken, direction);
+                        sendSwitchKeyboardLayout(event.getDisplayId(), focusedToken, direction);
                         return true;
                     }
                 }
@@ -4139,19 +4374,18 @@
         }
     }
 
-    private void sendSwitchKeyboardLayout(@NonNull KeyEvent event,
-            @Nullable IBinder focusedToken, int direction) {
+    private void sendSwitchKeyboardLayout(int displayId, @Nullable IBinder focusedToken,
+            int direction) {
         SwitchKeyboardLayoutMessageObject object =
-                new SwitchKeyboardLayoutMessageObject(event, focusedToken, direction);
+                new SwitchKeyboardLayoutMessageObject(displayId, focusedToken, direction);
         mHandler.obtainMessage(MSG_SWITCH_KEYBOARD_LAYOUT, object).sendToTarget();
     }
 
-    private void handleSwitchKeyboardLayout(@NonNull KeyEvent event, int direction,
-            IBinder focusedToken) {
+    private void handleSwitchKeyboardLayout(int displayId, int direction, IBinder focusedToken) {
         IBinder targetWindowToken =
                 mWindowManagerInternal.getTargetWindowTokenFromInputToken(focusedToken);
-        InputMethodManagerInternal.get().onSwitchKeyboardLayoutShortcut(direction,
-                event.getDisplayId(), targetWindowToken);
+        InputMethodManagerInternal.get().onSwitchKeyboardLayoutShortcut(direction, displayId,
+                targetWindowToken);
     }
 
     private boolean interceptFallback(IBinder focusedToken, KeyEvent fallbackEvent,
@@ -7000,16 +7234,22 @@
     }
 
     private int getTargetDisplayIdForKeyEvent(KeyEvent event) {
-        int displayId = event.getDisplayId();
-
-        if (displayId == INVALID_DISPLAY) {
-            displayId = mTopFocusedDisplayId;
+        if (event.getDisplayId() != INVALID_DISPLAY) {
+            return event.getDisplayId();
         }
-
-        if (displayId == INVALID_DISPLAY) {
-            return DEFAULT_DISPLAY;
-        } else {
-            return displayId;
+        if (mTopFocusedDisplayId != INVALID_DISPLAY) {
+            return mTopFocusedDisplayId;
         }
+        return DEFAULT_DISPLAY;
+    }
+
+    private int getTargetDisplayIdForKeyGestureEvent(KeyGestureEvent event) {
+        if (event.getDisplayId() != INVALID_DISPLAY) {
+            return event.getDisplayId();
+        }
+        if (mTopFocusedDisplayId != INVALID_DISPLAY) {
+            return mTopFocusedDisplayId;
+        }
+        return DEFAULT_DISPLAY;
     }
 }
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 185dd0c..ccc9b17 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -2641,7 +2641,7 @@
             return true;
         }
         // Only do transfer after transaction has done when starting window exist.
-        if (mStartingData != null && mStartingData.mWaitForSyncTransactionCommitCount > 0) {
+        if (mStartingData != null && mStartingData.mWaitForSyncTransactionCommit) {
             mStartingData.mRemoveAfterTransaction = AFTER_TRANSACTION_COPY_TO_CLIENT;
             return true;
         }
@@ -2804,11 +2804,9 @@
 
     @Override
     void waitForSyncTransactionCommit(ArraySet<WindowContainer> wcAwaitingCommit) {
-        // Only add once per transition.
-        final boolean added = wcAwaitingCommit.contains(this);
         super.waitForSyncTransactionCommit(wcAwaitingCommit);
-        if (!added && mStartingData != null) {
-            mStartingData.mWaitForSyncTransactionCommitCount++;
+        if (mStartingData != null) {
+            mStartingData.mWaitForSyncTransactionCommit = true;
         }
     }
 
@@ -2819,7 +2817,7 @@
             return;
         }
         final StartingData lastData = mStartingData;
-        lastData.mWaitForSyncTransactionCommitCount--;
+        lastData.mWaitForSyncTransactionCommit = false;
         if (lastData.mRemoveAfterTransaction == AFTER_TRANSACTION_REMOVE_DIRECTLY) {
             removeStartingWindowAnimation(lastData.mPrepareRemoveAnimation);
         } else if (lastData.mRemoveAfterTransaction == AFTER_TRANSACTION_COPY_TO_CLIENT) {
@@ -2849,7 +2847,7 @@
         final boolean animate;
         final boolean hasImeSurface;
         if (mStartingData != null) {
-            if (mStartingData.mWaitForSyncTransactionCommitCount > 0
+            if (mStartingData.mWaitForSyncTransactionCommit
                     || mSyncState != SYNC_STATE_NONE) {
                 mStartingData.mRemoveAfterTransaction = AFTER_TRANSACTION_REMOVE_DIRECTLY;
                 mStartingData.mPrepareRemoveAnimation = prepareAnimation;
diff --git a/services/core/java/com/android/server/wm/StartingData.java b/services/core/java/com/android/server/wm/StartingData.java
index 22c7e8c..24fb207 100644
--- a/services/core/java/com/android/server/wm/StartingData.java
+++ b/services/core/java/com/android/server/wm/StartingData.java
@@ -69,7 +69,7 @@
      * Note this isn't equal to transition playing, the period should be
      * Sync finishNow -> Start transaction apply.
      */
-    int mWaitForSyncTransactionCommitCount;
+    boolean mWaitForSyncTransactionCommit;
 
     /**
      * For Shell transition.
@@ -112,7 +112,7 @@
     public String toString() {
         return getClass().getSimpleName() + "{"
                 + Integer.toHexString(System.identityHashCode(this))
-                + " mWaitForSyncTransactionCommitCount=" + mWaitForSyncTransactionCommitCount
+                + " waitForSyncTransactionCommit=" + mWaitForSyncTransactionCommit
                 + " removeAfterTransaction= " + mRemoveAfterTransaction
                 + "}";
     }
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 4eba36f..655a6fb 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -2875,19 +2875,12 @@
             return out;
         }
 
-        // Get the animation theme from the top-most application window
-        // when Flags.customAnimationsBehindTranslucent() is false.
         final AnimationOptions animOptionsForActivityTransition =
                 calculateAnimationOptionsForActivityTransition(type, sortedTargets);
-
         if (!Flags.moveAnimationOptionsToChange() && animOptionsForActivityTransition != null) {
             out.setAnimationOptions(animOptionsForActivityTransition);
         }
 
-        // Store the animation options of the topmost non-translucent change
-        // (Used when Flags.customAnimationsBehindTranslucent() is true)
-        AnimationOptions activityAboveAnimationOptions = null;
-
         final ArraySet<WindowContainer> occludedAtEndContainers = new ArraySet<>();
         // Convert all the resolved ChangeInfos into TransactionInfo.Change objects in order.
         final int count = sortedTargets.size();
@@ -3006,26 +2999,9 @@
                 change.setBackgroundColor(ColorUtils.setAlphaComponent(backgroundColor, 255));
             }
 
-            // Calculate the animation options for this change
+            AnimationOptions animOptions = null;
             if (Flags.moveAnimationOptionsToChange()) {
-                AnimationOptions animOptions = null;
-                if (Flags.customAnimationsBehindTranslucent() && activityRecord != null) {
-                    if (activityAboveAnimationOptions != null) {
-                        // Inherit the options from one of the changes on top of this
-                        animOptions = activityAboveAnimationOptions;
-                    } else {
-                        // Create the options based on this change's custom animations and layout
-                        // parameters
-                        animOptions = getOptions(activityRecord /* customAnimActivity */,
-                                activityRecord /* animLpActivity */);
-                        if (!change.hasFlags(FLAG_TRANSLUCENT)) {
-                            // If this change is not translucent, its options are going to be
-                            // inherited by the changes below
-                            activityAboveAnimationOptions = animOptions;
-                        }
-                    }
-                } else if (activityRecord != null && animOptionsForActivityTransition != null) {
-                    // Use the same options from the top activity for all the activities
+                if (activityRecord != null && animOptionsForActivityTransition != null) {
                     animOptions = animOptionsForActivityTransition;
                 } else if (Flags.activityEmbeddingOverlayPresentationFlag()
                         && isEmbeddedTaskFragment) {
@@ -3074,42 +3050,28 @@
     @Nullable
     private static AnimationOptions calculateAnimationOptionsForActivityTransition(
             @TransitionType int type, @NonNull ArrayList<ChangeInfo> sortedTargets) {
+        TransitionInfo.AnimationOptions animOptions = null;
+
+        // Check if the top-most app is an activity (ie. activity->activity). If so, make sure
+        // to honor its custom transition options.
         WindowContainer<?> topApp = null;
         for (int i = 0; i < sortedTargets.size(); i++) {
-            if (!isWallpaper(sortedTargets.get(i).mContainer)) {
-                topApp = sortedTargets.get(i).mContainer;
-                break;
-            }
+            if (isWallpaper(sortedTargets.get(i).mContainer)) continue;
+            topApp = sortedTargets.get(i).mContainer;
+            break;
         }
-        ActivityRecord animLpActivity = findAnimLayoutParamsActivityRecord(type, sortedTargets);
-        return getOptions(topApp.asActivityRecord() /* customAnimActivity */,
-                animLpActivity /* animLpActivity */);
-    }
-
-    /**
-     * Updates and returns animOptions with the layout parameters of animLpActivity
-     * @param customAnimActivity the activity that drives the custom animation options
-     * @param animLpActivity the activity that drives the animation options with its layout
-     *                       parameters
-     * @return the options extracted from the provided activities
-     */
-    @Nullable
-    private static AnimationOptions getOptions(@Nullable ActivityRecord customAnimActivity,
-            @Nullable ActivityRecord animLpActivity) {
-        AnimationOptions animOptions = null;
-        // Custom
-        if (customAnimActivity != null) {
-            animOptions = addCustomActivityTransition(customAnimActivity, true /* open */,
-                    animOptions);
-            animOptions = addCustomActivityTransition(customAnimActivity, false /* open */,
+        if (topApp.asActivityRecord() != null) {
+            final ActivityRecord topActivity = topApp.asActivityRecord();
+            animOptions = addCustomActivityTransition(topActivity, true/* open */,
+                    null /* animOptions */);
+            animOptions = addCustomActivityTransition(topActivity, false/* open */,
                     animOptions);
         }
-
-        // Layout parameters
+        final ActivityRecord animLpActivity =
+                findAnimLayoutParamsActivityRecord(type, sortedTargets);
         final WindowState mainWindow = animLpActivity != null
                 ? animLpActivity.findMainWindow() : null;
-        final WindowManager.LayoutParams animLp = mainWindow != null ? mainWindow.mAttrs : null;
-
+        WindowManager.LayoutParams animLp = mainWindow != null ? mainWindow.mAttrs : null;
         if (animLp != null && animLp.type != TYPE_APPLICATION_STARTING
                 && animLp.windowAnimations != 0) {
             // Don't send animation options if no windowAnimations have been set or if the we
@@ -3249,9 +3211,10 @@
         return ancestor;
     }
 
-    @Nullable
-    private static ActivityRecord findAnimLayoutParamsActivityRecord(
-            @TransitionType int transit, @NonNull List<ChangeInfo> sortedTargets) {
+    private static ActivityRecord findAnimLayoutParamsActivityRecord(int type,
+            ArrayList<ChangeInfo> sortedTargets) {
+        // Find the layout params of the top-most application window that is part of the
+        // transition, which is what will control the animation theme.
         final ArraySet<Integer> activityTypes = new ArraySet<>();
         final int targetCount = sortedTargets.size();
         for (int i = 0; i < targetCount; ++i) {
@@ -3271,7 +3234,12 @@
             // activity through the layout parameter animation style.
             return null;
         }
+        return findAnimLayoutParamsActivityRecord(sortedTargets, type, activityTypes);
+    }
 
+    private static ActivityRecord findAnimLayoutParamsActivityRecord(
+            List<ChangeInfo> sortedTargets,
+            @TransitionType int transit, ArraySet<Integer> activityTypes) {
         // Remote animations always win, but fullscreen windows override non-fullscreen windows.
         ActivityRecord result = lookForTopWindowWithFilter(sortedTargets,
                 w -> w.getRemoteAnimationDefinition() != null
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index b6e45fc8..6314b85 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -10625,8 +10625,16 @@
                 final DevicePolicyData policyData = getUserData(userId);
                 if (transitionCheckNeeded) {
                     // Optional state transition check for non-ADB case.
-                    checkUserProvisioningStateTransition(policyData.mUserProvisioningState,
-                            newState);
+                    try {
+                        checkUserProvisioningStateTransition(
+                                policyData.mUserProvisioningState,
+                                newState);
+
+                    } catch (IllegalStateException e) {
+                        Slogf.e(LOG_TAG,
+                                "Exception caught while changing provisioning state", e);
+                        throw e;
+                    }
                 }
                 policyData.mUserProvisioningState = newState;
                 saveSettingsLocked(userId);
@@ -10637,6 +10645,10 @@
     }
 
     private void checkUserProvisioningStateTransition(int currentState, int newState) {
+        if (Flags.userProvisioningSameState()) {
+            Preconditions.checkState(newState != currentState, "New state cannot"
+                    + " be the same as the current state: [" + newState + "]");
+        }
         // Valid transitions for normal use-cases.
         switch (currentState) {
             case DevicePolicyManager.STATE_USER_UNMANAGED:
diff --git a/services/foldables/devicestateprovider/src/com/android/server/policy/feature/Android.bp b/services/foldables/devicestateprovider/src/com/android/server/policy/feature/Android.bp
index 1db9e8d..6393e11 100644
--- a/services/foldables/devicestateprovider/src/com/android/server/policy/feature/Android.bp
+++ b/services/foldables/devicestateprovider/src/com/android/server/policy/feature/Android.bp
@@ -1,7 +1,7 @@
 aconfig_declarations {
     name: "device_state_flags",
     package: "com.android.server.policy.feature.flags",
-    container: "system_ext",
+    container: "system",
     srcs: [
         "device_state_flags.aconfig",
     ],
diff --git a/services/foldables/devicestateprovider/src/com/android/server/policy/feature/device_state_flags.aconfig b/services/foldables/devicestateprovider/src/com/android/server/policy/feature/device_state_flags.aconfig
index f827b55..21e33dd 100644
--- a/services/foldables/devicestateprovider/src/com/android/server/policy/feature/device_state_flags.aconfig
+++ b/services/foldables/devicestateprovider/src/com/android/server/policy/feature/device_state_flags.aconfig
@@ -1,5 +1,5 @@
 package: "com.android.server.policy.feature.flags"
-container: "system_ext"
+container: "system"
 
 flag {
     name: "enable_dual_display_blocking"
diff --git a/services/tests/wmtests/src/com/android/server/policy/KeyGestureEventTests.java b/services/tests/wmtests/src/com/android/server/policy/KeyGestureEventTests.java
index 8f3adba..3d978e4 100644
--- a/services/tests/wmtests/src/com/android/server/policy/KeyGestureEventTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/KeyGestureEventTests.java
@@ -16,6 +16,8 @@
 
 package com.android.server.policy;
 
+import static android.view.Display.DEFAULT_DISPLAY;
+
 import static com.android.server.policy.PhoneWindowManager.DOUBLE_TAP_HOME_RECENT_SYSTEM_UI;
 import static com.android.server.policy.PhoneWindowManager.LONG_PRESS_HOME_ALL_APPS;
 import static com.android.server.policy.PhoneWindowManager.LONG_PRESS_HOME_ASSIST;
@@ -23,21 +25,21 @@
 import static com.android.server.policy.PhoneWindowManager.SETTINGS_KEY_BEHAVIOR_NOTIFICATION_PANEL;
 
 import android.hardware.input.KeyGestureEvent;
+import android.os.RemoteException;
+import android.platform.test.annotations.EnableFlags;
 import android.platform.test.annotations.Presubmit;
-import android.platform.test.annotations.RequiresFlagsEnabled;
-import android.platform.test.flag.junit.CheckFlagsRule;
-import android.platform.test.flag.junit.DeviceFlagsValueProvider;
 import android.view.KeyEvent;
 
 import androidx.test.filters.MediumTest;
 
 import com.android.internal.annotations.Keep;
 
+import junit.framework.Assert;
+
 import junitparams.JUnitParamsRunner;
 import junitparams.Parameters;
 
 import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -46,10 +48,6 @@
 @RunWith(JUnitParamsRunner.class)
 public class KeyGestureEventTests extends ShortcutKeyTestBase {
 
-    @Rule
-    public final CheckFlagsRule mCheckFlagsRule =
-            DeviceFlagsValueProvider.createCheckFlagsRule();
-
     private static final int META_KEY = KeyEvent.KEYCODE_META_LEFT;
     private static final int META_ON = MODIFIER.get(KeyEvent.KEYCODE_META_LEFT);
     private static final int ALT_KEY = KeyEvent.KEYCODE_ALT_LEFT;
@@ -149,9 +147,9 @@
                 {"VOLUME_MUTE key -> Mute Volume", new int[]{KeyEvent.KEYCODE_VOLUME_MUTE},
                         KeyGestureEvent.KEY_GESTURE_TYPE_VOLUME_MUTE,
                         KeyEvent.KEYCODE_VOLUME_MUTE, 0},
-                {"ALL_APPS key -> Open App Drawer in Accessibility mode",
+                {"ALL_APPS key -> Open App Drawer",
                         new int[]{KeyEvent.KEYCODE_ALL_APPS},
-                        KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_ALL_APPS,
+                        KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS,
                         KeyEvent.KEYCODE_ALL_APPS, 0},
                 {"SEARCH key -> Launch Search Activity", new int[]{KeyEvent.KEYCODE_SEARCH},
                         KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SEARCH,
@@ -160,8 +158,8 @@
                         new int[]{KeyEvent.KEYCODE_LANGUAGE_SWITCH},
                         KeyGestureEvent.KEY_GESTURE_TYPE_LANGUAGE_SWITCH,
                         KeyEvent.KEYCODE_LANGUAGE_SWITCH, 0},
-                {"META key -> Open App Drawer in Accessibility mode", new int[]{META_KEY},
-                        KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_ALL_APPS, META_KEY,
+                {"META key -> Open App Drawer", new int[]{META_KEY},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS, META_KEY,
                         META_ON},
                 {"Meta + Alt -> Toggle CapsLock", new int[]{META_KEY, ALT_KEY},
                         KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK, ALT_KEY,
@@ -182,12 +180,12 @@
                         META_ON | CTRL_ON},
                 {"Meta + Ctrl + DPAD_LEFT -> Split screen navigation",
                         new int[]{META_KEY, CTRL_KEY, KeyEvent.KEYCODE_DPAD_LEFT},
-                        KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION,
+                        KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_LEFT,
                         KeyEvent.KEYCODE_DPAD_LEFT,
                         META_ON | CTRL_ON},
                 {"Meta + Ctrl + DPAD_RIGHT -> Split screen navigation",
                         new int[]{META_KEY, CTRL_KEY, KeyEvent.KEYCODE_DPAD_RIGHT},
-                        KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION,
+                        KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_RIGHT,
                         KeyEvent.KEYCODE_DPAD_RIGHT,
                         META_ON | CTRL_ON},
                 {"Meta + L -> Lock Homescreen", new int[]{META_KEY, KeyEvent.KEYCODE_L},
@@ -320,18 +318,18 @@
                         new int[]{META_KEY, KeyEvent.KEYCODE_H}, LONG_PRESS_HOME_ASSIST,
                         KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_ASSISTANT, KeyEvent.KEYCODE_H,
                         META_ON},
-                {"Long press HOME key -> Open App Drawer in Accessibility mode",
+                {"Long press HOME key -> Open App Drawer",
                         new int[]{KeyEvent.KEYCODE_HOME}, LONG_PRESS_HOME_ALL_APPS,
-                        KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_ALL_APPS,
+                        KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS,
                         KeyEvent.KEYCODE_HOME, 0},
-                {"Long press META + ENTER -> Open App Drawer in Accessibility mode",
+                {"Long press META + ENTER -> Open App Drawer",
                         new int[]{META_KEY, KeyEvent.KEYCODE_ENTER}, LONG_PRESS_HOME_ALL_APPS,
-                        KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_ALL_APPS,
+                        KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS,
                         KeyEvent.KEYCODE_ENTER, META_ON},
-                {"Long press META + H -> Open App Drawer in Accessibility mode",
+                {"Long press META + H -> Open App Drawer",
                         new int[]{META_KEY, KeyEvent.KEYCODE_H},
                         LONG_PRESS_HOME_ALL_APPS,
-                        KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_ALL_APPS,
+                        KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS,
                         KeyEvent.KEYCODE_H, META_ON}};
     }
 
@@ -428,7 +426,7 @@
     }
 
     @Test
-    @RequiresFlagsEnabled(com.android.server.flags.Flags.FLAG_NEW_BUGREPORT_KEYBOARD_SHORTCUT)
+    @EnableFlags(com.android.server.flags.Flags.FLAG_NEW_BUGREPORT_KEYBOARD_SHORTCUT)
     public void testBugreportShortcutPress() {
         testShortcutInternal("Meta + Ctrl + Del -> Trigger bug report",
                 new int[]{META_KEY, CTRL_KEY, KeyEvent.KEYCODE_DEL},
@@ -444,4 +442,161 @@
                 new int[]{expectedKey}, expectedModifierState, expectedKeyGestureType,
                 "Failed while executing " + testName);
     }
+
+    @Test
+    public void testKeyGestureRecentApps() {
+        Assert.assertTrue(
+                sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS));
+        mPhoneWindowManager.assertShowRecentApps();
+    }
+
+    @Test
+    public void testKeyGestureAppSwitch() {
+        Assert.assertTrue(
+                sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_APP_SWITCH));
+        mPhoneWindowManager.assertToggleRecentApps();
+    }
+
+    @Test
+    public void testKeyGestureLaunchAssistant() {
+        Assert.assertTrue(
+                sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_ASSISTANT));
+        mPhoneWindowManager.assertSearchManagerLaunchAssist();
+    }
+
+    @Test
+    public void testKeyGestureGoHome() {
+        Assert.assertTrue(
+                sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_HOME));
+        mPhoneWindowManager.assertGoToHomescreen();
+    }
+
+    @Test
+    public void testKeyGestureLaunchSystemSettings() {
+        Assert.assertTrue(
+                sendKeyGestureEventComplete(
+                        KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS));
+        mPhoneWindowManager.assertLaunchSystemSettings();
+    }
+
+    @Test
+    public void testKeyGestureLock() {
+        Assert.assertTrue(
+                sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_LOCK_SCREEN));
+        mPhoneWindowManager.assertLockedAfterAppTransitionFinished();
+    }
+
+    @Test
+    public void testKeyGestureToggleNotificationPanel() throws RemoteException {
+        Assert.assertTrue(
+                sendKeyGestureEventComplete(
+                        KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL));
+        mPhoneWindowManager.assertTogglePanel();
+    }
+
+    @Test
+    public void testKeyGestureScreenshot() {
+        Assert.assertTrue(
+                sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_TAKE_SCREENSHOT));
+        mPhoneWindowManager.assertTakeScreenshotCalled();
+    }
+
+    @Test
+    public void testKeyGestureTriggerBugReport() throws RemoteException {
+        Assert.assertTrue(
+                sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_TRIGGER_BUG_REPORT));
+        mPhoneWindowManager.assertTakeBugreport(true);
+    }
+
+    @Test
+    public void testKeyGestureBack() {
+        Assert.assertTrue(sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_BACK));
+        mPhoneWindowManager.assertBackEventInjected();
+    }
+
+    @Test
+    public void testKeyGestureMultiWindowNavigation() {
+        Assert.assertTrue(sendKeyGestureEventComplete(
+                KeyGestureEvent.KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION));
+        mPhoneWindowManager.assertMoveFocusedTaskToFullscreen();
+    }
+
+    @Test
+    public void testKeyGestureDesktopMode() {
+        Assert.assertTrue(
+                sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_DESKTOP_MODE));
+        mPhoneWindowManager.assertMoveFocusedTaskToDesktop();
+    }
+
+    @Test
+    public void testKeyGestureSplitscreenNavigation() {
+        Assert.assertTrue(sendKeyGestureEventComplete(
+                KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_LEFT));
+        mPhoneWindowManager.assertMoveFocusedTaskToStageSplit(true);
+
+        Assert.assertTrue(sendKeyGestureEventComplete(
+                KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_RIGHT));
+        mPhoneWindowManager.assertMoveFocusedTaskToStageSplit(false);
+    }
+
+    @Test
+    public void testKeyGestureSplitscreenFocus() {
+        Assert.assertTrue(sendKeyGestureEventComplete(
+                KeyGestureEvent.KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_LEFT));
+        mPhoneWindowManager.assertSetSplitscreenFocus(true);
+
+        Assert.assertTrue(sendKeyGestureEventComplete(
+                KeyGestureEvent.KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_RIGHT));
+        mPhoneWindowManager.assertSetSplitscreenFocus(false);
+    }
+
+    @Test
+    public void testKeyGestureShortcutHelper() {
+        Assert.assertTrue(sendKeyGestureEventComplete(
+                KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER));
+        mPhoneWindowManager.assertToggleShortcutsMenu();
+    }
+
+    @Test
+    public void testKeyGestureBrightnessChange() {
+        float[] currentBrightness = new float[]{0.1f, 0.05f, 0.0f};
+        float[] newBrightness = new float[]{0.065738f, 0.0275134f, 0.0f};
+
+        for (int i = 0; i < currentBrightness.length; i++) {
+            mPhoneWindowManager.prepareBrightnessDecrease(currentBrightness[i]);
+            Assert.assertTrue(
+                    sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_BRIGHTNESS_DOWN));
+            mPhoneWindowManager.verifyNewBrightness(newBrightness[i]);
+        }
+    }
+
+    @Test
+    public void testKeyGestureRecentAppSwitcher() {
+        Assert.assertTrue(sendKeyGestureEventStart(
+                KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS_SWITCHER));
+        mPhoneWindowManager.assertShowRecentApps();
+
+        Assert.assertTrue(sendKeyGestureEventComplete(
+                KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS_SWITCHER));
+        mPhoneWindowManager.assertHideRecentApps();
+    }
+
+    @Test
+    public void testKeyGestureLanguageSwitch() {
+        Assert.assertTrue(
+                sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_LANGUAGE_SWITCH));
+        mPhoneWindowManager.assertSwitchKeyboardLayout(1, DEFAULT_DISPLAY);
+
+        Assert.assertTrue(
+                sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_LANGUAGE_SWITCH,
+                        KeyEvent.META_SHIFT_ON));
+        mPhoneWindowManager.assertSwitchKeyboardLayout(-1, DEFAULT_DISPLAY);
+    }
+
+    @Test
+    public void testKeyGestureLaunchSearch() {
+        Assert.assertTrue(
+                sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SEARCH));
+        mPhoneWindowManager.assertLaunchSearch();
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTests.java b/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTests.java
index 536dcfb..af3dc1d 100644
--- a/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTests.java
@@ -46,6 +46,7 @@
 import android.app.ActivityManager;
 import android.app.AppOpsManager;
 import android.content.Context;
+import android.hardware.input.InputManager;
 import android.os.PowerManager;
 import android.platform.test.flag.junit.SetFlagsRule;
 
@@ -95,6 +96,9 @@
         mStatusBarManagerInternal = mock(StatusBarManagerInternal.class);
         LocalServices.addService(StatusBarManagerInternal.class, mStatusBarManagerInternal);
         mPhoneWindowManager.mKeyguardDelegate = mock(KeyguardServiceDelegate.class);
+        final InputManager im = mock(InputManager.class);
+        doNothing().when(im).registerKeyGestureEventHandler(any());
+        doReturn(im).when(mContext).getSystemService(eq(Context.INPUT_SERVICE));
     }
 
     @After
diff --git a/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java b/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java
index 37e4fd6..50b7db4 100644
--- a/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java
+++ b/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java
@@ -56,6 +56,7 @@
 import android.content.pm.PackageManager;
 import android.content.res.Resources;
 import android.content.res.XmlResourceParser;
+import android.hardware.input.KeyGestureEvent;
 import android.platform.test.flag.junit.SetFlagsRule;
 import android.util.ArrayMap;
 import android.view.InputDevice;
@@ -228,6 +229,31 @@
         sendKeyCombination(new int[]{keyCode}, 0 /*durationMillis*/, longPress, DEFAULT_DISPLAY);
     }
 
+    boolean sendKeyGestureEventStart(int gestureType) {
+        return mPhoneWindowManager.sendKeyGestureEvent(
+                new KeyGestureEvent.Builder().setKeyGestureType(gestureType).setAction(
+                        KeyGestureEvent.ACTION_GESTURE_START).build());
+    }
+
+    boolean sendKeyGestureEventComplete(int gestureType) {
+        return mPhoneWindowManager.sendKeyGestureEvent(
+                new KeyGestureEvent.Builder().setKeyGestureType(gestureType).setAction(
+                        KeyGestureEvent.ACTION_GESTURE_COMPLETE).build());
+    }
+
+    boolean sendKeyGestureEventComplete(int gestureType, int modifierState) {
+        return mPhoneWindowManager.sendKeyGestureEvent(
+                new KeyGestureEvent.Builder().setModifierState(modifierState).setKeyGestureType(
+                        gestureType).setAction(KeyGestureEvent.ACTION_GESTURE_COMPLETE).build());
+    }
+
+    boolean sendKeyGestureEventComplete(int keycode, int modifierState, int gestureType) {
+        return mPhoneWindowManager.sendKeyGestureEvent(
+                new KeyGestureEvent.Builder().setKeycodes(new int[]{keycode}).setModifierState(
+                        modifierState).setKeyGestureType(gestureType).setAction(
+                        KeyGestureEvent.ACTION_GESTURE_COMPLETE).build());
+    }
+
     /**
      * Since we use SettingsProviderRule to mock the ContentResolver in these
      * tests, the settings observer registered by PhoneWindowManager will not
diff --git a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
index 79c7ac1..0b55e2b 100644
--- a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
+++ b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
@@ -71,6 +71,7 @@
 import android.hardware.display.DisplayManager;
 import android.hardware.display.DisplayManagerInternal;
 import android.hardware.input.InputManager;
+import android.hardware.input.KeyGestureEvent;
 import android.media.AudioManagerInternal;
 import android.os.Handler;
 import android.os.HandlerThread;
@@ -83,9 +84,11 @@
 import android.os.Vibrator;
 import android.os.VibratorInfo;
 import android.os.test.TestLooper;
+import android.provider.Settings;
 import android.service.dreams.DreamManagerInternal;
 import android.telecom.TelecomManager;
 import android.view.Display;
+import android.view.InputEvent;
 import android.view.KeyEvent;
 import android.view.accessibility.AccessibilityManager;
 import android.view.autofill.AutofillManagerInternal;
@@ -118,6 +121,7 @@
 import org.mockito.MockitoAnnotations;
 import org.mockito.quality.Strictness;
 
+import java.util.List;
 import java.util.function.Supplier;
 
 class TestPhoneWindowManager {
@@ -298,6 +302,8 @@
         doReturn(mAppOpsManager).when(mContext).getSystemService(eq(AppOpsManager.class));
         doReturn(mDisplayManager).when(mContext).getSystemService(eq(DisplayManager.class));
         doReturn(mInputManager).when(mContext).getSystemService(eq(InputManager.class));
+        doNothing().when(mInputManager).registerKeyGestureEventHandler(any());
+        doNothing().when(mInputManager).unregisterKeyGestureEventHandler(any());
         doReturn(mPackageManager).when(mContext).getPackageManager();
         doReturn(mSensorPrivacyManager).when(mContext).getSystemService(
                 eq(SensorPrivacyManager.class));
@@ -417,6 +423,10 @@
         mPhoneWindowManager.dispatchUnhandledKey(mInputToken, event, FLAG_INTERACTIVE);
     }
 
+    boolean sendKeyGestureEvent(KeyGestureEvent event) {
+        return mPhoneWindowManager.handleKeyGestureEvent(event, mInputToken);
+    }
+
     /**
      * Provide access to the SettingsObserver so that tests can manually trigger Settings changes.
      */
@@ -584,6 +594,16 @@
         doReturn(true).when(mInputManager).injectInputEvent(any(KeyEvent.class), anyInt());
     }
 
+    void assertBackEventInjected() {
+        ArgumentCaptor<InputEvent> intentCaptor = ArgumentCaptor.forClass(InputEvent.class);
+        verify(mInputManager, times(2)).injectInputEvent(intentCaptor.capture(), anyInt());
+        List<InputEvent> inputEvents = intentCaptor.getAllValues();
+        Assert.assertEquals(KeyEvent.KEYCODE_BACK, ((KeyEvent) inputEvents.get(0)).getKeyCode());
+        Assert.assertEquals(KeyEvent.KEYCODE_BACK, ((KeyEvent) inputEvents.get(1)).getKeyCode());
+        // Reset verifier for next call.
+        Mockito.clearInvocations(mContext);
+    }
+
     void overrideSearchKeyBehavior(int behavior) {
         mPhoneWindowManager.mSearchKeyBehavior = behavior;
     }
@@ -685,6 +705,24 @@
         verify(mSearchManager).launchAssist(any());
     }
 
+    void assertLaunchSystemSettings() {
+        mTestLooper.dispatchAll();
+        ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
+        verify(mContext).startActivityAsUser(intentCaptor.capture(), any(), any());
+        Assert.assertEquals(Settings.ACTION_SETTINGS, intentCaptor.getValue().getAction());
+        // Reset verifier for next call.
+        Mockito.clearInvocations(mContext);
+    }
+
+    void assertLaunchSearch() {
+        mTestLooper.dispatchAll();
+        ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
+        verify(mContext).startActivityAsUser(intentCaptor.capture(), any(), any());
+        Assert.assertEquals(Intent.ACTION_WEB_SEARCH, intentCaptor.getValue().getAction());
+        // Reset verifier for next call.
+        Mockito.clearInvocations(mContext);
+    }
+
     void assertLaunchCategory(String category) {
         mTestLooper.dispatchAll();
         ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
@@ -725,6 +763,36 @@
         verify(mStatusBarManagerInternal).showRecentApps(anyBoolean());
     }
 
+    void assertHideRecentApps() {
+        mTestLooper.dispatchAll();
+        verify(mStatusBarManagerInternal).hideRecentApps(anyBoolean(), anyBoolean());
+    }
+
+    void assertToggleRecentApps() {
+        mTestLooper.dispatchAll();
+        verify(mStatusBarManagerInternal).toggleRecentApps();
+    }
+
+    void assertMoveFocusedTaskToDesktop() {
+        mTestLooper.dispatchAll();
+        verify(mStatusBarManagerInternal).moveFocusedTaskToDesktop(anyInt());
+    }
+
+    void assertMoveFocusedTaskToFullscreen() {
+        mTestLooper.dispatchAll();
+        verify(mStatusBarManagerInternal).moveFocusedTaskToFullscreen(anyInt());
+    }
+
+    void assertMoveFocusedTaskToStageSplit(boolean leftOrTop) {
+        mTestLooper.dispatchAll();
+        verify(mStatusBarManagerInternal).moveFocusedTaskToStageSplit(anyInt(), eq(leftOrTop));
+    }
+
+    void assertSetSplitscreenFocus(boolean leftOrTop) {
+        mTestLooper.dispatchAll();
+        verify(mStatusBarManagerInternal).setSplitscreenFocus(eq(leftOrTop));
+    }
+
     void assertStatusBarStartAssist() {
         mTestLooper.dispatchAll();
         verify(mStatusBarManagerInternal).startAssist(any());
diff --git a/tests/FlickerTests/test-apps/flickerapp/Android.bp b/tests/FlickerTests/test-apps/flickerapp/Android.bp
index e3b23b9..a186679 100644
--- a/tests/FlickerTests/test-apps/flickerapp/Android.bp
+++ b/tests/FlickerTests/test-apps/flickerapp/Android.bp
@@ -19,6 +19,7 @@
     // to get the below license kinds:
     //   SPDX-license-identifier-Apache-2.0
     default_applicable_licenses: ["frameworks_base_license"],
+    default_team: "trendy_team_windowing_tools",
 }
 
 android_test {
diff --git a/tests/Input/Android.bp b/tests/Input/Android.bp
index f3e040a..6742cbe 100644
--- a/tests/Input/Android.bp
+++ b/tests/Input/Android.bp
@@ -39,6 +39,7 @@
         "flag-junit",
         "frameworks-base-testutils",
         "hamcrest-library",
+        "junit-params",
         "kotlin-test",
         "mockito-target-extended-minus-junit4",
         "platform-test-annotations",
diff --git a/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt b/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt
index e126797..28550cd 100644
--- a/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt
+++ b/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt
@@ -25,19 +25,27 @@
 import android.hardware.input.InputManager
 import android.hardware.input.InputManagerGlobal
 import android.hardware.input.KeyGestureEvent
+import android.hardware.input.KeyGestureEvent.KeyGestureType
 import android.os.IBinder
 import android.os.Process
 import android.os.test.TestLooper
 import android.platform.test.annotations.Presubmit
 import android.view.InputDevice
+import android.view.KeyCharacterMap
 import android.view.KeyEvent
 import androidx.test.core.app.ApplicationProvider
+import com.android.internal.annotations.Keep
 import com.android.internal.util.FrameworkStatsLog
 import com.android.modules.utils.testing.ExtendedMockitoRule
+import junitparams.JUnitParamsRunner
+import junitparams.Parameters
+import org.junit.Assert.assertArrayEquals
 import org.junit.Assert.assertEquals
+import org.junit.Assert.assertTrue
 import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
+import org.junit.runner.RunWith
 import org.mockito.Mock
 import org.mockito.Mockito
 
@@ -48,6 +56,7 @@
  * atest InputTests:KeyGestureControllerTests
  */
 @Presubmit
+@RunWith(JUnitParamsRunner::class)
 class KeyGestureControllerTests {
 
     companion object {
@@ -59,6 +68,16 @@
             .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_HOME)
             .setAction(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
             .build()
+        val MODIFIER = mapOf(
+            KeyEvent.KEYCODE_CTRL_LEFT to (KeyEvent.META_CTRL_LEFT_ON or KeyEvent.META_CTRL_ON),
+            KeyEvent.KEYCODE_CTRL_RIGHT to (KeyEvent.META_CTRL_RIGHT_ON or KeyEvent.META_CTRL_ON),
+            KeyEvent.KEYCODE_ALT_LEFT to (KeyEvent.META_ALT_LEFT_ON or KeyEvent.META_ALT_ON),
+            KeyEvent.KEYCODE_ALT_RIGHT to (KeyEvent.META_ALT_RIGHT_ON or KeyEvent.META_ALT_ON),
+            KeyEvent.KEYCODE_SHIFT_LEFT to (KeyEvent.META_SHIFT_LEFT_ON or KeyEvent.META_SHIFT_ON),
+            KeyEvent.KEYCODE_SHIFT_RIGHT to (KeyEvent.META_SHIFT_RIGHT_ON or KeyEvent.META_SHIFT_ON),
+            KeyEvent.KEYCODE_META_LEFT to (KeyEvent.META_META_LEFT_ON or KeyEvent.META_META_ON),
+            KeyEvent.KEYCODE_META_RIGHT to (KeyEvent.META_META_RIGHT_ON or KeyEvent.META_META_ON),
+        )
     }
 
     @JvmField
@@ -75,6 +94,7 @@
     private lateinit var inputManagerGlobalSession: InputManagerGlobal.TestSession
     private lateinit var testLooper: TestLooper
     private var events = mutableListOf<KeyGestureEvent>()
+    private var handleEvents = mutableListOf<KeyGestureEvent>()
 
     @Before
     fun setup() {
@@ -184,6 +204,440 @@
         )
     }
 
+    class TestData(
+        val name: String,
+        val keys: IntArray,
+        val expectedKeyGestureType: Int,
+        val expectedKeys: IntArray,
+        val expectedModifierState: Int,
+        val expectedActions: IntArray,
+    ) {
+        override fun toString(): String = name
+    }
+
+    @Keep
+    private fun keyGestureEventHandlerTestArguments(): Array<TestData> {
+        return arrayOf(
+            TestData(
+                "META + A -> Launch Assistant",
+                intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_A),
+                KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_ASSISTANT,
+                intArrayOf(KeyEvent.KEYCODE_A),
+                KeyEvent.META_META_ON,
+                intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+            ),
+            TestData(
+                "RECENT_APPS -> Show Overview",
+                intArrayOf(KeyEvent.KEYCODE_RECENT_APPS),
+                KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS,
+                intArrayOf(KeyEvent.KEYCODE_RECENT_APPS),
+                0,
+                intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+            ),
+            TestData(
+                "APP_SWITCH -> App Switch",
+                intArrayOf(KeyEvent.KEYCODE_APP_SWITCH),
+                KeyGestureEvent.KEY_GESTURE_TYPE_APP_SWITCH,
+                intArrayOf(KeyEvent.KEYCODE_APP_SWITCH),
+                0,
+                intArrayOf(
+                    KeyGestureEvent.ACTION_GESTURE_START,
+                    KeyGestureEvent.ACTION_GESTURE_COMPLETE
+                )
+            ),
+            TestData(
+                "META + H -> Go Home",
+                intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_H),
+                KeyGestureEvent.KEY_GESTURE_TYPE_HOME,
+                intArrayOf(KeyEvent.KEYCODE_H),
+                KeyEvent.META_META_ON,
+                intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+            ),
+            TestData(
+                "META + ENTER -> Go Home",
+                intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_ENTER),
+                KeyGestureEvent.KEY_GESTURE_TYPE_HOME,
+                intArrayOf(KeyEvent.KEYCODE_ENTER),
+                KeyEvent.META_META_ON,
+                intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+            ),
+            TestData(
+                "META + I -> Launch System Settings",
+                intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_I),
+                KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS,
+                intArrayOf(KeyEvent.KEYCODE_I),
+                KeyEvent.META_META_ON,
+                intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+            ),
+            TestData(
+                "META + L -> Lock",
+                intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_L),
+                KeyGestureEvent.KEY_GESTURE_TYPE_LOCK_SCREEN,
+                intArrayOf(KeyEvent.KEYCODE_L),
+                KeyEvent.META_META_ON,
+                intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+            ),
+            TestData(
+                "META + N -> Toggle Notification",
+                intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_N),
+                KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL,
+                intArrayOf(KeyEvent.KEYCODE_N),
+                KeyEvent.META_META_ON,
+                intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+            ),
+            TestData(
+                "META + CTRL + S -> Take Screenshot",
+                intArrayOf(
+                    KeyEvent.KEYCODE_META_LEFT,
+                    KeyEvent.KEYCODE_CTRL_LEFT,
+                    KeyEvent.KEYCODE_S
+                ),
+                KeyGestureEvent.KEY_GESTURE_TYPE_TAKE_SCREENSHOT,
+                intArrayOf(KeyEvent.KEYCODE_S),
+                KeyEvent.META_META_ON or KeyEvent.META_CTRL_ON,
+                intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+            ),
+            TestData(
+                "META + DEL -> Back",
+                intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_DEL),
+                KeyGestureEvent.KEY_GESTURE_TYPE_BACK,
+                intArrayOf(KeyEvent.KEYCODE_DEL),
+                KeyEvent.META_META_ON,
+                intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+            ),
+            TestData(
+                "META + ESC -> Back",
+                intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_ESCAPE),
+                KeyGestureEvent.KEY_GESTURE_TYPE_BACK,
+                intArrayOf(KeyEvent.KEYCODE_ESCAPE),
+                KeyEvent.META_META_ON,
+                intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+            ),
+            TestData(
+                "META + DPAD_LEFT -> Back",
+                intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_DPAD_LEFT),
+                KeyGestureEvent.KEY_GESTURE_TYPE_BACK,
+                intArrayOf(KeyEvent.KEYCODE_DPAD_LEFT),
+                KeyEvent.META_META_ON,
+                intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+            ),
+            TestData(
+                "META + CTRL + DPAD_UP -> Multi Window Navigation",
+                intArrayOf(
+                    KeyEvent.KEYCODE_META_LEFT,
+                    KeyEvent.KEYCODE_CTRL_LEFT,
+                    KeyEvent.KEYCODE_DPAD_UP
+                ),
+                KeyGestureEvent.KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION,
+                intArrayOf(KeyEvent.KEYCODE_DPAD_UP),
+                KeyEvent.META_META_ON or KeyEvent.META_CTRL_ON,
+                intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+            ),
+            TestData(
+                "META + CTRL + DPAD_DOWN -> Desktop Mode",
+                intArrayOf(
+                    KeyEvent.KEYCODE_META_LEFT,
+                    KeyEvent.KEYCODE_CTRL_LEFT,
+                    KeyEvent.KEYCODE_DPAD_DOWN
+                ),
+                KeyGestureEvent.KEY_GESTURE_TYPE_DESKTOP_MODE,
+                intArrayOf(KeyEvent.KEYCODE_DPAD_DOWN),
+                KeyEvent.META_META_ON or KeyEvent.META_CTRL_ON,
+                intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+            ),
+            TestData(
+                "META + CTRL + DPAD_LEFT -> Splitscreen Navigation Left",
+                intArrayOf(
+                    KeyEvent.KEYCODE_META_LEFT,
+                    KeyEvent.KEYCODE_CTRL_LEFT,
+                    KeyEvent.KEYCODE_DPAD_LEFT
+                ),
+                KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_LEFT,
+                intArrayOf(KeyEvent.KEYCODE_DPAD_LEFT),
+                KeyEvent.META_META_ON or KeyEvent.META_CTRL_ON,
+                intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+            ),
+            TestData(
+                "META + CTRL + DPAD_RIGHT -> Splitscreen Navigation Right",
+                intArrayOf(
+                    KeyEvent.KEYCODE_META_LEFT,
+                    KeyEvent.KEYCODE_CTRL_LEFT,
+                    KeyEvent.KEYCODE_DPAD_RIGHT
+                ),
+                KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_RIGHT,
+                intArrayOf(KeyEvent.KEYCODE_DPAD_RIGHT),
+                KeyEvent.META_META_ON or KeyEvent.META_CTRL_ON,
+                intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+            ),
+            TestData(
+                "META + ALT + DPAD_LEFT -> Change Splitscreen Focus Left",
+                intArrayOf(
+                    KeyEvent.KEYCODE_META_LEFT,
+                    KeyEvent.KEYCODE_ALT_LEFT,
+                    KeyEvent.KEYCODE_DPAD_LEFT
+                ),
+                KeyGestureEvent.KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_LEFT,
+                intArrayOf(KeyEvent.KEYCODE_DPAD_LEFT),
+                KeyEvent.META_META_ON or KeyEvent.META_ALT_ON,
+                intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+            ),
+            TestData(
+                "META + CTRL + DPAD_RIGHT -> Change Splitscreen Focus Right",
+                intArrayOf(
+                    KeyEvent.KEYCODE_META_LEFT,
+                    KeyEvent.KEYCODE_ALT_LEFT,
+                    KeyEvent.KEYCODE_DPAD_RIGHT
+                ),
+                KeyGestureEvent.KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_RIGHT,
+                intArrayOf(KeyEvent.KEYCODE_DPAD_RIGHT),
+                KeyEvent.META_META_ON or KeyEvent.META_ALT_ON,
+                intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+            ),
+            TestData(
+                "BRIGHTNESS_UP -> Brightness Up",
+                intArrayOf(KeyEvent.KEYCODE_BRIGHTNESS_UP),
+                KeyGestureEvent.KEY_GESTURE_TYPE_BRIGHTNESS_UP,
+                intArrayOf(KeyEvent.KEYCODE_BRIGHTNESS_UP),
+                0,
+                intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+            ),
+            TestData(
+                "BRIGHTNESS_DOWN -> Brightness Down",
+                intArrayOf(KeyEvent.KEYCODE_BRIGHTNESS_DOWN),
+                KeyGestureEvent.KEY_GESTURE_TYPE_BRIGHTNESS_DOWN,
+                intArrayOf(KeyEvent.KEYCODE_BRIGHTNESS_DOWN),
+                0,
+                intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+            ),
+            TestData(
+                "KEYBOARD_BACKLIGHT_UP -> Keyboard Backlight Up",
+                intArrayOf(KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_UP),
+                KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_UP,
+                intArrayOf(KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_UP),
+                0,
+                intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+            ),
+            TestData(
+                "KEYBOARD_BACKLIGHT_DOWN -> Keyboard Backlight Down",
+                intArrayOf(KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_DOWN),
+                KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_DOWN,
+                intArrayOf(KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_DOWN),
+                0,
+                intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+            ),
+            TestData(
+                "KEYBOARD_BACKLIGHT_TOGGLE -> Keyboard Backlight Toggle",
+                intArrayOf(KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_TOGGLE),
+                KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_TOGGLE,
+                intArrayOf(KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_TOGGLE),
+                0,
+                intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+            ),
+            TestData(
+                "ALL_APPS -> Open App Drawer",
+                intArrayOf(KeyEvent.KEYCODE_ALL_APPS),
+                KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS,
+                intArrayOf(KeyEvent.KEYCODE_ALL_APPS),
+                0,
+                intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+            ),
+            TestData(
+                "NOTIFICATION -> Toggle Notification Panel",
+                intArrayOf(KeyEvent.KEYCODE_NOTIFICATION),
+                KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL,
+                intArrayOf(KeyEvent.KEYCODE_NOTIFICATION),
+                0,
+                intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+            ),
+            TestData(
+                "LANGUAGE_SWITCH -> Switch Language Forward",
+                intArrayOf(KeyEvent.KEYCODE_LANGUAGE_SWITCH),
+                KeyGestureEvent.KEY_GESTURE_TYPE_LANGUAGE_SWITCH,
+                intArrayOf(KeyEvent.KEYCODE_LANGUAGE_SWITCH),
+                0,
+                intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+            ),
+            TestData(
+                "SHIFT + LANGUAGE_SWITCH -> Switch Language Backward",
+                intArrayOf(KeyEvent.KEYCODE_SHIFT_LEFT, KeyEvent.KEYCODE_LANGUAGE_SWITCH),
+                KeyGestureEvent.KEY_GESTURE_TYPE_LANGUAGE_SWITCH,
+                intArrayOf(KeyEvent.KEYCODE_LANGUAGE_SWITCH),
+                KeyEvent.META_SHIFT_ON,
+                intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+            ),
+            TestData(
+                "SCREENSHOT -> Take Screenshot",
+                intArrayOf(KeyEvent.KEYCODE_SCREENSHOT),
+                KeyGestureEvent.KEY_GESTURE_TYPE_TAKE_SCREENSHOT,
+                intArrayOf(KeyEvent.KEYCODE_SCREENSHOT),
+                0,
+                intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+            ),
+            TestData(
+                "META -> Open Apps Drawer",
+                intArrayOf(KeyEvent.KEYCODE_META_LEFT),
+                KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_ALL_APPS,
+                intArrayOf(KeyEvent.KEYCODE_META_LEFT),
+                0,
+                intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+            ),
+            TestData(
+                "META + ALT -> Toggle Caps Lock",
+                intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_ALT_LEFT),
+                KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK,
+                intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_ALT_LEFT),
+                0,
+                intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+            ),
+            TestData(
+                "ALT + META -> Toggle Caps Lock",
+                intArrayOf(KeyEvent.KEYCODE_ALT_LEFT, KeyEvent.KEYCODE_META_LEFT),
+                KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK,
+                intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_ALT_LEFT),
+                0,
+                intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+            ),
+            TestData(
+                "META + TAB -> Open Overview",
+                intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_TAB),
+                KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS,
+                intArrayOf(KeyEvent.KEYCODE_TAB),
+                KeyEvent.META_META_ON,
+                intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+            ),
+            TestData(
+                "ALT + TAB -> Toggle Recent Apps Switcher",
+                intArrayOf(KeyEvent.KEYCODE_ALT_LEFT, KeyEvent.KEYCODE_TAB),
+                KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS_SWITCHER,
+                intArrayOf(KeyEvent.KEYCODE_TAB),
+                KeyEvent.META_ALT_ON,
+                intArrayOf(
+                    KeyGestureEvent.ACTION_GESTURE_START,
+                    KeyGestureEvent.ACTION_GESTURE_COMPLETE
+                )
+            ),
+        )
+    }
+
+    @Test
+    @Parameters(method = "keyGestureEventHandlerTestArguments")
+    fun testKeyGestures(test: TestData) {
+        val handler = KeyGestureHandler { event, _ ->
+            handleEvents.add(KeyGestureEvent(event))
+            true
+        }
+        keyGestureController.registerKeyGestureHandler(handler, 0)
+        handleEvents.clear()
+
+        sendKeys(test.keys, /* assertAllConsumed = */ false)
+
+        assertEquals(
+            "Test: $test doesn't produce correct number of key gesture events",
+            test.expectedActions.size,
+            handleEvents.size
+        )
+        for (i in handleEvents.indices) {
+            val event = handleEvents[i]
+            assertArrayEquals(
+                "Test: $test doesn't produce correct key gesture keycodes",
+                test.expectedKeys,
+                event.keycodes
+            )
+            assertEquals(
+                "Test: $test doesn't produce correct key gesture modifier state",
+                test.expectedModifierState,
+                event.modifierState
+            )
+            assertEquals(
+                "Test: $test doesn't produce correct key gesture type",
+                test.expectedKeyGestureType,
+                event.keyGestureType
+            )
+            assertEquals(
+                "Test: $test doesn't produce correct key gesture action",
+                test.expectedActions[i],
+                event.action
+            )
+        }
+
+        keyGestureController.unregisterKeyGestureHandler(handler, 0)
+    }
+
+    @Test
+    fun testKeycodesFullyConsumed_irrespectiveOfHandlers() {
+        val testKeys = intArrayOf(
+            KeyEvent.KEYCODE_RECENT_APPS,
+            KeyEvent.KEYCODE_APP_SWITCH,
+            KeyEvent.KEYCODE_BRIGHTNESS_UP,
+            KeyEvent.KEYCODE_BRIGHTNESS_DOWN,
+            KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_DOWN,
+            KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_UP,
+            KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_TOGGLE,
+            KeyEvent.KEYCODE_ALL_APPS,
+            KeyEvent.KEYCODE_NOTIFICATION,
+            KeyEvent.KEYCODE_SETTINGS,
+            KeyEvent.KEYCODE_LANGUAGE_SWITCH,
+            KeyEvent.KEYCODE_SCREENSHOT,
+            KeyEvent.KEYCODE_META_LEFT,
+            KeyEvent.KEYCODE_META_RIGHT,
+            KeyEvent.KEYCODE_ASSIST,
+            KeyEvent.KEYCODE_VOICE_ASSIST,
+            KeyEvent.KEYCODE_STYLUS_BUTTON_PRIMARY,
+            KeyEvent.KEYCODE_STYLUS_BUTTON_SECONDARY,
+            KeyEvent.KEYCODE_STYLUS_BUTTON_TERTIARY,
+            KeyEvent.KEYCODE_STYLUS_BUTTON_TAIL,
+        )
+
+        val handler = KeyGestureHandler { _, _ -> false }
+        keyGestureController.registerKeyGestureHandler(handler, 0)
+
+        for (key in testKeys) {
+            sendKeys(intArrayOf(key), /* assertAllConsumed = */ true)
+        }
+    }
+
+    private fun sendKeys(testKeys: IntArray, assertAllConsumed: Boolean) {
+        var metaState = 0
+        for (key in testKeys) {
+            val downEvent = KeyEvent(
+                /* downTime = */0, /* eventTime = */ 0, KeyEvent.ACTION_DOWN, key,
+                0 /*repeat*/, metaState, KeyCharacterMap.VIRTUAL_KEYBOARD, 0 /*scancode*/,
+                0 /*flags*/, InputDevice.SOURCE_KEYBOARD
+            )
+            val consumed =
+                keyGestureController.interceptKeyBeforeDispatching(null, downEvent, 0) == -1L
+            if (assertAllConsumed) {
+                assertTrue(
+                    "interceptKeyBeforeDispatching should consume all events $downEvent",
+                    consumed
+                )
+            }
+            metaState = metaState or MODIFIER.getOrDefault(key, 0)
+
+            downEvent.recycle()
+            testLooper.dispatchAll()
+        }
+
+        for (key in testKeys.reversed()) {
+            val upEvent = KeyEvent(
+                /* downTime = */0, /* eventTime = */ 0, KeyEvent.ACTION_UP, key,
+                0 /*repeat*/, metaState, KeyCharacterMap.VIRTUAL_KEYBOARD, 0 /*scancode*/,
+                0 /*flags*/, InputDevice.SOURCE_KEYBOARD
+            )
+            val consumed =
+                keyGestureController.interceptKeyBeforeDispatching(null, upEvent, 0) == -1L
+            if (assertAllConsumed) {
+                assertTrue(
+                    "interceptKeyBeforeDispatching should consume all events $upEvent",
+                    consumed
+                )
+            }
+
+            upEvent.recycle()
+            testLooper.dispatchAll()
+        }
+    }
+
     inner class KeyGestureEventListener : IKeyGestureEventListener.Stub() {
         override fun onKeyGestureEvent(event: AidlKeyGestureEvent) {
             events.add(KeyGestureEvent(event))