Merge "Overload DeviceInfoUtils.getSecurityPatch method" into main
diff --git a/core/java/android/content/pm/multiuser.aconfig b/core/java/android/content/pm/multiuser.aconfig
index 34f3b61..fd1a896 100644
--- a/core/java/android/content/pm/multiuser.aconfig
+++ b/core/java/android/content/pm/multiuser.aconfig
@@ -221,6 +221,17 @@
 }
 
 flag {
+    name: "cache_user_properties_correctly_read_only"
+    namespace: "multiuser"
+    description: "UserProperties cache needs to take into account who the callingUid is."
+    bug: "369198539"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+  }
+  is_fixed_read_only: true
+}
+
+flag {
     name: "cache_user_serial_number"
     namespace: "multiuser"
     description: "Optimise user serial number retrieval"
diff --git a/core/java/android/os/IPowerManager.aidl b/core/java/android/os/IPowerManager.aidl
index dbb6f92..5f62b8b 100644
--- a/core/java/android/os/IPowerManager.aidl
+++ b/core/java/android/os/IPowerManager.aidl
@@ -46,6 +46,7 @@
 
     void userActivity(int displayId, long time, int event, int flags);
     void wakeUp(long time, int reason, String details, String opPackageName);
+    void wakeUpWithDisplayId(long time, int reason, String details, String opPackageName, int displayId);
     @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     void goToSleep(long time, int reason, int flags);
     @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index e4c12b6..b9bae5b 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -1567,27 +1567,9 @@
     }
 
     /**
-     * Forces the {@link android.view.Display#DEFAULT_DISPLAY_GROUP default display group}
-     * to turn on.
+     * Forces the {@link android.view.Display#DEFAULT_DISPLAY default display} to turn on.
      *
-     * <p>If the {@link android.view.Display#DEFAULT_DISPLAY_GROUP default display group} is
-     * turned off it will be turned on. Additionally, if the device is asleep it will be awoken. If
-     * the {@link android.view.Display#DEFAULT_DISPLAY_GROUP default display group} is already
-     * on then nothing will happen.
-     *
-     * <p>
-     * This is what happens when the power key is pressed to turn on the screen.
-     * </p><p>
-     * Requires the {@link android.Manifest.permission#DEVICE_POWER} permission.
-     * </p>
-     *
-     * @param time The time when the request to wake up was issued, in the
-     * {@link SystemClock#uptimeMillis()} time base.  This timestamp is used to correctly
-     * order the wake up request with other power management functions.  It should be set
-     * to the timestamp of the input event that caused the request to wake up.
-     *
-     * @see #userActivity
-     * @see #goToSleep
+     * @see #wakeUp(long, int, String, int)
      *
      * @deprecated Use {@link #wakeUp(long, int, String)} instead.
      * @removed Requires signature permission.
@@ -1598,30 +1580,9 @@
     }
 
     /**
-     * Forces the {@link android.view.Display#DEFAULT_DISPLAY_GROUP default display group}
-     * to turn on.
+     * Forces the {@link android.view.Display#DEFAULT_DISPLAY default display} to turn on.
      *
-     * <p>If the {@link android.view.Display#DEFAULT_DISPLAY_GROUP default display group} is
-     * turned off it will be turned on. Additionally, if the device is asleep it will be awoken. If
-     * the {@link android.view.Display#DEFAULT_DISPLAY_GROUP default display group} is already
-     * on then nothing will happen.
-     *
-     * <p>
-     * This is what happens when the power key is pressed to turn on the screen.
-     * </p><p>
-     * Requires the {@link android.Manifest.permission#DEVICE_POWER} permission.
-     * </p>
-     *
-     * @param time The time when the request to wake up was issued, in the
-     * {@link SystemClock#uptimeMillis()} time base.  This timestamp is used to correctly
-     * order the wake up request with other power management functions.  It should be set
-     * to the timestamp of the input event that caused the request to wake up.
-     *
-     * @param details A free form string to explain the specific details behind the wake up for
-     *                debugging purposes.
-     *
-     * @see #userActivity
-     * @see #goToSleep
+     * @see #wakeUp(long, int, String, int)
      *
      * @deprecated Use {@link #wakeUp(long, int, String)} instead.
      * @hide
@@ -1635,9 +1596,23 @@
     /**
      * Forces the {@link android.view.Display#DEFAULT_DISPLAY default display} to turn on.
      *
-     * <p>If the {@link android.view.Display#DEFAULT_DISPLAY default display} is turned off it will
-     * be turned on. Additionally, if the device is asleep it will be awoken. If the {@link
-     * android.view.Display#DEFAULT_DISPLAY default display} is already on then nothing will happen.
+     * @see #wakeUp(long, int, String, int)
+     * @hide
+     */
+    public void wakeUp(long time, @WakeReason int reason, String details) {
+        try {
+            mService.wakeUp(time, reason, details, mContext.getOpPackageName());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Forces the display with the supplied displayId to turn on.
+     *
+     * <p>If the corresponding display is turned off, it will be turned on. Additionally, if the
+     * device is asleep it will be awoken. If the corresponding display is already on then nothing
+     * will happen. If the corresponding display does not exist, then nothing will happen.
      *
      * <p>If the device is an Android TV playback device, it will attempt to turn on the
      * HDMI-connected TV and become the current active source via the HDMI-CEC One Touch Play
@@ -1658,14 +1633,16 @@
      *
      * @param details A free form string to explain the specific details behind the wake up for
      *                debugging purposes.
+     * @param displayId The displayId of the display to be woken up.
      *
      * @see #userActivity
      * @see #goToSleep
      * @hide
      */
-    public void wakeUp(long time, @WakeReason int reason, String details) {
+    public void wakeUp(long time, @WakeReason int reason, String details, int displayId) {
         try {
-            mService.wakeUp(time, reason, details, mContext.getOpPackageName());
+            mService.wakeUpWithDisplayId(time, reason, details, mContext.getOpPackageName(),
+                    displayId);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index e38281f..8ac5532 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -29,7 +29,6 @@
 import static android.view.WindowInsets.Type.ime;
 
 import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
-import static com.android.window.flags.Flags.insetsControlSeq;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -877,9 +876,7 @@
         @InsetsType int visibleTypes = 0;
         @InsetsType int[] cancelledUserAnimationTypes = {0};
         for (int i = 0, size = newState.sourceSize(); i < size; i++) {
-            final InsetsSource source = insetsControlSeq()
-                    ? new InsetsSource(newState.sourceAt(i))
-                    : newState.sourceAt(i);
+            final InsetsSource source = new InsetsSource(newState.sourceAt(i));
             @InsetsType int type = source.getType();
             @AnimationType int animationType = getAnimationType(type);
             final InsetsSourceConsumer consumer = mSourceConsumers.get(source.getId());
diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java
index 2e2ff1d..da788a7 100644
--- a/core/java/android/view/InsetsSourceConsumer.java
+++ b/core/java/android/view/InsetsSourceConsumer.java
@@ -29,7 +29,6 @@
 import static android.view.InsetsSourceConsumerProto.TYPE_NUMBER;
 
 import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
-import static com.android.window.flags.Flags.insetsControlSeq;
 
 import android.annotation.IntDef;
 import android.annotation.Nullable;
@@ -431,9 +430,6 @@
 
         // Frame is changing while animating. Keep note of the new frame but keep existing frame
         // until animation is finished.
-        if (!insetsControlSeq()) {
-            newSource = new InsetsSource(newSource);
-        }
         mPendingFrame = new Rect(newSource.getFrame());
         mPendingVisibleFrame = newSource.getVisibleFrame() != null
                 ? new Rect(newSource.getVisibleFrame())
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 1cad81b..daa0c57 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -130,7 +130,6 @@
 import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
 import static com.android.text.flags.Flags.disableHandwritingInitiatorForIme;
 import static com.android.window.flags.Flags.enableBufferTransformHintFromDisplay;
-import static com.android.window.flags.Flags.insetsControlSeq;
 import static com.android.window.flags.Flags.setScPropertiesInClient;
 import static com.android.window.flags.Flags.systemUiImmersiveConfirmationDialog;
 
@@ -888,10 +887,7 @@
     /** Non-{@code null} if {@link #mActivityConfigCallback} is not {@code null}. */
     @Nullable
     private ActivityWindowInfo mLastReportedActivityWindowInfo;
-    @Nullable
-    private final ClientWindowFrames mLastReportedFrames = insetsControlSeq()
-            ? new ClientWindowFrames()
-            : null;
+    private final ClientWindowFrames mLastReportedFrames = new ClientWindowFrames();
     private int mLastReportedInsetsStateSeq = getInitSeq();
     private int mLastReportedActiveControlsSeq = getInitSeq();
 
@@ -2316,9 +2312,6 @@
     }
 
     private void onClientWindowFramesChanged(@NonNull ClientWindowFrames inOutFrames) {
-        if (mLastReportedFrames == null) {
-            return;
-        }
         if (isIncomingSeqStale(mLastReportedFrames.seq, inOutFrames.seq)) {
             // If the incoming is stale, use the last reported instead.
             inOutFrames.setTo(mLastReportedFrames);
@@ -2329,14 +2322,12 @@
     }
 
     private void onInsetsStateChanged(@NonNull InsetsState insetsState) {
-        if (insetsControlSeq()) {
-            if (isIncomingSeqStale(mLastReportedInsetsStateSeq, insetsState.getSeq())) {
-                // The incoming is stale. Skip.
-                return;
-            }
-            // Keep track of the latest.
-            mLastReportedInsetsStateSeq = insetsState.getSeq();
+        if (isIncomingSeqStale(mLastReportedInsetsStateSeq, insetsState.getSeq())) {
+            // The incoming is stale. Skip.
+            return;
         }
+        // Keep track of the latest.
+        mLastReportedInsetsStateSeq = insetsState.getSeq();
 
         if (mTranslator != null) {
             mTranslator.translateInsetsStateInScreenToAppWindow(insetsState);
@@ -2351,15 +2342,13 @@
             return;
         }
 
-        if (insetsControlSeq()) {
-            if (isIncomingSeqStale(mLastReportedActiveControlsSeq, activeControls.getSeq())) {
-                // The incoming is stale. Skip.
-                activeControls.release();
-                return;
-            }
-            // Keep track of the latest.
-            mLastReportedActiveControlsSeq = activeControls.getSeq();
+        if (isIncomingSeqStale(mLastReportedActiveControlsSeq, activeControls.getSeq())) {
+            // The incoming is stale. Skip.
+            activeControls.release();
+            return;
         }
+        // Keep track of the latest.
+        mLastReportedActiveControlsSeq = activeControls.getSeq();
 
         final InsetsSourceControl[] controls = activeControls.get();
         if (mTranslator != null) {
diff --git a/core/java/android/window/flags/windowing_sdk.aconfig b/core/java/android/window/flags/windowing_sdk.aconfig
index 11d4db3..f0ea7a8 100644
--- a/core/java/android/window/flags/windowing_sdk.aconfig
+++ b/core/java/android/window/flags/windowing_sdk.aconfig
@@ -68,17 +68,6 @@
 
 flag {
     namespace: "windowing_sdk"
-    name: "insets_control_seq"
-    description: "Add seqId to InsetsControls to ensure the stale update is ignored"
-    bug: "339380439"
-    is_fixed_read_only: true
-    metadata {
-        purpose: PURPOSE_BUGFIX
-    }
-}
-
-flag {
-    namespace: "windowing_sdk"
      name: "move_animation_options_to_change"
      description: "Move AnimationOptions from TransitionInfo to each Change"
      bug: "327332488"
diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java
index e240a08..6327211 100644
--- a/core/tests/coretests/src/android/view/ViewRootImplTest.java
+++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java
@@ -71,7 +71,6 @@
 import android.hardware.display.DisplayManagerGlobal;
 import android.os.Binder;
 import android.os.SystemProperties;
-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;
@@ -1599,7 +1598,6 @@
                 nativeCreateASurfaceControlFromSurface(mViewRootImpl.mSurface));
     }
 
-    @EnableFlags(Flags.FLAG_INSETS_CONTROL_SEQ)
     @Test
     public void testHandleInsetsControlChanged() {
         mView = new View(sContext);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt
index 9c7d644..8c102eb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt
@@ -22,6 +22,7 @@
 import android.graphics.Color
 import android.graphics.Point
 import android.hardware.input.InputManager
+import android.os.Bundle
 import android.os.Handler
 import android.view.MotionEvent.ACTION_DOWN
 import android.view.SurfaceControl
@@ -29,7 +30,12 @@
 import android.view.View.OnClickListener
 import android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS
 import android.view.WindowManager
+import android.view.accessibility.AccessibilityEvent
+import android.view.accessibility.AccessibilityNodeInfo
+import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction
 import android.widget.ImageButton
+import androidx.core.view.ViewCompat
+import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat
 import com.android.internal.policy.SystemBarUtils
 import com.android.window.flags.Flags
 import com.android.wm.shell.R
@@ -67,6 +73,20 @@
         captionView.setOnTouchListener(onCaptionTouchListener)
         captionHandle.setOnTouchListener(onCaptionTouchListener)
         captionHandle.setOnClickListener(onCaptionButtonClickListener)
+        captionHandle.accessibilityDelegate = object : View.AccessibilityDelegate() {
+            override fun sendAccessibilityEvent(host: View, eventType: Int) {
+                when (eventType) {
+                    AccessibilityEvent.TYPE_VIEW_HOVER_ENTER,
+                    AccessibilityEvent.TYPE_VIEW_HOVER_EXIT -> {
+                        // Caption Handle itself can't get a11y focus because it's under the status
+                        // bar, so pass through TYPE_VIEW_HOVER a11y events to the status bar
+                        // input layer, so that it can get a11y focus on the caption handle's behalf
+                        statusBarInputLayer?.view?.sendAccessibilityEvent(eventType)
+                    }
+                    else -> super.sendAccessibilityEvent(host, eventType)
+                }
+            }
+        }
     }
 
     override fun bindData(
@@ -134,9 +154,53 @@
             captionHandle.dispatchTouchEvent(event)
             return@setOnTouchListener true
         }
+        setupAppHandleA11y(view)
         windowManagerWrapper.updateViewLayout(view, lp)
     }
 
+    private fun setupAppHandleA11y(view: View) {
+        view.accessibilityDelegate = object : View.AccessibilityDelegate() {
+            override fun onInitializeAccessibilityNodeInfo(
+                host: View,
+                info: AccessibilityNodeInfo
+            ) {
+                // Allow the status bar input layer to be a11y clickable so it can interact with
+                // a11y services on behalf of caption handle (due to being under status bar)
+                super.onInitializeAccessibilityNodeInfo(host, info)
+                info.addAction(AccessibilityAction.ACTION_CLICK)
+                host.isClickable = true
+            }
+
+            override fun performAccessibilityAction(
+                host: View,
+                action: Int,
+                args: Bundle?
+            ): Boolean {
+                // Passthrough the a11y click action so the caption handle, so that app handle menu
+                // is opened on a11y click, similar to a real click
+                if (action == AccessibilityAction.ACTION_CLICK.id) {
+                    captionHandle.performClick()
+                }
+                return super.performAccessibilityAction(host, action, args)
+            }
+
+            override fun onPopulateAccessibilityEvent(host: View, event: AccessibilityEvent) {
+                super.onPopulateAccessibilityEvent(host, event)
+                // When the status bar input layer is focused, use the content description of the
+                // caption handle so that it appears as "App handle" and not "Unlabelled view"
+                if (event.eventType == AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED) {
+                    event.text.add(captionHandle.contentDescription)
+                }
+            }
+        }
+
+        // Update a11y action text so that Talkback announces "Press double tap to open app handle
+        // menu" while focused on status bar input layer
+        ViewCompat.replaceAccessibilityAction(
+            view, AccessibilityActionCompat.ACTION_CLICK, "Open app handle menu", null
+        )
+    }
+
     private fun updateStatusBarInputLayer(globalPosition: Point) {
         statusBarInputLayer?.setPosition(
             SurfaceControl.Transaction(),
@@ -173,7 +237,8 @@
         return taskInfo.taskDescription
             ?.let { taskDescription ->
                 if (Color.alpha(taskDescription.statusBarColor) != 0 &&
-                    taskInfo.windowingMode == WINDOWING_MODE_FREEFORM) {
+                    taskInfo.windowingMode == WINDOWING_MODE_FREEFORM
+                ) {
                     Color.valueOf(taskDescription.statusBarColor).luminance() < 0.5
                 } else {
                     taskDescription.systemBarsAppearance and APPEARANCE_LIGHT_STATUS_BARS == 0
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/SliderPage.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/SliderPage.kt
deleted file mode 100644
index 1051549..0000000
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/SliderPage.kt
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settingslib.spa.gallery.page
-
-import android.os.Bundle
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.outlined.AccessAlarm
-import androidx.compose.material.icons.outlined.MusicNote
-import androidx.compose.material.icons.outlined.MusicOff
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.setValue
-import androidx.compose.ui.tooling.preview.Preview
-import com.android.settingslib.spa.framework.common.SettingsEntry
-import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
-import com.android.settingslib.spa.framework.common.SettingsPageProvider
-import com.android.settingslib.spa.framework.common.createSettingsPage
-import com.android.settingslib.spa.framework.compose.navigator
-import com.android.settingslib.spa.framework.theme.SettingsTheme
-import com.android.settingslib.spa.widget.preference.SliderPreference
-import com.android.settingslib.spa.widget.preference.SliderPreferenceModel
-import com.android.settingslib.spa.widget.preference.Preference
-import com.android.settingslib.spa.widget.preference.PreferenceModel
-
-private const val TITLE = "Sample Slider"
-
-object SliderPageProvider : SettingsPageProvider {
-    override val name = "Slider"
-    private val owner = createSettingsPage()
-
-    override fun buildEntry(arguments: Bundle?): List<SettingsEntry> {
-        val entryList = mutableListOf<SettingsEntry>()
-        entryList.add(
-            SettingsEntryBuilder.create("Simple Slider", owner)
-                .setUiLayoutFn {
-                    SliderPreference(object : SliderPreferenceModel {
-                        override val title = "Simple Slider"
-                        override val initValue = 40
-                    })
-                }.build()
-        )
-        entryList.add(
-            SettingsEntryBuilder.create("Slider with icon", owner)
-                .setUiLayoutFn {
-                    SliderPreference(object : SliderPreferenceModel {
-                        override val title = "Slider with icon"
-                        override val initValue = 30
-                        override val onValueChangeFinished = {
-                            println("onValueChangeFinished")
-                        }
-                        override val icon = Icons.Outlined.AccessAlarm
-                    })
-                }.build()
-        )
-        entryList.add(
-            SettingsEntryBuilder.create("Slider with changeable icon", owner)
-                .setUiLayoutFn {
-                    val initValue = 0
-                    var icon by remember { mutableStateOf(Icons.Outlined.MusicOff) }
-                    var sliderPosition by remember { mutableStateOf(initValue) }
-                    SliderPreference(object : SliderPreferenceModel {
-                        override val title = "Slider with changeable icon"
-                        override val initValue = initValue
-                        override val onValueChange = { it: Int ->
-                            sliderPosition = it
-                            icon = if (it > 0) Icons.Outlined.MusicNote else Icons.Outlined.MusicOff
-                        }
-                        override val onValueChangeFinished = {
-                            println("onValueChangeFinished: the value is $sliderPosition")
-                        }
-                        override val icon = icon
-                    })
-                }.build()
-        )
-        entryList.add(
-            SettingsEntryBuilder.create("Slider with steps", owner)
-                .setUiLayoutFn {
-                    SliderPreference(object : SliderPreferenceModel {
-                        override val title = "Slider with steps"
-                        override val initValue = 2
-                        override val valueRange = 1..5
-                        override val showSteps = true
-                    })
-                }.build()
-        )
-
-        return entryList
-    }
-
-    fun buildInjectEntry(): SettingsEntryBuilder {
-        return SettingsEntryBuilder.createInject(owner)
-            .setUiLayoutFn {
-                Preference(object : PreferenceModel {
-                    override val title = TITLE
-                    override val onClick = navigator(name)
-                })
-            }
-    }
-
-    override fun getTitle(arguments: Bundle?): String {
-        return TITLE
-    }
-}
-
-@Preview(showBackground = true)
-@Composable
-private fun SliderPagePreview() {
-    SettingsTheme {
-        SliderPageProvider.Page(null)
-    }
-}
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/SliderPageProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/SliderPageProvider.kt
new file mode 100644
index 0000000..89b10ee
--- /dev/null
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/SliderPageProvider.kt
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spa.gallery.page
+
+import android.os.Bundle
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.outlined.AccessAlarm
+import androidx.compose.material.icons.outlined.MusicNote
+import androidx.compose.material.icons.outlined.MusicOff
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.tooling.preview.Preview
+import com.android.settingslib.spa.framework.common.SettingsEntry
+import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
+import com.android.settingslib.spa.framework.common.SettingsPageProvider
+import com.android.settingslib.spa.framework.common.createSettingsPage
+import com.android.settingslib.spa.framework.compose.navigator
+import com.android.settingslib.spa.framework.theme.SettingsTheme
+import com.android.settingslib.spa.widget.preference.Preference
+import com.android.settingslib.spa.widget.preference.PreferenceModel
+import com.android.settingslib.spa.widget.preference.SliderPreference
+import com.android.settingslib.spa.widget.preference.SliderPreferenceModel
+
+private const val TITLE = "Sample Slider"
+
+object SliderPageProvider : SettingsPageProvider {
+    override val name = "Slider"
+    private val owner = createSettingsPage()
+
+    override fun buildEntry(arguments: Bundle?): List<SettingsEntry> {
+        val entryList = mutableListOf<SettingsEntry>()
+        entryList.add(
+            SettingsEntryBuilder.create("Simple Slider", owner)
+                .setUiLayoutFn {
+                    SliderPreference(
+                        object : SliderPreferenceModel {
+                            override val title = "Simple Slider"
+                            override val initValue = 40
+                        }
+                    )
+                }
+                .build()
+        )
+        entryList.add(
+            SettingsEntryBuilder.create("Slider with icon", owner)
+                .setUiLayoutFn {
+                    SliderPreference(
+                        object : SliderPreferenceModel {
+                            override val title = "Slider with icon"
+                            override val initValue = 30
+                            override val onValueChangeFinished = {
+                                println("onValueChangeFinished")
+                            }
+                            override val iconStart = Icons.Outlined.AccessAlarm
+                        }
+                    )
+                }
+                .build()
+        )
+        entryList.add(
+            SettingsEntryBuilder.create("Slider with changeable icon", owner)
+                .setUiLayoutFn {
+                    val initValue = 0
+                    var icon by remember { mutableStateOf(Icons.Outlined.MusicOff) }
+                    var sliderPosition by remember { mutableStateOf(initValue) }
+                    SliderPreference(
+                        object : SliderPreferenceModel {
+                            override val title = "Slider with changeable icon"
+                            override val initValue = initValue
+                            override val onValueChange = { it: Int ->
+                                sliderPosition = it
+                                icon =
+                                    if (it > 0) Icons.Outlined.MusicNote
+                                    else Icons.Outlined.MusicOff
+                            }
+                            override val onValueChangeFinished = {
+                                println("onValueChangeFinished: the value is $sliderPosition")
+                            }
+                            override val iconEnd = icon
+                        }
+                    )
+                }
+                .build()
+        )
+        entryList.add(
+            SettingsEntryBuilder.create("Slider with steps", owner)
+                .setUiLayoutFn {
+                    SliderPreference(
+                        object : SliderPreferenceModel {
+                            override val title = "Slider with steps"
+                            override val initValue = 2
+                            override val valueRange = 1..5
+                            override val showSteps = true
+                        }
+                    )
+                }
+                .build()
+        )
+
+        return entryList
+    }
+
+    fun buildInjectEntry(): SettingsEntryBuilder {
+        return SettingsEntryBuilder.createInject(owner).setUiLayoutFn {
+            Preference(
+                object : PreferenceModel {
+                    override val title = TITLE
+                    override val onClick = navigator(name)
+                }
+            )
+        }
+    }
+
+    override fun getTitle(arguments: Bundle?): String {
+        return TITLE
+    }
+}
+
+@Preview(showBackground = true)
+@Composable
+private fun SliderPagePreview() {
+    SettingsTheme { SliderPageProvider.Page(null) }
+}
diff --git a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/SliderPreferenceScreenshotTest.kt b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/SliderPreferenceScreenshotTest.kt
index 3a96a70..ca36e5b 100644
--- a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/SliderPreferenceScreenshotTest.kt
+++ b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/SliderPreferenceScreenshotTest.kt
@@ -60,7 +60,7 @@
                     override val onValueChangeFinished = {
                         println("onValueChangeFinished")
                     }
-                    override val icon = Icons.Outlined.AccessAlarm
+                    override val iconStart = Icons.Outlined.AccessAlarm
                 })
 
                 SliderPreference(object : SliderPreferenceModel {
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/button/ActionButtons.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/button/ActionButtons.kt
index 70d353d..7e1df16 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/button/ActionButtons.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/button/ActionButtons.kt
@@ -18,6 +18,7 @@
 
 import androidx.compose.foundation.background
 import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.IntrinsicSize
@@ -25,11 +26,14 @@
 import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.RowScope
 import androidx.compose.foundation.layout.fillMaxHeight
+import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.height
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.size
 import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.shape.RoundedCornerShape
 import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.automirrored.outlined.Launch
 import androidx.compose.material.icons.outlined.Delete
 import androidx.compose.material.icons.outlined.WarningAmber
 import androidx.compose.material3.ButtonDefaults
@@ -51,7 +55,7 @@
 import com.android.settingslib.spa.framework.theme.SettingsShape
 import com.android.settingslib.spa.framework.theme.SettingsTheme
 import com.android.settingslib.spa.framework.theme.divider
-import androidx.compose.material.icons.automirrored.outlined.Launch
+import com.android.settingslib.spa.framework.theme.isSpaExpressiveEnabled
 
 data class ActionButton(
     val text: String,
@@ -62,48 +66,64 @@
 
 @Composable
 fun ActionButtons(actionButtons: List<ActionButton>) {
-    Row(
-        Modifier
-            .padding(SettingsDimension.buttonPadding)
-            .clip(SettingsShape.CornerExtraLarge)
-            .height(IntrinsicSize.Min)
-    ) {
-        for ((index, actionButton) in actionButtons.withIndex()) {
-            if (index > 0) ButtonDivider()
-            ActionButton(actionButton)
+    if (isSpaExpressiveEnabled) {
+        Row(
+            horizontalArrangement = Arrangement.SpaceAround,
+            modifier = Modifier
+                .padding(SettingsDimension.buttonPadding)
+                .height(IntrinsicSize.Min)
+                .fillMaxWidth()
+        ) {
+            for (actionButton in actionButtons) {
+                ActionButton(actionButton)
+            }
+        }
+    } else {
+        Row(
+            Modifier
+                .padding(SettingsDimension.buttonPadding)
+                .clip(SettingsShape.CornerExtraLarge)
+                .height(IntrinsicSize.Min)
+        ) {
+            for ((index, actionButton) in actionButtons.withIndex()) {
+                if (index > 0) ButtonDivider()
+                ActionButton(actionButton)
+            }
         }
     }
 }
 
 @Composable
 private fun RowScope.ActionButton(actionButton: ActionButton) {
-    FilledTonalButton(
-        onClick = actionButton.onClick,
-        modifier = Modifier
-            .weight(1f)
-            .fillMaxHeight(),
-        enabled = actionButton.enabled,
-        // Because buttons could appear, disappear or change positions, reset the interaction source
-        // to prevent highlight the wrong button.
-        interactionSource = remember(actionButton) { MutableInteractionSource() },
-        shape = RectangleShape,
-        colors = ButtonDefaults.filledTonalButtonColors(
-            containerColor = MaterialTheme.colorScheme.surface,
-            contentColor = MaterialTheme.colorScheme.primary,
-            disabledContainerColor = MaterialTheme.colorScheme.surface,
-        ),
-        contentPadding = PaddingValues(horizontal = 4.dp, vertical = 20.dp),
-    ) {
+    if (isSpaExpressiveEnabled) {
         Column(horizontalAlignment = Alignment.CenterHorizontally) {
-            Icon(
-                imageVector = actionButton.imageVector,
-                contentDescription = null,
-                modifier = Modifier.size(SettingsDimension.itemIconSize),
-            )
+            FilledTonalButton(
+                onClick = actionButton.onClick,
+                modifier = Modifier
+                    .weight(1f)
+                    .fillMaxHeight()
+                    .clip(RoundedCornerShape(100.dp)),
+                enabled = actionButton.enabled,
+                // Because buttons could appear, disappear or change positions, reset the interaction source
+                // to prevent highlight the wrong button.
+                interactionSource = remember(actionButton) { MutableInteractionSource() },
+                shape = RectangleShape,
+                colors = ButtonDefaults.filledTonalButtonColors(
+                    containerColor = MaterialTheme.colorScheme.primaryContainer,
+                    contentColor = MaterialTheme.colorScheme.onPrimary,
+                    disabledContainerColor = MaterialTheme.colorScheme.surface,
+                ),
+                contentPadding = PaddingValues(horizontal = 24.dp, vertical = 16.dp),
+            ) {
+                Icon(
+                    imageVector = actionButton.imageVector,
+                    contentDescription = null,
+                    modifier = Modifier.size(SettingsDimension.itemIconSize),
+                )
+            }
             Box(
                 modifier = Modifier
-                    .padding(top = 4.dp)
-                    .fillMaxHeight(),
+                    .padding(top = 6.dp),
                 contentAlignment = Alignment.Center,
             ) {
                 Text(
@@ -113,6 +133,44 @@
                 )
             }
         }
+    } else {
+        FilledTonalButton(
+            onClick = actionButton.onClick,
+            modifier = Modifier
+                .weight(1f)
+                .fillMaxHeight(),
+            enabled = actionButton.enabled,
+            // Because buttons could appear, disappear or change positions, reset the interaction source
+            // to prevent highlight the wrong button.
+            interactionSource = remember(actionButton) { MutableInteractionSource() },
+            shape = RectangleShape,
+            colors = ButtonDefaults.filledTonalButtonColors(
+                containerColor = MaterialTheme.colorScheme.surface,
+                contentColor = MaterialTheme.colorScheme.primary,
+                disabledContainerColor = MaterialTheme.colorScheme.surface,
+            ),
+            contentPadding = PaddingValues(horizontal = 4.dp, vertical = 20.dp),
+        ) {
+            Column(horizontalAlignment = Alignment.CenterHorizontally) {
+                Icon(
+                    imageVector = actionButton.imageVector,
+                    contentDescription = null,
+                    modifier = Modifier.size(SettingsDimension.itemIconSize),
+                )
+                Box(
+                    modifier = Modifier
+                        .padding(top = 4.dp)
+                        .fillMaxHeight(),
+                    contentAlignment = Alignment.Center,
+                ) {
+                    Text(
+                        text = actionButton.text,
+                        textAlign = TextAlign.Center,
+                        style = MaterialTheme.typography.labelMedium,
+                    )
+                }
+            }
+        }
     }
 }
 
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/SliderPreference.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/SliderPreference.kt
index 7bca38f..b2a3aac 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/SliderPreference.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/SliderPreference.kt
@@ -16,6 +16,10 @@
 
 package com.android.settingslib.spa.widget.preference
 
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.padding
 import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.outlined.AccessAlarm
 import androidx.compose.material.icons.outlined.MusicNote
@@ -27,30 +31,25 @@
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.saveable.rememberSaveable
 import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.vector.ImageVector
 import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import com.android.settingslib.spa.framework.theme.SettingsDimension
 import com.android.settingslib.spa.framework.theme.SettingsTheme
 import com.android.settingslib.spa.framework.util.EntryHighlight
 import com.android.settingslib.spa.widget.ui.SettingsSlider
 
-/**
- * The widget model for [SliderPreference] widget.
- */
+/** The widget model for [SliderPreference] widget. */
 interface SliderPreferenceModel {
-    /**
-     * The title of this [SliderPreference].
-     */
+    /** The title of this [SliderPreference]. */
     val title: String
 
-    /**
-     * The initial position of the [SliderPreference].
-     */
+    /** The initial position of the [SliderPreference]. */
     val initValue: Int
 
-    /**
-     * The value range for this [SliderPreference].
-     */
+    /** The value range for this [SliderPreference]. */
     val valueRange: IntRange
         get() = 0..100
 
@@ -69,15 +68,23 @@
         get() = null
 
     /**
-     * The icon image for [SliderPreference]. If not specified, the slider hides the icon by default.
+     * The start icon image for [SliderPreference]. If not specified, the slider hides the icon by
+     * default.
      */
-    val icon: ImageVector?
+    val iconStart: ImageVector?
         get() = null
 
     /**
-     * Indicates whether to show step marks. If show step marks, when user finish sliding,
-     * the slider will automatically jump to the nearest step mark. Otherwise, the slider hides
-     * the step marks by default.
+     * The end icon image for [SliderPreference]. If not specified, the slider hides the icon by
+     * default.
+     */
+    val iconEnd: ImageVector?
+        get() = null
+
+    /**
+     * Indicates whether to show step marks. If show step marks, when user finish sliding, the
+     * slider will automatically jump to the nearest step mark. Otherwise, the slider hides the step
+     * marks by default.
      *
      * The step is fixed to 1.
      */
@@ -99,7 +106,8 @@
             valueRange = model.valueRange,
             onValueChange = model.onValueChange,
             onValueChangeFinished = model.onValueChangeFinished,
-            icon = model.icon,
+            iconStart = model.iconStart,
+            iconEnd = model.iconEnd,
             showSteps = model.showSteps,
         )
     }
@@ -113,27 +121,45 @@
     valueRange: IntRange = 0..100,
     onValueChange: ((value: Int) -> Unit)? = null,
     onValueChangeFinished: (() -> Unit)? = null,
-    icon: ImageVector? = null,
+    iconStart: ImageVector? = null,
+    iconEnd: ImageVector? = null,
     showSteps: Boolean = false,
 ) {
+
     BaseLayout(
         title = title,
         subTitle = {
-            SettingsSlider(
-                initValue,
-                modifier,
-                valueRange,
-                onValueChange,
-                onValueChangeFinished,
-                showSteps
-            )
+            Row(verticalAlignment = Alignment.CenterVertically) {
+                if (iconStart != null) {
+                    SliderIcon(icon = iconStart)
+                    Spacer(Modifier.padding(SettingsDimension.paddingSmall))
+                }
+                Box(Modifier.weight(1f)) {
+                    SettingsSlider(
+                        initValue,
+                        modifier,
+                        valueRange,
+                        onValueChange,
+                        onValueChangeFinished,
+                        showSteps,
+                    )
+                }
+                if (iconEnd != null) {
+                    Spacer(Modifier.padding(SettingsDimension.paddingSmall))
+                    SliderIcon(icon = iconEnd)
+                }
+            }
         },
-        icon = if (icon != null) ({
-            Icon(imageVector = icon, contentDescription = null)
-        }) else null,
     )
 }
 
+@Composable
+fun SliderIcon(icon: ImageVector) {
+    Box(modifier = Modifier.padding(8.dp), contentAlignment = Alignment.Center) {
+        Icon(imageVector = icon, contentDescription = null)
+    }
+}
+
 @Preview
 @Composable
 private fun SliderPreferencePreview() {
@@ -147,7 +173,7 @@
             onValueChangeFinished = {
                 println("onValueChangeFinished: the value is $sliderPosition")
             },
-            icon = Icons.Outlined.AccessAlarm,
+            iconStart = Icons.Outlined.AccessAlarm,
         )
     }
 }
@@ -163,7 +189,22 @@
             onValueChange = { it: Int ->
                 icon = if (it > 0) Icons.Outlined.MusicNote else Icons.Outlined.MusicOff
             },
-            icon = icon,
+            iconEnd = icon,
+        )
+    }
+}
+
+@Preview
+@Composable
+private fun SliderPreferenceTwoIconPreview() {
+    SettingsTheme {
+        val iconStart by remember { mutableStateOf(Icons.Outlined.MusicNote) }
+        val iconEnd by remember { mutableStateOf(Icons.Outlined.MusicOff) }
+        SliderPreference(
+            title = "Media Volume",
+            initValue = 40,
+            iconStart = iconStart,
+            iconEnd = iconEnd,
         )
     }
 }
@@ -172,11 +213,6 @@
 @Composable
 private fun SliderPreferenceStepsPreview() {
     SettingsTheme {
-        SliderPreference(
-            title = "Display Text",
-            initValue = 2,
-            valueRange = 1..5,
-            showSteps = true,
-        )
+        SliderPreference(title = "Display Text", initValue = 2, valueRange = 1..5, showSteps = true)
     }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerTest.java b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
index 312e62d..94d3b2c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
@@ -62,7 +62,9 @@
 import com.android.systemui.res.R;
 import com.android.systemui.statusbar.policy.UserSwitcherController;
 import com.android.systemui.user.data.source.UserRecord;
+import com.android.systemui.util.concurrency.FakeExecutor;
 import com.android.systemui.util.settings.GlobalSettings;
+import com.android.systemui.util.time.FakeSystemClock;
 
 import org.junit.Before;
 import org.junit.Rule;
@@ -96,6 +98,7 @@
     private FalsingA11yDelegate mFalsingA11yDelegate;
 
     private KeyguardSecurityContainer mKeyguardSecurityContainer;
+    private FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
 
     @Before
     public void setup() {
@@ -106,6 +109,7 @@
         mSecurityViewFlipper = new KeyguardSecurityViewFlipper(getContext());
         mSecurityViewFlipper.setId(View.generateViewId());
         mKeyguardSecurityContainer = new KeyguardSecurityContainer(getContext());
+        mKeyguardSecurityContainer.setBackgroundExecutor(mExecutor);
         mKeyguardSecurityContainer.setRight(VIEW_WIDTH);
         mKeyguardSecurityContainer.setLeft(0);
         mKeyguardSecurityContainer.setTop(0);
@@ -342,7 +346,7 @@
 
     @Test
     public void testTwoOrMoreUsersDoesAllowDropDown() {
-        // GIVEN one user has been setup
+        // GIVEN two users have been setup
         ArrayList<UserRecord> records = buildUserRecords(2);
         when(mUserSwitcherController.getCurrentUserRecord()).thenReturn(records.get(0));
         when(mUserSwitcherController.getUsers()).thenReturn(records);
@@ -350,7 +354,7 @@
         // WHEN UserSwitcherViewMode is initialized
         setupUserSwitcher();
 
-        // THEN the UserSwitcher anchor should not be clickable
+        // THEN the UserSwitcher anchor should be clickable
         ViewGroup anchor = mKeyguardSecurityContainer.findViewById(R.id.user_switcher_anchor);
         assertThat(anchor.isClickable()).isTrue();
     }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index ffbc85c..f05cbf4 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -109,6 +109,8 @@
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
 
 /** Determines how the bouncer is displayed to the user. */
 public class KeyguardSecurityContainer extends ConstraintLayout {
@@ -170,6 +172,7 @@
     private ViewMode mViewMode = new DefaultViewMode();
     private boolean mIsInteractable;
     protected ViewMediatorCallback mViewMediatorCallback;
+    private Executor mBgExecutor;
     /*
      * Using MODE_UNINITIALIZED to mean the view mode is set to DefaultViewMode, but init() has not
      * yet been called on it. This will happen when the ViewController is initialized.
@@ -352,6 +355,10 @@
         updateBiometricRetry(securityMode, faceAuthEnabled);
     }
 
+    void setBackgroundExecutor(Executor bgExecutor) {
+        mBgExecutor = bgExecutor;
+    }
+
     void initMode(@Mode int mode, GlobalSettings globalSettings, FalsingManager falsingManager,
             UserSwitcherController userSwitcherController,
             UserSwitcherViewMode.UserSwitcherCallback userSwitcherCallback,
@@ -367,7 +374,7 @@
                 mViewMode = new OneHandedViewMode();
                 break;
             case MODE_USER_SWITCHER:
-                mViewMode = new UserSwitcherViewMode(userSwitcherCallback);
+                mViewMode = new UserSwitcherViewMode(userSwitcherCallback, mBgExecutor);
                 break;
             default:
                 mViewMode = new DefaultViewMode();
@@ -991,6 +998,7 @@
         private FalsingManager mFalsingManager;
         private UserSwitcherController mUserSwitcherController;
         private KeyguardUserSwitcherPopupMenu mPopup;
+        private Executor mBgExecutor;
         private Resources mResources;
         private UserSwitcherController.UserSwitchCallback mUserSwitchCallback =
                 this::setupUserSwitcher;
@@ -998,8 +1006,9 @@
         private UserSwitcherCallback mUserSwitcherCallback;
         private FalsingA11yDelegate mFalsingA11yDelegate;
 
-        UserSwitcherViewMode(UserSwitcherCallback userSwitcherCallback) {
+        UserSwitcherViewMode(UserSwitcherCallback userSwitcherCallback, Executor bgExecutor) {
             mUserSwitcherCallback = userSwitcherCallback;
+            mBgExecutor = bgExecutor;
         }
 
         @Override
@@ -1068,18 +1077,22 @@
             mView.removeView(mUserSwitcher);
         }
 
-        private Drawable findLargeUserIcon(int userId) {
-            Bitmap userIcon = UserManager.get(mView.getContext()).getUserIcon(userId);
-            if (userIcon != null) {
-                int iconSize =
-                        mResources.getDimensionPixelSize(R.dimen.bouncer_user_switcher_icon_size);
-                return CircleFramedDrawable.getInstance(
-                    mView.getContext(),
-                    Icon.scaleDownIfNecessary(userIcon, iconSize, iconSize)
-                );
-            }
-
-            return UserIcons.getDefaultUserIcon(mResources, userId, false);
+        private void findLargeUserIcon(int userId, Consumer<Drawable> consumer) {
+            mBgExecutor.execute(() -> {
+                Drawable icon;
+                Bitmap userIcon = UserManager.get(mView.getContext()).getUserIcon(userId);
+                if (userIcon != null) {
+                    int iconSize = mResources.getDimensionPixelSize(
+                            R.dimen.bouncer_user_switcher_icon_size);
+                    icon = CircleFramedDrawable.getInstance(
+                        mView.getContext(),
+                        Icon.scaleDownIfNecessary(userIcon, iconSize, iconSize)
+                    );
+                } else  {
+                    icon = UserIcons.getDefaultUserIcon(mResources, userId, false);
+                }
+                consumer.accept(icon);
+            });
         }
 
         @Override
@@ -1136,8 +1149,15 @@
                 return;
             }
             final String currentUserName = mUserSwitcherController.getCurrentUserName();
-            Drawable userIcon = findLargeUserIcon(currentUser.info.id);
-            ((ImageView) mView.findViewById(R.id.user_icon)).setImageDrawable(userIcon);
+            findLargeUserIcon(currentUser.info.id,
+                    (Drawable userIcon) -> {
+                        mView.post(() -> {
+                            ImageView view = (ImageView) mView.findViewById(R.id.user_icon);
+                            if (view != null) {
+                                view.setImageDrawable(userIcon);
+                            }
+                        });
+                });
             mUserSwitcher.setText(currentUserName);
 
             KeyguardUserSwitcherAnchor anchor = mView.findViewById(R.id.user_switcher_anchor);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index 2d28a18..a408211 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -76,6 +76,7 @@
 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor;
 import com.android.systemui.classifier.FalsingA11yDelegate;
 import com.android.systemui.classifier.FalsingCollector;
+import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor;
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor;
 import com.android.systemui.flags.FeatureFlags;
@@ -102,6 +103,7 @@
 
 import java.io.File;
 import java.util.Arrays;
+import java.util.concurrent.Executor;
 
 import javax.inject.Inject;
 import javax.inject.Provider;
@@ -426,6 +428,7 @@
     private final Provider<JavaAdapter> mJavaAdapter;
     private final DeviceProvisionedController mDeviceProvisionedController;
     private final Lazy<PrimaryBouncerInteractor> mPrimaryBouncerInteractor;
+    private final Executor mBgExecutor;
     @Nullable
     private Job mSceneTransitionCollectionJob;
 
@@ -459,6 +462,7 @@
             DevicePolicyManager devicePolicyManager,
             KeyguardDismissTransitionInteractor keyguardDismissTransitionInteractor,
             Lazy<PrimaryBouncerInteractor> primaryBouncerInteractor,
+            @Background Executor bgExecutor,
             Provider<DeviceEntryInteractor> deviceEntryInteractor
     ) {
         super(view);
@@ -493,11 +497,13 @@
         mDeviceProvisionedController = deviceProvisionedController;
         mPrimaryBouncerInteractor = primaryBouncerInteractor;
         mDevicePolicyManager = devicePolicyManager;
+        mBgExecutor = bgExecutor;
     }
 
     @Override
     public void onInit() {
         mSecurityViewFlipperController.init();
+        mView.setBackgroundExecutor(mBgExecutor);
         updateResources();
         configureMode();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt
index 93cd1cf4..5e05dab 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt
@@ -403,7 +403,7 @@
     SubCategoryTitle(subCategory.label)
     subCategory.shortcuts.fastForEachIndexed { index, shortcut ->
         if (index > 0) {
-            HorizontalDivider()
+            HorizontalDivider(color = MaterialTheme.colorScheme.surfaceContainerHigh)
         }
         ShortcutView(Modifier.padding(vertical = 24.dp), searchQuery, shortcut)
     }
@@ -482,7 +482,7 @@
             Spacer(Modifier.height(8.dp))
             subCategory.shortcuts.fastForEachIndexed { index, shortcut ->
                 if (index > 0) {
-                    HorizontalDivider()
+                    HorizontalDivider(color = MaterialTheme.colorScheme.surfaceContainerHigh)
                 }
                 ShortcutView(Modifier.padding(vertical = 16.dp), searchQuery, shortcut)
             }
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
index e444db4..5fe5cb3 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
@@ -76,6 +76,7 @@
 import com.android.systemui.statusbar.policy.UserSwitcherController
 import com.android.systemui.testKosmos
 import com.android.systemui.user.domain.interactor.SelectedUserInteractor
+import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.kotlin.JavaAdapter
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.argThat
@@ -84,6 +85,7 @@
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
 import com.android.systemui.util.settings.GlobalSettings
+import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth
 import junit.framework.Assert
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -172,6 +174,7 @@
     @Mock private lateinit var primaryBouncerInteractor: PrimaryBouncerInteractor
     private lateinit var sceneTransitionStateFlow: MutableStateFlow<ObservableTransitionState>
     private lateinit var fakeSceneDataSource: FakeSceneDataSource
+    private val executor = FakeExecutor(FakeSystemClock())
 
     private lateinit var underTest: KeyguardSecurityContainerController
 
@@ -210,13 +213,9 @@
         featureFlags = FakeFeatureFlags()
         featureFlags.set(Flags.LOCKSCREEN_ENABLE_LANDSCAPE, false)
 
-        mSetFlagsRule.enableFlags(
-            AConfigFlags.FLAG_REVAMPED_BOUNCER_MESSAGES,
-        )
+        mSetFlagsRule.enableFlags(AConfigFlags.FLAG_REVAMPED_BOUNCER_MESSAGES)
         if (!SceneContainerFlag.isEnabled) {
-            mSetFlagsRule.disableFlags(
-                AConfigFlags.FLAG_KEYGUARD_WM_STATE_REFACTOR,
-            )
+            mSetFlagsRule.disableFlags(AConfigFlags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
         }
 
         keyguardPasswordViewController =
@@ -239,7 +238,7 @@
                 mSelectedUserInteractor,
                 keyguardKeyboardInteractor,
                 null,
-                mUserActivityNotifier
+                mUserActivityNotifier,
             )
 
         kosmos = testKosmos()
@@ -283,6 +282,7 @@
                 devicePolicyManager,
                 kosmos.keyguardDismissTransitionInteractor,
                 { primaryBouncerInteractor },
+                executor,
             ) {
                 deviceEntryInteractor
             }
@@ -298,7 +298,7 @@
                 eq(falsingManager),
                 eq(userSwitcherController),
                 any(),
-                eq(falsingA11yDelegate)
+                eq(falsingA11yDelegate),
             )
     }
 
@@ -334,7 +334,7 @@
                 eq(falsingManager),
                 eq(userSwitcherController),
                 any(),
-                eq(falsingA11yDelegate)
+                eq(falsingA11yDelegate),
             )
 
         // Update rotation. Should trigger update
@@ -347,7 +347,7 @@
                 eq(falsingManager),
                 eq(userSwitcherController),
                 any(),
-                eq(falsingA11yDelegate)
+                eq(falsingA11yDelegate),
             )
     }
 
@@ -359,7 +359,7 @@
                 MotionEvent.ACTION_DOWN,
                 /* x= */ 0f,
                 /* y= */ 0f,
-                /* metaState= */ 0
+                /* metaState= */ 0,
             )
         )
     }
@@ -386,7 +386,7 @@
                 eq(falsingManager),
                 eq(userSwitcherController),
                 any(),
-                eq(falsingA11yDelegate)
+                eq(falsingA11yDelegate),
             )
     }
 
@@ -401,7 +401,7 @@
                 eq(falsingManager),
                 eq(userSwitcherController),
                 any(),
-                eq(falsingA11yDelegate)
+                eq(falsingA11yDelegate),
             )
     }
 
@@ -416,7 +416,7 @@
                 eq(falsingManager),
                 eq(userSwitcherController),
                 any(),
-                eq(falsingA11yDelegate)
+                eq(falsingA11yDelegate),
             )
     }
 
@@ -431,7 +431,7 @@
                 eq(falsingManager),
                 eq(userSwitcherController),
                 any(),
-                eq(falsingA11yDelegate)
+                eq(falsingA11yDelegate),
             )
     }
 
@@ -446,7 +446,7 @@
                 eq(falsingManager),
                 eq(userSwitcherController),
                 any(),
-                eq(falsingA11yDelegate)
+                eq(falsingA11yDelegate),
             )
     }
 
@@ -462,7 +462,7 @@
             .showMessage(
                 /* message= */ context.getString(R.string.keyguard_unlock_to_continue),
                 /* colorState= */ null,
-                /* animated= */ true
+                /* animated= */ true,
             )
     }
 
@@ -496,7 +496,7 @@
             /* authenticated= */ true,
             TARGET_USER_ID,
             /* bypassSecondaryLockScreen= */ true,
-            SecurityMode.SimPin
+            SecurityMode.SimPin,
         )
 
         // THEN the next security method of None will dismiss keyguard.
@@ -514,7 +514,7 @@
                 /* authenticated= */ true,
                 TARGET_USER_ID,
                 /* bypassSecondaryLockScreen= */ true,
-                SecurityMode.SimPin
+                SecurityMode.SimPin,
             )
 
         // THEN no action has happened, which will not dismiss the security screens
@@ -539,7 +539,7 @@
             /* authenticated= */ true,
             TARGET_USER_ID,
             /* bypassSecondaryLockScreen= */ true,
-            SecurityMode.SimPin
+            SecurityMode.SimPin,
         )
 
         // THEN the next security method of None will dismiss keyguard.
@@ -564,7 +564,7 @@
             /* authenticated= */ true,
             TARGET_USER_ID,
             /* bypassSecondaryLockScreen= */ true,
-            SecurityMode.SimPin
+            SecurityMode.SimPin,
         )
 
         // THEN the next security method of None will dismiss keyguard.
@@ -589,7 +589,7 @@
             /* authenticated= */ true,
             TARGET_USER_ID,
             /* bypassSecondaryLockScreen= */ true,
-            SecurityMode.SimPin
+            SecurityMode.SimPin,
         )
 
         // THEN we will not show the password screen.
@@ -615,7 +615,7 @@
             /* authenticated= */ true,
             TARGET_USER_ID,
             /* bypassSecondaryLockScreen= */ true,
-            SecurityMode.SimPin
+            SecurityMode.SimPin,
         )
 
         // THEN we will not show the password screen.
@@ -717,7 +717,7 @@
         // Now simulate a config change
         testableResources.addOverride(
             R.integer.keyguard_host_view_gravity,
-            Gravity.CENTER_HORIZONTAL or Gravity.BOTTOM
+            Gravity.CENTER_HORIZONTAL or Gravity.BOTTOM,
         )
         underTest.updateResources()
         verify(view).layoutParams = any()
@@ -728,7 +728,7 @@
         testableResources.addOverride(R.integer.keyguard_host_view_gravity, Gravity.CENTER)
         testableResources.addOverride(
             R.integer.keyguard_host_view_one_handed_gravity,
-            Gravity.CENTER_HORIZONTAL or Gravity.BOTTOM
+            Gravity.CENTER_HORIZONTAL or Gravity.BOTTOM,
         )
 
         // Start disabled.
@@ -948,7 +948,7 @@
             /* expiringUserId = */ mainUserId,
             /* mainUserId = */ mainUserId,
             /* remainingBeforeWipe = */ 1,
-            /* failedAttempts = */ 1
+            /* failedAttempts = */ 1,
         )
 
         verify(view)
@@ -965,14 +965,14 @@
             /* expiringUserId = */ secondaryUserId,
             /* mainUserId = */ mainUserId,
             /* remainingBeforeWipe = */ 1,
-            /* failedAttempts = */ 1
+            /* failedAttempts = */ 1,
         )
 
         verify(view)
             .showAlmostAtWipeDialog(
                 any(),
                 any(),
-                eq(KeyguardSecurityContainer.USER_TYPE_SECONDARY_USER)
+                eq(KeyguardSecurityContainer.USER_TYPE_SECONDARY_USER),
             )
     }
 
diff --git a/services/accessibility/java/com/android/server/accessibility/MouseKeysInterceptor.java b/services/accessibility/java/com/android/server/accessibility/MouseKeysInterceptor.java
index 54368ca..4b97745 100644
--- a/services/accessibility/java/com/android/server/accessibility/MouseKeysInterceptor.java
+++ b/services/accessibility/java/com/android/server/accessibility/MouseKeysInterceptor.java
@@ -73,12 +73,16 @@
 
     private static final int MESSAGE_MOVE_MOUSE_POINTER = 1;
     private static final int MESSAGE_SCROLL_MOUSE_POINTER = 2;
-    private static final float MOUSE_POINTER_MOVEMENT_STEP = 1.8f;
     private static final int KEY_NOT_SET = -1;
 
     /** Time interval after which mouse action will be repeated */
     private static final int INTERVAL_MILLIS = 10;
 
+    @VisibleForTesting
+    public static final float MOUSE_POINTER_MOVEMENT_STEP = 1.8f;
+    @VisibleForTesting
+    public static final float MOUSE_SCROLL_STEP = 0.2f;
+
     private final AccessibilityManagerService mAms;
     private final Handler mHandler;
     private final InputManager mInputManager;
@@ -281,8 +285,8 @@
         MouseKeyEvent mouseKeyEvent = MouseKeyEvent.from(
                 keyCode, mActiveInputDeviceId, mDeviceKeyCodeMap);
         float y = switch (mouseKeyEvent) {
-            case UP_MOVE_OR_SCROLL -> 1.0f;
-            case DOWN_MOVE_OR_SCROLL -> -1.0f;
+            case UP_MOVE_OR_SCROLL -> MOUSE_SCROLL_STEP;
+            case DOWN_MOVE_OR_SCROLL -> -MOUSE_SCROLL_STEP;
             default -> 0.0f;
         };
         waitForVirtualMouseCreation();
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 71cb882..e053964 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -2207,7 +2207,7 @@
                     + ", groupId=" + powerGroup.getGroupId()
                     + ", reason=" + PowerManager.wakeReasonToString(reason) + ", uid=" + uid);
         }
-        if (mForceSuspendActive || !mSystemReady) {
+        if (mForceSuspendActive || !mSystemReady || (powerGroup == null)) {
             return;
         }
         powerGroup.wakeUpLocked(eventTime, reason, details, uid, opPackageName, opUid,
@@ -6027,6 +6027,12 @@
         @Override // Binder call
         public void wakeUp(long eventTime, @WakeReason int reason, String details,
                 String opPackageName) {
+            wakeUpWithDisplayId(eventTime, reason, details, opPackageName, Display.DEFAULT_DISPLAY);
+        }
+
+        @Override // Binder call
+        public void wakeUpWithDisplayId(long eventTime, @WakeReason int reason, String details,
+                String opPackageName, int displayId) {
             final long now = mClock.uptimeMillis();
             if (eventTime > now) {
                 Slog.e(TAG, "Event time " + eventTime + " cannot be newer than " + now);
@@ -6039,13 +6045,14 @@
             final int uid = Binder.getCallingUid();
             final long ident = Binder.clearCallingIdentity();
             try {
+                int displayGroupId = getDisplayGroupId(displayId);
                 synchronized (mLock) {
                     if (!mBootCompleted && sQuiescent) {
                         mDirty |= DIRTY_QUIESCENT;
                         updatePowerStateLocked();
                         return;
                     }
-                    wakePowerGroupLocked(mPowerGroups.get(Display.DEFAULT_DISPLAY_GROUP), eventTime,
+                    wakePowerGroupLocked(mPowerGroups.get(displayGroupId), eventTime,
                             reason, details, uid, opPackageName, uid);
                 }
             } finally {
@@ -7335,4 +7342,12 @@
             }
         }
     }
+
+    private int getDisplayGroupId(int displayId) {
+        DisplayInfo displayInfo = mDisplayManagerInternal.getDisplayInfo(displayId);
+        if (displayInfo == null) {
+            return Display.INVALID_DISPLAY_GROUP;
+        }
+        return displayInfo.displayGroupId;
+    }
 }
diff --git a/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java
index b58c28b..54a02cf 100644
--- a/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java
+++ b/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java
@@ -268,6 +268,10 @@
 
         mClock = new OffsettableClock.Stopped();
         mTestLooper = new TestLooper(mClock::now);
+        DisplayInfo displayInfo = Mockito.mock(DisplayInfo.class);
+        displayInfo.displayGroupId = Display.DEFAULT_DISPLAY_GROUP;
+        when(mDisplayManagerInternalMock.getDisplayInfo(Display.DEFAULT_DISPLAY))
+                .thenReturn(displayInfo);
     }
 
     private PowerManagerService createService() {
@@ -794,6 +798,57 @@
         assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);
     }
 
+    @Test
+    public void testWakefulnessPerGroup_IPowerManagerWakeUpWithDisplayId() {
+        final int nonDefaultPowerGroupId = Display.DEFAULT_DISPLAY_GROUP + 1;
+        int displayInNonDefaultGroup = 1;
+        final AtomicReference<DisplayManagerInternal.DisplayGroupListener> listener =
+                new AtomicReference<>();
+        long eventTime1 = 10;
+        long eventTime2 = eventTime1 + 1;
+        long eventTime3 = eventTime2 + 1;
+        doAnswer((Answer<Void>) invocation -> {
+            listener.set(invocation.getArgument(0));
+            return null;
+        }).when(mDisplayManagerInternalMock).registerDisplayGroupListener(any());
+
+        createService();
+        startSystem();
+        listener.get().onDisplayGroupAdded(nonDefaultPowerGroupId);
+
+        // Verify the global wakefulness is AWAKE
+        assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);
+
+        // Transition default display to doze, and verify the global wakefulness
+        mService.setWakefulnessLocked(Display.DEFAULT_DISPLAY_GROUP, WAKEFULNESS_DOZING, eventTime1,
+                0, PowerManager.GO_TO_SLEEP_REASON_INATTENTIVE, 0, null, null);
+        assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);
+
+        // Transition the display from non default power group to doze, and verify the change in
+        // the global wakefulness
+        mService.setWakefulnessLocked(nonDefaultPowerGroupId, WAKEFULNESS_DOZING, eventTime2,
+                0, PowerManager.GO_TO_SLEEP_REASON_APPLICATION, 0, null, null);
+        assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_DOZING);
+        assertThat(mService.getWakefulnessLocked(nonDefaultPowerGroupId))
+                .isEqualTo(WAKEFULNESS_DOZING);
+
+        // Wakeup the display from the non default power group
+        DisplayInfo displayInfo = Mockito.mock(DisplayInfo.class);
+        displayInfo.displayGroupId = nonDefaultPowerGroupId;
+        when(mDisplayManagerInternalMock.getDisplayInfo(displayInNonDefaultGroup))
+                .thenReturn(displayInfo);
+        mClock.fastForward(eventTime3);
+        mService.getBinderServiceInstance().wakeUpWithDisplayId(eventTime3,
+                PowerManager.WAKE_REASON_APPLICATION, "testing IPowerManager.wakeUp()",
+                "pkg.name", displayInNonDefaultGroup);
+
+        assertThat(mService.getWakefulnessLocked(nonDefaultPowerGroupId))
+                .isEqualTo(WAKEFULNESS_AWAKE);
+        assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);
+        assertThat(mService.getWakefulnessLocked(Display.DEFAULT_DISPLAY))
+                .isEqualTo(WAKEFULNESS_DOZING);
+    }
+
     /**
      * Tests a series of variants that control whether a device wakes-up when it is plugged in
      * or docked.
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/MouseKeysInterceptorTest.kt b/services/tests/servicestests/src/com/android/server/accessibility/MouseKeysInterceptorTest.kt
index 019ccf9..c76392b 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/MouseKeysInterceptorTest.kt
+++ b/services/tests/servicestests/src/com/android/server/accessibility/MouseKeysInterceptorTest.kt
@@ -61,7 +61,6 @@
     companion object {
         const val DISPLAY_ID = 1
         const val DEVICE_ID = 123
-        const val MOUSE_POINTER_MOVEMENT_STEP = 1.8f
         // This delay is required for key events to be sent and handled correctly.
         // The handler only performs a move/scroll event if it receives the key event
         // at INTERVAL_MILLIS (which happens in practice). Hence, we need this delay in the tests.
@@ -159,8 +158,8 @@
         testLooper.dispatchAll()
 
         // Verify the sendRelativeEvent method is called once and capture the arguments
-        verifyRelativeEvents(arrayOf(-MOUSE_POINTER_MOVEMENT_STEP / sqrt(2.0f)),
-            arrayOf(MOUSE_POINTER_MOVEMENT_STEP / sqrt(2.0f)))
+        verifyRelativeEvents(arrayOf(-MouseKeysInterceptor.MOUSE_POINTER_MOVEMENT_STEP / sqrt(2.0f)),
+            arrayOf(MouseKeysInterceptor.MOUSE_POINTER_MOVEMENT_STEP / sqrt(2.0f)))
     }
 
     @Test
@@ -232,7 +231,8 @@
         testLooper.dispatchAll()
 
         // Verify the sendScrollEvent method is called once and capture the arguments
-        verifyScrollEvents(arrayOf<Float>(0f), arrayOf<Float>(1.0f))
+        verifyScrollEvents(arrayOf<Float>(0f),
+	    arrayOf<Float>(MouseKeysInterceptor.MOUSE_SCROLL_STEP))
     }
 
     @Test
@@ -247,7 +247,8 @@
         testLooper.dispatchAll()
 
         // Verify the sendRelativeEvent method is called once and capture the arguments
-        verifyRelativeEvents(arrayOf<Float>(0f), arrayOf<Float>(-MOUSE_POINTER_MOVEMENT_STEP))
+        verifyRelativeEvents(arrayOf<Float>(0f),
+	    arrayOf<Float>(-MouseKeysInterceptor.MOUSE_POINTER_MOVEMENT_STEP))
     }
 
     private fun verifyRelativeEvents(expectedX: Array<Float>, expectedY: Array<Float>) {