Merge "Fix a bug in ContrastColorUtil that was copying NoCopySpan." into main
diff --git a/core/api/current.txt b/core/api/current.txt
index c5a70df..3c5a31c 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -43911,6 +43911,7 @@
     field @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") public static final String KEY_SATELLITE_ENTITLEMENT_STATUS_REFRESH_DAYS_INT = "satellite_entitlement_status_refresh_days_int";
     field @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") public static final String KEY_SATELLITE_ENTITLEMENT_SUPPORTED_BOOL = "satellite_entitlement_supported_bool";
     field @FlaggedApi("com.android.internal.telephony.flags.carrier_roaming_nb_iot_ntn") public static final String KEY_SATELLITE_ESOS_SUPPORTED_BOOL = "satellite_esos_supported_bool";
+    field @FlaggedApi("com.android.internal.telephony.flags.carrier_roaming_nb_iot_ntn") public static final String KEY_SATELLITE_SCREEN_OFF_INACTIVITY_TIMEOUT_SEC_INT = "satellite_screen_off_inactivity_timeout_duration_sec_int";
     field public static final String KEY_SHOW_4G_FOR_3G_DATA_ICON_BOOL = "show_4g_for_3g_data_icon_bool";
     field public static final String KEY_SHOW_4G_FOR_LTE_DATA_ICON_BOOL = "show_4g_for_lte_data_icon_bool";
     field public static final String KEY_SHOW_APN_SETTING_CDMA_BOOL = "show_apn_setting_cdma_bool";
diff --git a/core/java/android/hardware/display/BrightnessInfo.java b/core/java/android/hardware/display/BrightnessInfo.java
index c091062..109b0a8 100644
--- a/core/java/android/hardware/display/BrightnessInfo.java
+++ b/core/java/android/hardware/display/BrightnessInfo.java
@@ -60,7 +60,8 @@
     @IntDef(prefix = {"BRIGHTNESS_MAX_REASON_"}, value = {
             BRIGHTNESS_MAX_REASON_NONE,
             BRIGHTNESS_MAX_REASON_THERMAL,
-            BRIGHTNESS_MAX_REASON_POWER_IC
+            BRIGHTNESS_MAX_REASON_POWER_IC,
+            BRIGHTNESS_MAX_REASON_WEAR_BEDTIME_MODE
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface BrightnessMaxReason {}
diff --git a/libs/WindowManager/Shell/res/drawable/desktop_mode_ic_handle_menu_new_window.xml b/libs/WindowManager/Shell/res/drawable/desktop_mode_ic_handle_menu_new_window.xml
new file mode 100644
index 0000000..c154059
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/desktop_mode_ic_handle_menu_new_window.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="20dp"
+    android:height="20dp"
+    android:viewportWidth="20"
+    android:viewportHeight="20">
+  <path
+      android:pathData="M15 16V14H13V12.5H15V10.5H16.5V12.5H18.5V14H16.5V16H15ZM3.5 17C3.09722 17 2.74306 16.8542 2.4375 16.5625C2.14583 16.2569 2 15.9028 2 15.5V4.5C2 4.08333 2.14583 3.72917 2.4375 3.4375C2.74306 3.14583 3.09722 3 3.5 3H14.5C14.9167 3 15.2708 3.14583 15.5625 3.4375C15.8542 3.72917 16 4.08333 16 4.5V9H14.5V7H3.5V15.5H13.625V17H3.5ZM3.5 5.5H14.5V4.5H3.5V5.5ZM3.5 5.5V4.5V5.5Z"
+      android:fillColor="#1C1C14"/>
+</vector>
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml
index 49d9029..eea3de8 100644
--- a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml
@@ -125,7 +125,7 @@
     <LinearLayout
         android:id="@+id/more_actions_pill"
         android:layout_width="match_parent"
-        android:layout_height="@dimen/desktop_mode_handle_menu_more_actions_pill_height"
+        android:layout_height="wrap_content"
         android:layout_marginTop="@dimen/desktop_mode_handle_menu_pill_spacing_margin"
         android:layout_marginStart="1dp"
         android:orientation="vertical"
@@ -139,6 +139,14 @@
             android:drawableStart="@drawable/desktop_mode_ic_handle_menu_screenshot"
             android:drawableTint="?androidprv:attr/materialColorOnSurface"
             style="@style/DesktopModeHandleMenuActionButton"/>
+
+        <Button
+            android:id="@+id/new_window_button"
+            android:contentDescription="@string/new_window_text"
+            android:text="@string/new_window_text"
+            android:drawableStart="@drawable/desktop_mode_ic_handle_menu_new_window"
+            android:drawableTint="?androidprv:attr/materialColorOnSurface"
+            style="@style/DesktopModeHandleMenuActionButton" />
     </LinearLayout>
 
     <LinearLayout
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index 1eb2458..e6807ac 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -497,7 +497,7 @@
 
     <!-- The maximum height of the handle menu in desktop mode. Four pills (52dp each) plus 2dp
         spacing between them plus 4dp top padding. -->
-    <dimen name="desktop_mode_handle_menu_height">218dp</dimen>
+    <dimen name="desktop_mode_handle_menu_height">270dp</dimen>
 
     <!-- The elevation set on the handle menu pills. -->
     <dimen name="desktop_mode_handle_menu_pill_elevation">1dp</dimen>
@@ -508,8 +508,11 @@
     <!-- The height of the handle menu's "Windowing" pill in desktop mode. -->
     <dimen name="desktop_mode_handle_menu_windowing_pill_height">52dp</dimen>
 
-    <!-- The height of the handle menu's "More Actions" pill in desktop mode. -->
-    <dimen name="desktop_mode_handle_menu_more_actions_pill_height">52dp</dimen>
+    <!-- The maximum height of the handle menu's "New Window" button in desktop mode. -->
+    <dimen name="desktop_mode_handle_menu_new_window_height">52dp</dimen>
+
+    <!-- The maximum height of the handle menu's "Screenshot" button in desktop mode. -->
+    <dimen name="desktop_mode_handle_menu_screenshot_height">52dp</dimen>
 
     <!-- The height of the handle menu's "Open in browser" pill in desktop mode. -->
     <dimen name="desktop_mode_handle_menu_open_in_browser_pill_height">52dp</dimen>
diff --git a/libs/WindowManager/Shell/res/values/strings.xml b/libs/WindowManager/Shell/res/values/strings.xml
index 8669af3..0a8166f 100644
--- a/libs/WindowManager/Shell/res/values/strings.xml
+++ b/libs/WindowManager/Shell/res/values/strings.xml
@@ -282,6 +282,8 @@
     <string name="screenshot_text">Screenshot</string>
     <!-- Accessibility text for the handle menu open in browser button [CHAR LIMIT=NONE] -->
     <string name="open_in_browser_text">Open in browser</string>
+    <!-- Accessibility text for the handle menu new window button [CHAR LIMIT=NONE] -->
+    <string name="new_window_text">New Window</string>
     <!-- Accessibility text for the handle menu close button [CHAR LIMIT=NONE] -->
     <string name="close_text">Close</string>
     <!-- Accessibility text for the handle menu close menu button [CHAR LIMIT=NONE] -->
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlags.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlags.kt
index 2493e98..6d63971 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlags.kt
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlags.kt
@@ -46,7 +46,8 @@
     DYNAMIC_INITIAL_BOUNDS(Flags::enableWindowingDynamicInitialBounds, true),
     ENABLE_DESKTOP_WINDOWING_TASK_LIMIT(Flags::enableDesktopWindowingTaskLimit, true),
     BACK_NAVIGATION(Flags::enableDesktopWindowingBackNavigation, true),
-    EDGE_DRAG_RESIZE(Flags::enableWindowingEdgeDragResize, true);
+    EDGE_DRAG_RESIZE(Flags::enableWindowingEdgeDragResize, true),
+    TASKBAR_RUNNING_APPS(Flags::enableDesktopWindowingTaskbarRunningApps, true);
 
     /**
      * Determines state of flag based on the actual flag and desktop mode developer option
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/MultiInstanceHelper.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/MultiInstanceHelper.kt
index a6be640..4cd2fd0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/MultiInstanceHelper.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/MultiInstanceHelper.kt
@@ -22,7 +22,6 @@
 import android.content.pm.PackageManager
 import android.os.UserHandle
 import android.view.WindowManager.PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI
-import com.android.internal.annotations.VisibleForTesting
 import com.android.internal.protolog.ProtoLog
 import com.android.wm.shell.R
 import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL
@@ -41,7 +40,6 @@
     /**
      * Returns whether a specific component desires to be launched in multiple instances.
      */
-    @VisibleForTesting
     fun supportsMultiInstanceSplit(componentName: ComponentName?): Boolean {
         if (componentName == null || componentName.packageName == null) {
             // TODO(b/262864589): Handle empty component case
@@ -60,7 +58,7 @@
 
         if (!supportsMultiInstanceProperty) {
             // If not checking the multi-instance properties, then return early
-            return false;
+            return false
         }
 
         // Check the activity property first
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java
index 08de900..271c07d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java
@@ -19,6 +19,8 @@
 import static android.view.WindowManager.LARGE_SCREEN_SMALLEST_SCREEN_WIDTH_DP;
 import static android.window.TaskConstants.TASK_CHILD_LAYER_COMPAT_UI;
 
+import static com.android.wm.shell.shared.desktopmode.DesktopModeFlags.DESKTOP_WINDOWING_MODE;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.TaskInfo;
@@ -38,7 +40,6 @@
 import com.android.wm.shell.compatui.api.CompatUIEvent;
 import com.android.wm.shell.compatui.impl.CompatUIEvents.SizeCompatRestartButtonAppeared;
 import com.android.wm.shell.shared.desktopmode.DesktopModeFlags;
-import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
 
 import java.util.function.Consumer;
 
@@ -82,7 +83,7 @@
         super(context, taskInfo, syncQueue, taskListener, displayLayout);
         mCallback = callback;
         mHasSizeCompat = taskInfo.appCompatTaskInfo.topActivityInSizeCompat;
-        if (DesktopModeStatus.canEnterDesktopMode(context)
+        if (DESKTOP_WINDOWING_MODE.isEnabled(mContext)
                 && DesktopModeFlags.DYNAMIC_INITIAL_BOUNDS.isEnabled(context)) {
             // Don't show the SCM button for freeform tasks
             mHasSizeCompat &= !taskInfo.isFreeform();
@@ -138,7 +139,7 @@
             boolean canShow) {
         final boolean prevHasSizeCompat = mHasSizeCompat;
         mHasSizeCompat = taskInfo.appCompatTaskInfo.topActivityInSizeCompat;
-        if (DesktopModeStatus.canEnterDesktopMode(mContext)
+        if (DESKTOP_WINDOWING_MODE.isEnabled(mContext)
                 && DesktopModeFlags.DYNAMIC_INITIAL_BOUNDS.isEnabled(mContext)) {
             // Don't show the SCM button for freeform tasks
             mHasSizeCompat &= !taskInfo.isFreeform();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index da1af0d..9cfbde4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -226,7 +226,8 @@
             Optional<DesktopTasksController> desktopTasksController,
             RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
             InteractionJankMonitor interactionJankMonitor,
-            AppToWebGenericLinksParser genericLinksParser) {
+            AppToWebGenericLinksParser genericLinksParser,
+            MultiInstanceHelper multiInstanceHelper) {
         if (DesktopModeStatus.canEnterDesktopMode(context)) {
             return new DesktopModeWindowDecorViewModel(
                     context,
@@ -246,7 +247,8 @@
                     desktopTasksController,
                     rootTaskDisplayAreaOrganizer,
                     interactionJankMonitor,
-                    genericLinksParser);
+                    genericLinksParser,
+                    multiInstanceHelper);
         }
         return new CaptionWindowDecorViewModel(
                 context,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
index 7175ec4..2bb172f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
@@ -98,6 +98,7 @@
 import com.android.wm.shell.windowdecor.MoveToDesktopAnimator
 import com.android.wm.shell.windowdecor.OnTaskResizeAnimationListener
 import com.android.wm.shell.windowdecor.extension.isFullscreen
+import com.android.wm.shell.windowdecor.extension.isMultiWindow
 import java.io.PrintWriter
 import java.util.Optional
 import java.util.concurrent.Executor
@@ -902,6 +903,60 @@
             request.triggerTask != null
     }
 
+    fun openNewWindow(
+        taskInfo: RunningTaskInfo
+    ) {
+        // TODO(b/337915660): Add a transition handler for these; animations
+        //  need updates in some cases.
+        val newTaskWindowingMode = when {
+            taskInfo.isFreeform -> {
+                WINDOWING_MODE_FREEFORM
+            }
+            taskInfo.isFullscreen || taskInfo.isMultiWindow -> {
+                WINDOWING_MODE_MULTI_WINDOW
+            }
+            else -> {
+                error("Invalid windowing mode: ${taskInfo.windowingMode}")
+            }
+        }
+
+        val baseActivity = taskInfo.baseActivity ?: return
+        val fillIn: Intent = context.packageManager
+            .getLaunchIntentForPackage(
+                baseActivity.packageName
+            ) ?: return
+        fillIn
+            .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_MULTIPLE_TASK)
+        val options =
+            ActivityOptions.makeBasic().apply {
+                launchWindowingMode = newTaskWindowingMode
+                isPendingIntentBackgroundActivityLaunchAllowedByPermission = true
+                pendingIntentBackgroundActivityStartMode =
+                    ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED
+            }
+        val launchIntent = PendingIntent.getActivity(
+            context,
+            /* requestCode= */ 0,
+            fillIn,
+            PendingIntent.FLAG_IMMUTABLE
+        )
+        when (newTaskWindowingMode) {
+            WINDOWING_MODE_MULTI_WINDOW -> {
+                val splitPosition = splitScreenController.determineNewInstancePosition(taskInfo)
+                splitScreenController.startIntent(
+                    launchIntent, context.userId, fillIn, splitPosition,
+                    options.toBundle(), null /* hideTaskToken */
+                )
+            }
+            WINDOWING_MODE_FREEFORM -> {
+                // TODO(b/336289597): This currently does not respect the desktop window limit.
+                val wct = WindowContainerTransaction()
+                wct.sendPendingIntent(launchIntent, fillIn, options.toBundle())
+                transitions.startTransition(TRANSIT_OPEN, wct, null)
+            }
+        }
+    }
+
     private fun handleFreeformTaskLaunch(
         task: RunningTaskInfo,
         transition: IBinder
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
index 54f908b..da7e03f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
@@ -19,7 +19,6 @@
 import static android.app.ActivityTaskManager.INVALID_TASK_ID;
 import static android.content.pm.PackageManager.FEATURE_PC;
 
-import static com.android.window.flags.Flags.enableDesktopWindowingTaskbarRunningApps;
 import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_RECENT_TASKS;
 
 import android.app.ActivityManager;
@@ -365,7 +364,7 @@
     private boolean shouldEnableRunningTasksForDesktopMode() {
         return mPcFeatureEnabled
                 || (DesktopModeStatus.canEnterDesktopMode(mContext)
-                && enableDesktopWindowingTaskbarRunningApps());
+                && DesktopModeFlags.TASKBAR_RUNNING_APPS.isEnabled(mContext));
     }
 
     @VisibleForTesting
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index b857556..c4af148 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -19,6 +19,7 @@
 import static android.app.ActivityManager.START_SUCCESS;
 import static android.app.ActivityManager.START_TASK_TO_FRONT;
 import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
 import static android.content.Intent.FLAG_ACTIVITY_NO_USER_ACTION;
 import static android.view.Display.DEFAULT_DISPLAY;
@@ -431,6 +432,20 @@
         mStageCoordinator.clearSplitPairedInRecents(reason);
     }
 
+    /**
+     * Determines which split position a new instance of a task should take.
+     * @param callingTask The task requesting a new instance.
+     * @return the split position of the new instance
+     */
+    public int determineNewInstancePosition(@NonNull ActivityManager.RunningTaskInfo callingTask) {
+        if (callingTask.getWindowingMode() == WINDOWING_MODE_FULLSCREEN
+                || getSplitPosition(callingTask.taskId) == SPLIT_POSITION_TOP_OR_LEFT) {
+            return SPLIT_POSITION_BOTTOM_OR_RIGHT;
+        } else {
+            return SPLIT_POSITION_TOP_OR_LEFT;
+        }
+    }
+
     public void enterSplitScreen(int taskId, boolean leftOrTop) {
         enterSplitScreen(taskId, leftOrTop, new WindowContainerTransaction());
     }
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 ecd5eda..633067d 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
@@ -93,6 +93,7 @@
 import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.common.DisplayInsetsController;
 import com.android.wm.shell.common.DisplayLayout;
+import com.android.wm.shell.common.MultiInstanceHelper;
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.SyncTransactionQueue;
 import com.android.wm.shell.common.desktopmode.DesktopModeTransitionSource;
@@ -145,6 +146,7 @@
     private final DesktopTasksController mDesktopTasksController;
     private final InputManager mInputManager;
     private final InteractionJankMonitor mInteractionJankMonitor;
+    private final MultiInstanceHelper mMultiInstanceHelper;
     private boolean mTransitionDragActive;
 
     private SparseArray<EventReceiver> mEventReceiversByDisplay = new SparseArray<>();
@@ -204,7 +206,8 @@
             Optional<DesktopTasksController> desktopTasksController,
             RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
             InteractionJankMonitor interactionJankMonitor,
-            AppToWebGenericLinksParser genericLinksParser
+            AppToWebGenericLinksParser genericLinksParser,
+            MultiInstanceHelper multiInstanceHelper
     ) {
         this(
                 context,
@@ -223,6 +226,7 @@
                 transitions,
                 desktopTasksController,
                 genericLinksParser,
+                multiInstanceHelper,
                 new DesktopModeWindowDecoration.Factory(),
                 new InputMonitorFactory(),
                 SurfaceControl.Transaction::new,
@@ -249,6 +253,7 @@
             Transitions transitions,
             Optional<DesktopTasksController> desktopTasksController,
             AppToWebGenericLinksParser genericLinksParser,
+            MultiInstanceHelper multiInstanceHelper,
             DesktopModeWindowDecoration.Factory desktopModeWindowDecorFactory,
             InputMonitorFactory inputMonitorFactory,
             Supplier<SurfaceControl.Transaction> transactionFactory,
@@ -268,6 +273,7 @@
         mSyncQueue = syncQueue;
         mTransitions = transitions;
         mDesktopTasksController = desktopTasksController.get();
+        mMultiInstanceHelper = multiInstanceHelper;
         mShellCommandHandler = shellCommandHandler;
         mWindowManager = windowManager;
         mDesktopModeWindowDecorFactory = desktopModeWindowDecorFactory;
@@ -536,6 +542,9 @@
                 decoration.onOpenInBrowserClick();
             } else if (id == R.id.collapse_menu_button) {
                 decoration.closeHandleMenu();
+            } else if (id == R.id.new_window_button) {
+                decoration.closeHandleMenu();
+                mDesktopTasksController.openNewWindow(decoration.mTaskInfo);
             } else if (id == R.id.maximize_window) {
                 // TODO(b/346441962): move click detection logic into the decor's
                 //  {@link AppHeaderViewHolder}. Let it encapsulate the that and have it report
@@ -1161,7 +1170,8 @@
                         mMainChoreographer,
                         mSyncQueue,
                         mRootTaskDisplayAreaOrganizer,
-                        mGenericLinksParser);
+                        mGenericLinksParser,
+                        mMultiInstanceHelper);
         mWindowDecorByTaskId.put(taskInfo.taskId, windowDecoration);
 
         final DragPositioningCallback dragPositioningCallback;
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 df41d31..b888344 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
@@ -74,6 +74,7 @@
 import com.android.wm.shell.apptoweb.AppToWebGenericLinksParser;
 import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.common.DisplayLayout;
+import com.android.wm.shell.common.MultiInstanceHelper;
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.SyncTransactionQueue;
 import com.android.wm.shell.shared.annotations.ShellBackgroundThread;
@@ -157,6 +158,7 @@
     // to cancel the close.
     private final Runnable mCloseMaximizeWindowRunnable = this::closeMaximizeMenu;
     private final Runnable mCapturedLinkExpiredRunnable = this::onCapturedLinkExpired;
+    private final MultiInstanceHelper mMultiInstanceHelper;
 
     DesktopModeWindowDecoration(
             Context context,
@@ -171,13 +173,15 @@
             Choreographer choreographer,
             SyncTransactionQueue syncQueue,
             RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
-            AppToWebGenericLinksParser genericLinksParser) {
+            AppToWebGenericLinksParser genericLinksParser,
+            MultiInstanceHelper multiInstanceHelper) {
         this (context, userContext, displayController, splitScreenController, taskOrganizer,
                 taskInfo, taskSurface, handler, bgExecutor, choreographer, syncQueue,
                 rootTaskDisplayAreaOrganizer, genericLinksParser, SurfaceControl.Builder::new,
                 SurfaceControl.Transaction::new,  WindowContainerTransaction::new,
                 SurfaceControl::new, new SurfaceControlViewHostFactory() {},
-                DefaultMaximizeMenuFactory.INSTANCE, DefaultHandleMenuFactory.INSTANCE);
+                DefaultMaximizeMenuFactory.INSTANCE, DefaultHandleMenuFactory.INSTANCE,
+                multiInstanceHelper);
     }
 
     DesktopModeWindowDecoration(
@@ -200,7 +204,8 @@
             Supplier<SurfaceControl> surfaceControlSupplier,
             SurfaceControlViewHostFactory surfaceControlViewHostFactory,
             MaximizeMenuFactory maximizeMenuFactory,
-            HandleMenuFactory handleMenuFactory) {
+            HandleMenuFactory handleMenuFactory,
+            MultiInstanceHelper multiInstanceHelper) {
         super(context, userContext, displayController, taskOrganizer, taskInfo, taskSurface,
                 surfaceControlBuilderSupplier, surfaceControlTransactionSupplier,
                 windowContainerTransactionSupplier, surfaceControlSupplier,
@@ -214,6 +219,7 @@
         mGenericLinksParser = genericLinksParser;
         mMaximizeMenuFactory = maximizeMenuFactory;
         mHandleMenuFactory = handleMenuFactory;
+        mMultiInstanceHelper = multiInstanceHelper;
     }
 
     /**
@@ -966,6 +972,9 @@
                 mDisplayController,
                 splitScreenController,
                 DesktopModeStatus.canEnterDesktopMode(mContext),
+                Flags.enableDesktopWindowingMultiInstanceFeatures()
+                        && mMultiInstanceHelper
+                        .supportsMultiInstanceSplit(mTaskInfo.baseActivity),
                 getBrowserLink(),
                 mResult.mCaptionWidth,
                 mResult.mCaptionHeight,
@@ -1274,7 +1283,8 @@
                 Choreographer choreographer,
                 SyncTransactionQueue syncQueue,
                 RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
-                AppToWebGenericLinksParser genericLinksParser) {
+                AppToWebGenericLinksParser genericLinksParser,
+                MultiInstanceHelper multiInstanceHelper) {
             return new DesktopModeWindowDecoration(
                     context,
                     userContext,
@@ -1288,7 +1298,8 @@
                     choreographer,
                     syncQueue,
                     rootTaskDisplayAreaOrganizer,
-                    genericLinksParser);
+                    genericLinksParser,
+                    multiInstanceHelper);
         }
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt
index 7a81d4c..74a6407 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt
@@ -70,6 +70,7 @@
     private val displayController: DisplayController,
     private val splitScreenController: SplitScreenController,
     private val shouldShowWindowingPill: Boolean,
+    private val shouldShowNewWindowButton: Boolean,
     val openInBrowserLink: Uri?,
     private val captionWidth: Int,
     private val captionHeight: Int,
@@ -245,13 +246,24 @@
      * Set up interactive elements & height of handle menu's more actions pill
      */
     private fun setupMoreActionsPill(handleMenu: View, style: MenuStyle) {
-        val pill = handleMenu.requireViewById<View>(R.id.more_actions_pill).apply {
-            isGone = !SHOULD_SHOW_MORE_ACTIONS_PILL
-            background.colorFilter = BlendModeColorFilter(style.backgroundColor, BlendMode.MULTIPLY)
-        }
-        pill.requireViewById<Button>(R.id.screenshot_button).let { screenshotBtn ->
-            screenshotBtn.setTextColor(style.textColor)
-            screenshotBtn.compoundDrawableTintList = ColorStateList.valueOf(style.textColor)
+        val moreActionsPill = handleMenu.findViewById<View>(R.id.more_actions_pill)
+        moreActionsPill.isGone = !shouldShowNewWindowButton && !SHOULD_SHOW_SCREENSHOT_BUTTON
+        if (!moreActionsPill.isGone) {
+            handleMenu.requireViewById<Button>(R.id.screenshot_button).apply {
+                isGone = !SHOULD_SHOW_SCREENSHOT_BUTTON
+                background.colorFilter =
+                    BlendModeColorFilter(style.backgroundColor, BlendMode.MULTIPLY)
+                setTextColor(style.textColor)
+                compoundDrawableTintList = ColorStateList.valueOf(style.textColor)
+            }
+            handleMenu.findViewById<Button>(R.id.new_window_button).apply {
+                isGone = !shouldShowNewWindowButton
+                background.colorFilter =
+                    BlendModeColorFilter(style.backgroundColor, BlendMode.MULTIPLY)
+                setTextColor(style.textColor)
+                compoundDrawableTintList = ColorStateList.valueOf(style.textColor)
+                if (!isGone) setOnClickListener(onClickListener)
+            }
         }
     }
 
@@ -439,9 +451,17 @@
                 R.dimen.desktop_mode_handle_menu_windowing_pill_height)
             menuHeight -= pillTopMargin
         }
-        if (!SHOULD_SHOW_MORE_ACTIONS_PILL) {
+        if (!SHOULD_SHOW_SCREENSHOT_BUTTON) {
             menuHeight -= loadDimensionPixelSize(
-                R.dimen.desktop_mode_handle_menu_more_actions_pill_height)
+                R.dimen.desktop_mode_handle_menu_screenshot_height
+            )
+        }
+        if (!shouldShowNewWindowButton) {
+            menuHeight -= loadDimensionPixelSize(
+                R.dimen.desktop_mode_handle_menu_new_window_height
+            )
+        }
+        if (!SHOULD_SHOW_SCREENSHOT_BUTTON && !shouldShowNewWindowButton) {
             menuHeight -= pillTopMargin
         }
         if (!shouldShowBrowserPill) {
@@ -501,7 +521,7 @@
 
     companion object {
         private const val TAG = "HandleMenu"
-        private const val SHOULD_SHOW_MORE_ACTIONS_PILL = false
+        private const val SHOULD_SHOW_SCREENSHOT_BUTTON = false
     }
 }
 
@@ -517,6 +537,7 @@
         displayController: DisplayController,
         splitScreenController: SplitScreenController,
         shouldShowWindowingPill: Boolean,
+        shouldShowNewWindowButton: Boolean,
         openInBrowserLink: Uri?,
         captionWidth: Int,
         captionHeight: Int,
@@ -536,6 +557,7 @@
         displayController: DisplayController,
         splitScreenController: SplitScreenController,
         shouldShowWindowingPill: Boolean,
+        shouldShowNewWindowButton: Boolean,
         openInBrowserLink: Uri?,
         captionWidth: Int,
         captionHeight: Int,
@@ -551,6 +573,7 @@
             displayController,
             splitScreenController,
             shouldShowWindowingPill,
+            shouldShowNewWindowButton,
             openInBrowserLink,
             captionWidth,
             captionHeight,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
index e264160..c3a0bd9 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
@@ -68,6 +68,7 @@
 import com.android.wm.shell.common.DisplayController
 import com.android.wm.shell.common.DisplayInsetsController
 import com.android.wm.shell.common.DisplayLayout
+import com.android.wm.shell.common.MultiInstanceHelper
 import com.android.wm.shell.common.ShellExecutor
 import com.android.wm.shell.common.SyncTransactionQueue
 import com.android.wm.shell.desktopmode.DesktopTasksController
@@ -145,6 +146,7 @@
     @Mock private lateinit var mockInteractionJankMonitor: InteractionJankMonitor
     @Mock private lateinit var mockGenericLinksParser: AppToWebGenericLinksParser
     private val bgExecutor = TestShellExecutor()
+    @Mock private lateinit var mockMultiInstanceHelper: MultiInstanceHelper
 
     private val transactionFactory = Supplier<SurfaceControl.Transaction> {
         SurfaceControl.Transaction()
@@ -176,6 +178,7 @@
                 mockTransitions,
                 Optional.of(mockDesktopTasksController),
                 mockGenericLinksParser,
+                mockMultiInstanceHelper,
                 mockDesktopModeWindowDecorFactory,
                 mockInputMonitorFactory,
                 transactionFactory,
@@ -659,8 +662,9 @@
     private fun setUpMockDecorationForTask(task: RunningTaskInfo): DesktopModeWindowDecoration {
         val decoration = mock(DesktopModeWindowDecoration::class.java)
         whenever(
-            mockDesktopModeWindowDecorFactory.create(any(), any(), any(), any(), any(), eq(task),
-                any(), any(), any(), any(), any(), any(), any())
+            mockDesktopModeWindowDecorFactory.create(
+                any(), any(), any(), any(), any(), eq(task), any(), any(), any(), any(), any(),
+                any(), any(), any())
         ).thenReturn(decoration)
         decoration.mTaskInfo = task
         whenever(decoration.isFocused).thenReturn(task.isFocused)
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 04b1eed..e60eb77 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
@@ -89,6 +89,7 @@
 import com.android.wm.shell.TestShellExecutor;
 import com.android.wm.shell.apptoweb.AppToWebGenericLinksParser;
 import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.MultiInstanceHelper;
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.SyncTransactionQueue;
 import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
@@ -174,6 +175,7 @@
     private HandleMenu mMockHandleMenu;
     @Mock
     private HandleMenuFactory mMockHandleMenuFactory;
+    private MultiInstanceHelper mMockMultiInstanceHelper;
     @Captor
     private ArgumentCaptor<Function1<Boolean, Unit>> mOnMaxMenuHoverChangeListener;
     @Captor
@@ -218,7 +220,8 @@
         doReturn(defaultDisplay).when(mMockDisplayController).getDisplay(Display.DEFAULT_DISPLAY);
         doReturn(mInsetsState).when(mMockDisplayController).getInsetsState(anyInt());
         doReturn(mMockHandleMenu).when(mMockHandleMenuFactory).create(any(), anyInt(), any(), any(),
-                any(), any(), any(), any(), anyBoolean(), any(), anyInt(), anyInt(), anyInt());
+                any(), any(), any(), any(), anyBoolean(), anyBoolean(), any(), anyInt(), anyInt(),
+                anyInt());
     }
 
     @After
@@ -775,7 +778,7 @@
 
     private void verifyHandleMenuCreated(@Nullable Uri uri) {
         verify(mMockHandleMenuFactory).create(any(), anyInt(), any(), any(), any(), any(), any(),
-                any(), anyBoolean(), eq(uri), anyInt(), anyInt(), anyInt());
+                any(), anyBoolean(), anyBoolean(), eq(uri), anyInt(), anyInt(), anyInt());
     }
 
     private void createMaximizeMenu(DesktopModeWindowDecoration decoration, MaximizeMenu menu) {
@@ -831,7 +834,8 @@
                 mMockChoreographer, mMockSyncQueue, mMockRootTaskDisplayAreaOrganizer,
                 mMockGenericLinksParser, SurfaceControl.Builder::new, mMockTransactionSupplier,
                 WindowContainerTransaction::new, SurfaceControl::new,
-                mMockSurfaceControlViewHostFactory, maximizeMenuFactory, mMockHandleMenuFactory);
+                mMockSurfaceControlViewHostFactory, maximizeMenuFactory, mMockHandleMenuFactory,
+                        mMockMultiInstanceHelper);
         windowDecor.setCaptionListeners(mMockTouchEventListener, mMockTouchEventListener,
                 mMockTouchEventListener, mMockTouchEventListener);
         windowDecor.setExclusionRegionListener(mMockExclusionRegionListener);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt
index ed43aa3..cca83dd 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt
@@ -227,11 +227,11 @@
             else -> error("Invalid windowing mode")
         }
         val handleMenu = HandleMenu(mockDesktopWindowDecoration, layoutId,
-                onClickListener, onTouchListener, appIcon, appName, displayController,
-                splitScreenController, shouldShowWindowingPill = true,
-                null /* openInBrowserLink */, captionWidth = HANDLE_WIDTH, captionHeight = 50,
-                captionX = captionX
-        )
+            onClickListener, onTouchListener, appIcon, appName, displayController,
+            splitScreenController, shouldShowWindowingPill = true,
+            shouldShowNewWindowButton = true,
+            openInBrowserLink = null, captionWidth = HANDLE_WIDTH, captionHeight = 50,
+            captionX = captionX)
         handleMenu.show()
         return handleMenu
     }
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/EdgeToEdgeUtils.java b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/EdgeToEdgeUtils.java
index 6e53012..062e9b8 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/EdgeToEdgeUtils.java
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/EdgeToEdgeUtils.java
@@ -36,7 +36,7 @@
      * Enable Edge to Edge and handle overlaps using insets. It should be called before
      * setContentView.
      */
-    static void enable(@NonNull ComponentActivity activity) {
+    public static void enable(@NonNull ComponentActivity activity) {
         if (Build.VERSION.SDK_INT < Build.VERSION_CODES.VANILLA_ICE_CREAM) {
             return;
         }
diff --git a/packages/SystemUI/aconfig/biometrics_framework.aconfig b/packages/SystemUI/aconfig/biometrics_framework.aconfig
index bd1a442..e81d5d5 100644
--- a/packages/SystemUI/aconfig/biometrics_framework.aconfig
+++ b/packages/SystemUI/aconfig/biometrics_framework.aconfig
@@ -4,13 +4,6 @@
 # NOTE: Keep alphabetized to help limit merge conflicts from multiple simultaneous editors.
 
 flag {
-    name: "bp_talkback"
-    namespace: "biometrics_framework"
-    description: "Adds talkback directional guidance when using UDFPS with biometric prompt"
-    bug: "310044658"
-}
-
-flag {
     name: "constraint_bp"
     namespace: "biometrics_framework"
     description: "Refactors Biometric Prompt to use a ConstraintLayout"
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
index 34fbcac..40fb769 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
@@ -60,7 +60,6 @@
 import com.android.systemui.res.R;
 import com.android.systemui.scene.FakeWindowRootViewComponent;
 import com.android.systemui.settings.UserTracker;
-import com.android.systemui.shade.ui.viewmodel.NotificationShadeWindowModel;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
 import com.android.systemui.statusbar.phone.DozeParameters;
@@ -107,7 +106,6 @@
     @Mock private ShadeWindowLogger mShadeWindowLogger;
     @Mock private SelectedUserInteractor mSelectedUserInteractor;
     @Mock private UserTracker mUserTracker;
-    @Mock private NotificationShadeWindowModel mNotificationShadeWindowModel;
     @Captor private ArgumentCaptor<WindowManager.LayoutParams> mLayoutParameters;
     @Captor private ArgumentCaptor<StatusBarStateController.StateListener> mStateListener;
 
@@ -163,7 +161,7 @@
                 mShadeWindowLogger,
                 () -> mSelectedUserInteractor,
                 mUserTracker,
-                mNotificationShadeWindowModel,
+                mKosmos.getNotificationShadeWindowModel(),
                 mKosmos::getCommunalInteractor) {
                     @Override
                     protected boolean isDebuggable() {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
index 0352da3..9521be1 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
@@ -412,7 +412,6 @@
         });
 
         setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
-        setFocusableInTouchMode(true);
         requestFocus();
     }
 
@@ -464,7 +463,8 @@
         }
     }
 
-    private void onBackInvoked() {
+    @VisibleForTesting
+    public void onBackInvoked() {
         sendEarlyUserCanceled();
         animateAway(AuthDialogCallback.DISMISSED_USER_CANCELED);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
index b1cba2f..874ad9e 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
@@ -36,7 +36,6 @@
 import android.view.HapticFeedbackConstants
 import android.view.MotionEvent
 import com.android.launcher3.icons.IconProvider
-import com.android.systemui.Flags.bpTalkback
 import com.android.systemui.Flags.constraintBp
 import com.android.systemui.biometrics.UdfpsUtils
 import com.android.systemui.biometrics.Utils
@@ -916,10 +915,9 @@
         touchExplorationEnabled: Boolean,
     ): Boolean {
         if (
-            bpTalkback() &&
-                modalities.first().hasUdfps &&
-                touchExplorationEnabled &&
-                !isAuthenticated.first().isAuthenticated
+            modalities.first().hasUdfps &&
+            touchExplorationEnabled &&
+            !isAuthenticated.first().isAuthenticated
         ) {
             // TODO(b/315184924): Remove uses of UdfpsUtils
             val scaledTouch =
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
index 6ed84e5..89fce4a 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
@@ -293,11 +293,11 @@
         if (shouldUseFloatBrightness()) {
             mDozeService.setDozeScreenBrightnessFloat(
                     clampToDimBrightnessForScreenOffFloat(
-                            clampToUserSettingFloat(mDefaultDozeBrightnessFloat)));
+                            clampToUserSettingOrAutoBrightnessFloat(mDefaultDozeBrightnessFloat)));
         } else {
             mDozeService.setDozeScreenBrightness(
                     clampToDimBrightnessForScreenOff(
-                            clampToUserSetting(mDefaultDozeBrightness)));
+                            clampToUserSettingOrAutoBrightness(mDefaultDozeBrightness)));
         }
         mDozeHost.setAodDimmingScrim(0f);
     }
@@ -310,10 +310,7 @@
             return brightness;
         }
 
-        int userSetting = mSystemSettings.getIntForUser(
-                Settings.System.SCREEN_BRIGHTNESS, Integer.MAX_VALUE,
-                UserHandle.USER_CURRENT);
-        return Math.min(brightness, userSetting);
+        return Math.min(brightness, getScreenBrightness());
     }
 
     @SuppressLint("AndroidFrameworkRequiresPermission")
@@ -325,8 +322,33 @@
             return brightness;
         }
 
-        float userSetting = mDisplayManager.getBrightness(Display.DEFAULT_DISPLAY);
-        return Math.min(brightness, userSetting);
+        return Math.min(brightness, getScreenBrightnessFloat());
+    }
+
+    private int clampToUserSettingOrAutoBrightness(int brightness) {
+        return Math.min(brightness, getScreenBrightness());
+    }
+
+    private float clampToUserSettingOrAutoBrightnessFloat(float brightness) {
+        return Math.min(brightness, getScreenBrightnessFloat());
+    }
+
+    /**
+     * Gets the current screen brightness that may have been set by manually by the user
+     * or by autobrightness.
+     */
+    private int getScreenBrightness() {
+        return mSystemSettings.getIntForUser(
+                Settings.System.SCREEN_BRIGHTNESS, Integer.MAX_VALUE,
+                UserHandle.USER_CURRENT);
+    }
+
+    /**
+     * Gets the current screen brightness that may have been set by manually by the user
+     * or by autobrightness.
+     */
+    private float getScreenBrightnessFloat() {
+        return mDisplayManager.getBrightness(Display.DEFAULT_DISPLAY);
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/policy/WorkProfilePolicy.kt b/packages/SystemUI/src/com/android/systemui/screenshot/policy/WorkProfilePolicy.kt
index 29450a2..3fe3162 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/policy/WorkProfilePolicy.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/policy/WorkProfilePolicy.kt
@@ -26,7 +26,7 @@
 import com.android.systemui.screenshot.policy.CapturePolicy.PolicyResult
 import com.android.systemui.screenshot.policy.CapturePolicy.PolicyResult.NotMatched
 import com.android.systemui.screenshot.policy.CaptureType.IsolatedTask
-import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
+import com.android.wm.shell.shared.desktopmode.DesktopModeFlags
 import javax.inject.Inject
 import kotlinx.coroutines.flow.first
 
@@ -48,7 +48,7 @@
             return NotMatched(policy = NAME, reason = SHADE_EXPANDED)
         }
 
-        if (DesktopModeStatus.canEnterDesktopMode(context)) {
+        if (DesktopModeFlags.DESKTOP_WINDOWING_MODE.isEnabled(context)) {
             content.rootTasks.firstOrNull()?.also {
                 if (it.windowingMode == WINDOWING_MODE_FREEFORM) {
                     return NotMatched(policy = NAME, reason = DESKTOP_MODE_ENABLED)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
index 1e3ee28..dc69cda 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
@@ -35,7 +35,6 @@
 import android.testing.TestableLooper
 import android.testing.TestableLooper.RunWithLooper
 import android.testing.ViewUtils
-import android.view.KeyEvent
 import android.view.View
 import android.view.WindowInsets
 import android.view.WindowManager
@@ -202,8 +201,7 @@
         val root = container.rootView
 
         // Simulate back invocation
-        container.dispatchKeyEvent(KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK))
-        container.dispatchKeyEvent(KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK))
+        container.onBackInvoked()
         waitForIdleSync()
 
         assertThat(container.parent).isNull()
@@ -217,8 +215,7 @@
         val root = container.rootView
 
         // Simulate back invocation
-        container.dispatchKeyEvent(KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK))
-        container.dispatchKeyEvent(KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK))
+        container.onBackInvoked()
         waitForIdleSync()
 
         assertThat(container.parent).isNull()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
index e603db4..b8b93c9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
@@ -44,7 +44,6 @@
 import android.view.Surface
 import androidx.test.filters.SmallTest
 import com.android.app.activityTaskManager
-import com.android.systemui.Flags.FLAG_BP_TALKBACK
 import com.android.systemui.Flags.FLAG_CONSTRAINT_BP
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.biometrics.AuthController
@@ -1356,7 +1355,6 @@
     }
 
     @Test
-    @EnableFlags(FLAG_BP_TALKBACK)
     fun hint_for_talkback_guidance() = runGenericTest {
         val hint by collectLastValue(kosmos.promptViewModel.accessibilityHint)
 
@@ -1379,7 +1377,6 @@
     }
 
     @Test
-    @EnableFlags(FLAG_BP_TALKBACK)
     fun no_hint_for_talkback_guidance_after_auth() = runGenericTest {
         val hint by collectLastValue(kosmos.promptViewModel.accessibilityHint)
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
index 4818119..86da203 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
@@ -58,7 +58,6 @@
 import com.android.internal.display.BrightnessSynchronizer;
 import com.android.server.display.feature.flags.Flags;
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.dock.DockManager;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
 import com.android.systemui.statusbar.phone.DozeParameters;
 import com.android.systemui.statusbar.policy.DevicePostureController;
@@ -105,8 +104,6 @@
     @Mock
     DozeParameters mDozeParameters;
     @Mock
-    DockManager mDockManager;
-    @Mock
     DevicePostureController mDevicePostureController;
     @Mock
     DozeLog mDozeLog;
@@ -114,8 +111,8 @@
     SystemSettings mSystemSettings;
     @Mock
     DisplayManager mDisplayManager;
-    private FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock());
-    private FakeThreadFactory mFakeThreadFactory = new FakeThreadFactory(mFakeExecutor);
+    private final FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock());
+    private final FakeThreadFactory mFakeThreadFactory = new FakeThreadFactory(mFakeExecutor);
 
     private DozeScreenBrightness mScreen;
 
@@ -249,32 +246,35 @@
     }
 
     @Test
-    @RequiresFlagsDisabled(Flags.FLAG_DOZE_BRIGHTNESS_FLOAT)
-    public void testAod_usesLightSensorNotClampingToAutoBrightnessValue_Int() {
-        int maxBrightness = 3;
-        when(mSystemSettings.getIntForUser(eq(Settings.System.SCREEN_BRIGHTNESS), anyInt(),
-                eq(UserHandle.USER_CURRENT))).thenReturn(maxBrightness);
+    @RequiresFlagsEnabled(Flags.FLAG_DOZE_BRIGHTNESS_FLOAT)
+    public void initialBrightness_clampsToAutoBrightnessValue_Float() {
+        float maxBrightnessFromAutoBrightness = DEFAULT_BRIGHTNESS_FLOAT / 2;
+        when(mDisplayManager.getBrightness(Display.DEFAULT_DISPLAY)).thenReturn(
+                maxBrightnessFromAutoBrightness
+        );
         when(mSystemSettings.getIntForUser(eq(Settings.System.SCREEN_BRIGHTNESS_MODE), anyInt(),
                 eq(UserHandle.USER_CURRENT)))
                 .thenReturn(Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
 
         mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
-        assertEquals(DEFAULT_BRIGHTNESS_INT, mServiceFake.screenBrightnessInt);
-        assertTrue(Float.isNaN(mServiceFake.screenBrightnessFloat));
+        assertEquals(maxBrightnessFromAutoBrightness, mServiceFake.screenBrightnessFloat,
+                DELTA);
+        assertEquals(PowerManager.BRIGHTNESS_DEFAULT, mServiceFake.screenBrightnessInt);
     }
 
     @Test
-    @RequiresFlagsEnabled(Flags.FLAG_DOZE_BRIGHTNESS_FLOAT)
-    public void testAod_usesLightSensorNotClampingToAutoBrightnessValue_Float() {
-        float maxBrightness = DEFAULT_BRIGHTNESS_FLOAT / 2;
-        when(mDisplayManager.getBrightness(Display.DEFAULT_DISPLAY)).thenReturn(maxBrightness);
+    @RequiresFlagsDisabled(Flags.FLAG_DOZE_BRIGHTNESS_FLOAT)
+    public void initialBrightness_clampsToAutoBrightnessValue_Int() {
+        int maxBrightnessFromAutoBrightness = DEFAULT_BRIGHTNESS_INT / 2;
+        when(mSystemSettings.getIntForUser(eq(Settings.System.SCREEN_BRIGHTNESS), anyInt(),
+                eq(UserHandle.USER_CURRENT))).thenReturn(maxBrightnessFromAutoBrightness);
         when(mSystemSettings.getIntForUser(eq(Settings.System.SCREEN_BRIGHTNESS_MODE), anyInt(),
                 eq(UserHandle.USER_CURRENT)))
                 .thenReturn(Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
 
         mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
-        assertEquals(DEFAULT_BRIGHTNESS_FLOAT, mServiceFake.screenBrightnessFloat, DELTA);
-        assertEquals(PowerManager.BRIGHTNESS_DEFAULT, mServiceFake.screenBrightnessInt);
+        assertEquals(maxBrightnessFromAutoBrightness, mServiceFake.screenBrightnessInt);
+        assertTrue(Float.isNaN(mServiceFake.screenBrightnessFloat));
     }
 
     @Test
@@ -378,6 +378,54 @@
     }
 
     @Test
+    @RequiresFlagsEnabled(Flags.FLAG_DOZE_BRIGHTNESS_FLOAT)
+    public void lightSensorChangesInAod_doesNotClampToAutoBrightnessValue_Float() {
+        // GIVEN auto brightness reports low brightness
+        float maxBrightnessFromAutoBrightness = DEFAULT_BRIGHTNESS_FLOAT / 2;
+        when(mDisplayManager.getBrightness(Display.DEFAULT_DISPLAY))
+                .thenReturn(maxBrightnessFromAutoBrightness);
+        when(mSystemSettings.getIntForUser(eq(Settings.System.SCREEN_BRIGHTNESS_MODE), anyInt(),
+                eq(UserHandle.USER_CURRENT)))
+                .thenReturn(Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
+
+        // GIVEN the device is DOZE_AOD and the display state changes to ON
+        mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
+        mScreen.transitionTo(INITIALIZED, DOZE_AOD);
+        waitForSensorManager();
+
+        // WHEN new sensor event sent
+        mSensor.sendSensorEvent(3);
+
+        // THEN brightness is updated
+        assertEquals(SENSOR_TO_BRIGHTNESS_FLOAT[3], mServiceFake.screenBrightnessFloat, DELTA);
+        assertEquals(PowerManager.BRIGHTNESS_DEFAULT, mServiceFake.screenBrightnessInt);
+    }
+
+    @Test
+    @RequiresFlagsDisabled(Flags.FLAG_DOZE_BRIGHTNESS_FLOAT)
+    public void lightSensorChangesInAod_doesNotClampToAutoBrightnessValue_Int() {
+        // GIVEN auto brightness reports low brightness
+        int maxBrightnessFromAutoBrightness = 1;
+        when(mSystemSettings.getIntForUser(eq(Settings.System.SCREEN_BRIGHTNESS), anyInt(),
+                eq(UserHandle.USER_CURRENT))).thenReturn(maxBrightnessFromAutoBrightness);
+        when(mSystemSettings.getIntForUser(eq(Settings.System.SCREEN_BRIGHTNESS_MODE), anyInt(),
+                eq(UserHandle.USER_CURRENT)))
+                .thenReturn(Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
+
+        // GIVEN the device is DOZE_AOD and the display state changes to ON
+        mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
+        mScreen.transitionTo(INITIALIZED, DOZE_AOD);
+        waitForSensorManager();
+
+        // WHEN new sensor event sent
+        mSensor.sendSensorEvent(3);
+
+        // THEN brightness is updated
+        assertEquals(SENSOR_TO_BRIGHTNESS_INT[3], mServiceFake.screenBrightnessInt);
+        assertTrue(Float.isNaN(mServiceFake.screenBrightnessFloat));
+    }
+
+    @Test
     @RequiresFlagsDisabled(Flags.FLAG_DOZE_BRIGHTNESS_FLOAT)
     public void docked_usesLightSensor_Int() {
         // GIVEN the device is docked and the display state changes to ON
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 ff65887..e3a38a8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -111,7 +111,6 @@
 import com.android.systemui.shade.ShadeExpansionStateManager;
 import com.android.systemui.shade.ShadeWindowLogger;
 import com.android.systemui.shade.domain.interactor.ShadeInteractor;
-import com.android.systemui.shade.ui.viewmodel.NotificationShadeWindowModel;
 import com.android.systemui.statusbar.NotificationShadeDepthController;
 import com.android.systemui.statusbar.NotificationShadeWindowController;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
@@ -226,7 +225,6 @@
     private @Mock DreamViewModel mDreamViewModel;
     private @Mock CommunalTransitionViewModel mCommunalTransitionViewModel;
     private @Mock SystemPropertiesHelper mSystemPropertiesHelper;
-    @Mock private NotificationShadeWindowModel mNotificationShadeWindowModel;
 
     private FakeFeatureFlags mFeatureFlags;
     private final int mDefaultUserId = 100;
@@ -274,7 +272,7 @@
                 mShadeWindowLogger,
                 () -> mSelectedUserInteractor,
                 mUserTracker,
-                mNotificationShadeWindowModel,
+                mKosmos.getNotificationShadeWindowModel(),
                 mKosmos::getCommunalInteractor);
         mFeatureFlags = new FakeFeatureFlags();
         mSetFlagsRule.enableFlags(FLAG_REFACTOR_GET_CURRENT_USER);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/policy/WorkProfilePolicyTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/policy/WorkProfilePolicyTest.kt
index 191a048..a5fbfb5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/policy/WorkProfilePolicyTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/policy/WorkProfilePolicyTest.kt
@@ -18,13 +18,11 @@
 
 import android.content.ComponentName
 import android.content.Context
-import android.content.res.Resources
 import android.os.UserHandle
 import android.platform.test.annotations.DisableFlags
 import android.platform.test.annotations.EnableFlags
 import android.platform.test.flag.junit.SetFlagsRule
 import androidx.test.ext.junit.runners.AndroidJUnit4
-import com.android.internal.R
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.screenshot.data.model.DisplayContentModel
 import com.android.systemui.screenshot.data.model.DisplayContentScenarios.ActivityNames.FILES
@@ -49,7 +47,6 @@
 import com.android.systemui.screenshot.policy.WorkProfilePolicy.Companion.SHADE_EXPANDED
 import com.android.systemui.screenshot.policy.WorkProfilePolicy.Companion.WORK_TASK_IS_TOP
 import com.android.systemui.screenshot.policy.WorkProfilePolicy.Companion.WORK_TASK_NOT_TOP
-import com.android.systemui.util.mockito.whenever
 import com.android.window.flags.Flags
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.test.runTest
@@ -58,7 +55,6 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mock
-import org.mockito.MockitoAnnotations
 import org.mockito.junit.MockitoJUnit
 import org.mockito.junit.MockitoRule
 
@@ -70,19 +66,12 @@
     @JvmField @Rule(order = 2) val mockitoRule: MockitoRule = MockitoJUnit.rule()
 
     @Mock lateinit var mContext: Context
-    @Mock lateinit var mResources: Resources
 
     private val kosmos = Kosmos()
     private lateinit var policy: WorkProfilePolicy
 
     @Before
     fun setUp() {
-        MockitoAnnotations.initMocks(this)
-
-        // Set desktop mode supported
-        whenever(mContext.resources).thenReturn(mResources)
-        whenever(mResources.getBoolean(R.bool.config_isDesktopModeSupported)).thenReturn(true)
-
         policy = WorkProfilePolicy(kosmos.profileTypeRepository, mContext)
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index 7f964d1..e5e04dc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -349,10 +349,10 @@
     private Display mDefaultDisplay;
     @Mock
     private Lazy<ViewCapture> mLazyViewCapture;
-    @Mock private NotificationShadeWindowModel mNotificationShadeWindowModel;
 
     private final KosmosJavaAdapter mKosmos = new KosmosJavaAdapter(this);
     private ShadeInteractor mShadeInteractor;
+    private NotificationShadeWindowModel mNotificationShadeWindowModel;
     private ShellTaskOrganizer mShellTaskOrganizer;
     private TaskViewTransitions mTaskViewTransitions;
 
@@ -411,6 +411,7 @@
         when(deviceEntryUdfpsInteractor.isUdfpsSupported()).thenReturn(MutableStateFlow(false));
 
         mShadeInteractor = mKosmos.getShadeInteractor();
+        mNotificationShadeWindowModel = mKosmos.getNotificationShadeWindowModel();
 
         mNotificationShadeWindowController = new NotificationShadeWindowControllerImpl(
                 mContext,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
index 8614fc9..e6bd24b 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
@@ -59,6 +59,7 @@
 import com.android.systemui.shade.data.repository.shadeRepository
 import com.android.systemui.shade.domain.interactor.shadeInteractor
 import com.android.systemui.shade.shadeController
+import com.android.systemui.shade.ui.viewmodel.notificationShadeWindowModel
 import com.android.systemui.statusbar.chips.ui.viewmodel.ongoingActivityChipsViewModel
 import com.android.systemui.statusbar.notification.domain.interactor.seenNotificationsInteractor
 import com.android.systemui.statusbar.notification.stack.domain.interactor.headsUpNotificationInteractor
@@ -140,6 +141,7 @@
     val shadeController by lazy { kosmos.shadeController }
     val shadeRepository by lazy { kosmos.shadeRepository }
     val shadeInteractor by lazy { kosmos.shadeInteractor }
+    val notificationShadeWindowModel by lazy { kosmos.notificationShadeWindowModel }
     val wifiInteractor by lazy { kosmos.wifiInteractor }
     val fakeWifiRepository by lazy { kosmos.fakeWifiRepository }
     val volumeDialogInteractor by lazy { kosmos.volumeDialogInteractor }
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 03fbfd37..f936b81 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -620,6 +620,10 @@
 
     private void setPowerStatsThrottlePeriods(BatteryStatsImpl.BatteryStatsConfig.Builder builder,
             String configString) {
+        if (configString == null) {
+            return;
+        }
+
         Matcher matcher = Pattern.compile("([^:]+):(\\d+)\\s*").matcher(configString);
         while (matcher.find()) {
             String powerComponentName = matcher.group(1);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
index ef7abdd..6cce722 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
@@ -227,6 +227,9 @@
                 onError(BiometricFaceConstants.FACE_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */);
                 mCallback.onClientFinished(this, false /* success */);
             }
+        } else {
+            Slog.e(TAG, "Cancellation signal is null");
+            mCallback.onClientFinished(this, false /* success */);
         }
     }
 
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
index d04afdb..dee4b4f 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
@@ -359,7 +359,8 @@
                 mCallback.onClientFinished(this, false /* success */);
             }
         } else {
-            Slog.e(TAG, "cancellation signal was null");
+            Slog.e(TAG, "Cancellation signal was null");
+            mCallback.onClientFinished(this, false /* success */);
         }
     }
 
diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java
index 12c3197..59fffe7 100644
--- a/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java
+++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java
@@ -347,7 +347,7 @@
                         data.mDisplayDeviceConfig));
             }
             if (flags.useNewHdrBrightnessModifier()) {
-                modifiers.add(new HdrBrightnessModifier(handler, listener, data));
+                modifiers.add(new HdrBrightnessModifier(handler, context, listener, data));
             }
             return modifiers;
         }
diff --git a/services/core/java/com/android/server/display/brightness/clamper/HdrBrightnessModifier.java b/services/core/java/com/android/server/display/brightness/clamper/HdrBrightnessModifier.java
index ae1801c..4ab4336 100644
--- a/services/core/java/com/android/server/display/brightness/clamper/HdrBrightnessModifier.java
+++ b/services/core/java/com/android/server/display/brightness/clamper/HdrBrightnessModifier.java
@@ -21,10 +21,15 @@
 
 import android.annotation.Nullable;
 import android.annotation.SuppressLint;
+import android.content.Context;
+import android.database.ContentObserver;
 import android.hardware.display.DisplayManagerInternal;
+import android.net.Uri;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.PowerManager;
+import android.os.UserHandle;
+import android.provider.Settings;
 import android.view.SurfaceControlHdrLayerInfoListener;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -44,6 +49,11 @@
     static final float DEFAULT_MAX_HDR_SDR_RATIO = 1.0f;
     private static final float DEFAULT_HDR_LAYER_SIZE = -1.0f;
 
+    private final Uri mLowPowerModeSetting = Settings.Global.getUriFor(
+            Settings.Global.LOW_POWER_MODE);
+
+    private final ContentObserver mContentObserver;
+
     private final SurfaceControlHdrLayerInfoListener mHdrListener =
             new SurfaceControlHdrLayerInfoListener() {
                 @Override
@@ -52,7 +62,8 @@
                     boolean hdrLayerPresent = numberOfHdrLayers > 0;
                     mHandler.post(() -> HdrBrightnessModifier.this.onHdrInfoChanged(
                             hdrLayerPresent ? (float) (maxW * maxH) : DEFAULT_HDR_LAYER_SIZE,
-                            hdrLayerPresent ? maxDesiredHdrSdrRatio : DEFAULT_MAX_HDR_SDR_RATIO));
+                            hdrLayerPresent ? Math.max(maxDesiredHdrSdrRatio,
+                                    DEFAULT_MAX_HDR_SDR_RATIO) : DEFAULT_MAX_HDR_SDR_RATIO));
                 }
             };
 
@@ -62,6 +73,7 @@
     private final Runnable mDebouncer;
 
     private IBinder mRegisteredDisplayToken;
+    private boolean mContentObserverRegistered = false;
 
     private DisplayDeviceConfig mDisplayDeviceConfig;
     @Nullable
@@ -73,6 +85,8 @@
 
     private float mAmbientLux = INVALID_LUX;
 
+    private boolean mLowPowerMode = false;
+
     private Mode mMode = Mode.NO_HDR;
     // The maximum brightness allowed for current lux
     private float mMaxBrightness = PowerManager.BRIGHTNESS_MAX;
@@ -81,17 +95,17 @@
     private float mTransitionRate = CUSTOM_ANIMATION_RATE_NOT_SET;
     private float mPendingTransitionRate = CUSTOM_ANIMATION_RATE_NOT_SET;
 
-    HdrBrightnessModifier(Handler handler,
+    HdrBrightnessModifier(Handler handler, Context context,
             BrightnessClamperController.ClamperChangeListener clamperChangeListener,
             BrightnessClamperController.DisplayDeviceData displayData) {
-        this(new Handler(handler.getLooper()), clamperChangeListener, new Injector(), displayData);
+        this(new Handler(handler.getLooper()), clamperChangeListener,
+                new Injector(context), displayData);
     }
 
     @VisibleForTesting
     HdrBrightnessModifier(Handler handler,
             BrightnessClamperController.ClamperChangeListener clamperChangeListener,
-            Injector injector,
-            BrightnessClamperController.DisplayDeviceData displayData) {
+            Injector injector, BrightnessClamperController.DisplayDeviceData displayData) {
         mHandler = handler;
         mClamperChangeListener = clamperChangeListener;
         mInjector = injector;
@@ -100,6 +114,12 @@
             mMaxBrightness = mPendingMaxBrightness;
             mClamperChangeListener.onChanged();
         };
+        mContentObserver = new ContentObserver(mHandler) {
+            @Override
+            public void onChange(boolean selfChange) {
+                onLowPowerModeChange();
+            }
+        };
         mHandler.post(() -> onDisplayChanged(displayData));
     }
 
@@ -135,12 +155,14 @@
         pw.println("  mMaxDesiredHdrRatio=" + mMaxDesiredHdrRatio);
         pw.println("  mHdrLayerSize=" + mHdrLayerSize);
         pw.println("  mAmbientLux=" + mAmbientLux);
+        pw.println("  mLowPowerMode=" + mLowPowerMode);
         pw.println("  mMode=" + mMode);
         pw.println("  mMaxBrightness=" + mMaxBrightness);
         pw.println("  mPendingMaxBrightness=" + mPendingMaxBrightness);
         pw.println("  mTransitionRate=" + mTransitionRate);
         pw.println("  mPendingTransitionRate=" + mPendingTransitionRate);
         pw.println("  mHdrListener registered=" + (mRegisteredDisplayToken != null));
+        pw.println("  mContentObserverRegistered=" + mContentObserverRegistered);
     }
 
     // Called in DisplayControllerHandler
@@ -182,7 +204,25 @@
         } else {
             registerHdrListener(displayData.mDisplayToken);
         }
-        recalculate(data, mMaxDesiredHdrRatio);
+        if (data == null || data.allowInLowPowerMode) {
+            unregisterContentObserver();
+        } else {
+            registerContentObserver();
+        }
+
+        Mode newMode = recalculateMode(data);
+        // mode changed, or mode was HDR  and HdrBrightnessData changed
+        boolean needToNotifyChange = mMode != newMode
+                || (mMode != HdrBrightnessModifier.Mode.NO_HDR && data != mHdrBrightnessData);
+        mMode = newMode;
+        mHdrBrightnessData = data;
+        mMaxBrightness = findBrightnessLimit(mHdrBrightnessData, mAmbientLux);
+
+        if (needToNotifyChange) {
+            // data changed, reset custom transition rate
+            mTransitionRate = CUSTOM_ANIMATION_RATE_NOT_SET;
+            mClamperChangeListener.onChanged();
+        }
     }
 
     // Called in DisplayControllerHandler, when any modifier state changes
@@ -226,30 +266,6 @@
     }
 
     // Called in DisplayControllerHandler
-    private void recalculate(@Nullable HdrBrightnessData data, float maxDesiredHdrRatio) {
-        Mode newMode = recalculateMode(data);
-        // if HDR mode changed, notify changed
-        boolean needToNotifyChange = mMode != newMode;
-        // If HDR mode is active, we need to check if other HDR params are changed
-        if (mMode != HdrBrightnessModifier.Mode.NO_HDR) {
-            if (!BrightnessSynchronizer.floatEquals(mMaxDesiredHdrRatio, maxDesiredHdrRatio)
-                    || data != mHdrBrightnessData) {
-                needToNotifyChange = true;
-            }
-        }
-
-        mMode = newMode;
-        mHdrBrightnessData = data;
-        mMaxDesiredHdrRatio = maxDesiredHdrRatio;
-
-        if (needToNotifyChange) {
-            // data or hdr layer changed, reset custom transition rate
-            mTransitionRate = CUSTOM_ANIMATION_RATE_NOT_SET;
-            mClamperChangeListener.onChanged();
-        }
-    }
-
-    // Called in DisplayControllerHandler
     private Mode recalculateMode(@Nullable HdrBrightnessData data) {
         // no config
         if (data == null) {
@@ -259,6 +275,10 @@
         if (mHdrLayerSize == DEFAULT_HDR_LAYER_SIZE) {
             return Mode.NO_HDR;
         }
+        // low power mode and not allowed in low power mode
+        if (!data.allowInLowPowerMode && mLowPowerMode) {
+            return Mode.NO_HDR;
+        }
         // HDR layer < minHdr % for Nbm
         if (mHdrLayerSize < mScreenSize * data.minimumHdrPercentOfScreenForNbm) {
             return Mode.NO_HDR;
@@ -271,6 +291,16 @@
         return Mode.HBM_HDR;
     }
 
+    private void onLowPowerModeChange() {
+        mLowPowerMode = mInjector.isLowPowerMode();
+        Mode newMode = recalculateMode(mHdrBrightnessData);
+        if (newMode != mMode) {
+            mMode = newMode;
+            mTransitionRate = CUSTOM_ANIMATION_RATE_NOT_SET;
+            mClamperChangeListener.onChanged();
+        }
+    }
+
     private float getMaxBrightness(Mode mode, float maxBrightness, HdrBrightnessData data) {
         if (mode == Mode.NBM_HDR) {
             return Math.min(data.hbmTransitionPoint, maxBrightness);
@@ -282,7 +312,13 @@
     }
 
     // Called in DisplayControllerHandler
-    private float findBrightnessLimit(HdrBrightnessData data, float ambientLux) {
+    private float findBrightnessLimit(@Nullable HdrBrightnessData data, float ambientLux) {
+        if (data == null) {
+            return PowerManager.BRIGHTNESS_MAX;
+        }
+        if (ambientLux == INVALID_LUX) {
+            return PowerManager.BRIGHTNESS_MAX;
+        }
         float foundAmbientBoundary = Float.MAX_VALUE;
         float foundMaxBrightness = PowerManager.BRIGHTNESS_MAX;
         for (Map.Entry<Float, Float> brightnessPoint :
@@ -300,7 +336,17 @@
     // Called in DisplayControllerHandler
     private void onHdrInfoChanged(float hdrLayerSize, float maxDesiredHdrSdrRatio) {
         mHdrLayerSize = hdrLayerSize;
-        recalculate(mHdrBrightnessData, maxDesiredHdrSdrRatio);
+        Mode newMode = recalculateMode(mHdrBrightnessData);
+        // mode changed, or mode was HDR  and maxDesiredHdrRatio changed
+        boolean needToNotifyChange = mMode != newMode
+                || (mMode != HdrBrightnessModifier.Mode.NO_HDR
+                && !BrightnessSynchronizer.floatEquals(mMaxDesiredHdrRatio, maxDesiredHdrSdrRatio));
+        mMode = newMode;
+        mMaxDesiredHdrRatio = maxDesiredHdrSdrRatio;
+        if (needToNotifyChange) {
+            mTransitionRate = CUSTOM_ANIMATION_RATE_NOT_SET;
+            mClamperChangeListener.onChanged();
+        }
     }
 
     // Called in DisplayControllerHandler
@@ -324,12 +370,36 @@
         }
     }
 
+    // Called in DisplayControllerHandler
+    private void registerContentObserver() {
+        if (!mContentObserverRegistered) {
+            mInjector.registerContentObserver(mContentObserver, mLowPowerModeSetting);
+            mContentObserverRegistered = true;
+            mLowPowerMode = mInjector.isLowPowerMode();
+        }
+    }
+
+    // Called in DisplayControllerHandler
+    private void unregisterContentObserver() {
+        if (mContentObserverRegistered) {
+            mInjector.unregisterContentObserver(mContentObserver);
+            mContentObserverRegistered = false;
+            mLowPowerMode = false;
+        }
+    }
+
     private enum Mode {
         NO_HDR, NBM_HDR, HBM_HDR
     }
 
     @SuppressLint("MissingPermission")
     static class Injector {
+        private final Context mContext;
+
+        Injector(Context context) {
+            mContext = context;
+        }
+
         void registerHdrListener(SurfaceControlHdrLayerInfoListener listener, IBinder token) {
             listener.register(token);
         }
@@ -337,5 +407,19 @@
         void unregisterHdrListener(SurfaceControlHdrLayerInfoListener listener, IBinder token) {
             listener.unregister(token);
         }
+
+        void registerContentObserver(ContentObserver observer, Uri uri) {
+            mContext.getContentResolver().registerContentObserver(uri, false,
+                    observer, UserHandle.USER_ALL);
+        }
+
+        void unregisterContentObserver(ContentObserver observer) {
+            mContext.getContentResolver().unregisterContentObserver(observer);
+        }
+
+        boolean isLowPowerMode() {
+            return Settings.Global.getInt(
+                    mContext.getContentResolver(), Settings.Global.LOW_POWER_MODE, 0) != 0;
+        }
     }
 }
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 730077c..61d8e0b 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -619,7 +619,7 @@
             case Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD: {
                 if (!mNewInputMethodSwitcherMenuEnabled) {
                     if (userId == mCurrentUserId) {
-                        mMenuController.updateKeyboardFromSettingsLocked();
+                        mMenuController.updateKeyboardFromSettingsLocked(userId);
                     }
                 }
                 break;
@@ -693,7 +693,7 @@
                                 senderUserId);
                     }
                 } else {
-                    mMenuController.hideInputMethodMenu();
+                    mMenuController.hideInputMethodMenu(senderUserId);
                 }
             } else {
                 Slog.w(TAG, "Unexpected intent " + intent);
@@ -1222,12 +1222,6 @@
         }
     }
 
-    @GuardedBy("ImfLock.class")
-    @UserIdInt
-    int getCurrentImeUserIdLocked() {
-        return mCurrentUserId;
-    }
-
     private final class InkWindowInitializer implements Runnable {
         public void run() {
             synchronized (ImfLock.class) {
@@ -1239,12 +1233,11 @@
         }
     }
 
-    private void onUpdateEditorToolType(int toolType) {
-        synchronized (ImfLock.class) {
-            IInputMethodInvoker curMethod = getCurMethodLocked();
-            if (curMethod != null) {
-                curMethod.updateEditorToolType(toolType);
-            }
+    @GuardedBy("ImfLock.class")
+    private void onUpdateEditorToolTypeLocked(@MotionEvent.ToolType int toolType) {
+        final IInputMethodInvoker curMethod = getCurMethodLocked();
+        if (curMethod != null) {
+            curMethod.updateEditorToolType(toolType);
         }
     }
 
@@ -1826,7 +1819,7 @@
             if (mNewInputMethodSwitcherMenuEnabled) {
                 mMenuControllerNew.hide(bindingController.getCurTokenDisplayId(), userId);
             } else {
-                mMenuController.hideInputMethodMenuLocked();
+                mMenuController.hideInputMethodMenuLocked(userId);
             }
         }
     }
@@ -2860,7 +2853,7 @@
     void updateFromSettingsLocked(boolean enabledMayChange, @UserIdInt int userId) {
         updateInputMethodsFromSettingsLocked(enabledMayChange, userId);
         if (!mNewInputMethodSwitcherMenuEnabled) {
-            mMenuController.updateKeyboardFromSettingsLocked();
+            mMenuController.updateKeyboardFromSettingsLocked(userId);
         }
     }
 
@@ -3465,7 +3458,7 @@
             if (Flags.useHandwritingListenerForTooltype()) {
                 maybeReportToolType();
             } else if (lastClickToolType != MotionEvent.TOOL_TYPE_UNKNOWN) {
-                onUpdateEditorToolType(lastClickToolType);
+                onUpdateEditorToolTypeLocked(lastClickToolType);
             }
             mVisibilityApplier.performShowIme(windowToken, statsToken,
                     mVisibilityStateComputer.getShowFlagsForInputMethodServiceOnly(),
@@ -3499,7 +3492,7 @@
             // other toolTypes are irrelevant and reported as unknown.
             toolType = MotionEvent.TOOL_TYPE_UNKNOWN;
         }
-        onUpdateEditorToolType(toolType);
+        onUpdateEditorToolTypeLocked(toolType);
     }
 
     @Override
@@ -5071,7 +5064,7 @@
             mMenuControllerNew.show(menuItems, selectedIndex, displayId, userId);
         } else {
             mMenuController.showInputMethodMenuLocked(showAuxSubtypes, displayId,
-                    lastInputMethodId, lastInputMethodSubtypeId, imList);
+                    lastInputMethodId, lastInputMethodSubtypeId, imList, userId);
         }
     }
 
@@ -5948,7 +5941,7 @@
                         final var bindingController = getInputMethodBindingController(userId);
                         mMenuControllerNew.hide(bindingController.getCurTokenDisplayId(), userId);
                     } else {
-                        mMenuController.hideInputMethodMenuLocked();
+                        mMenuController.hideInputMethodMenuLocked(userId);
                     }
                 }
             }
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java b/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java
index ba5c13e..f16a5a0 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java
@@ -21,6 +21,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.UserIdInt;
 import android.app.AlertDialog;
 import android.content.Context;
 import android.content.DialogInterface;
@@ -77,13 +78,12 @@
     @GuardedBy("ImfLock.class")
     void showInputMethodMenuLocked(boolean showAuxSubtypes, int displayId,
             String preferredInputMethodId, int preferredInputMethodSubtypeId,
-            @NonNull List<ImeSubtypeListItem> imList) {
+            @NonNull List<ImeSubtypeListItem> imList, @UserIdInt int userId) {
         if (DEBUG) Slog.v(TAG, "Show switching menu. showAuxSubtypes=" + showAuxSubtypes);
 
-        final int userId = mService.getCurrentImeUserIdLocked();
         final var bindingController = mService.getInputMethodBindingController(userId);
 
-        hideInputMethodMenuLocked();
+        hideInputMethodMenuLocked(userId);
 
         if (preferredInputMethodSubtypeId == NOT_A_SUBTYPE_ID) {
             final InputMethodSubtype currentSubtype =
@@ -131,7 +131,7 @@
         }
         final Context dialogWindowContext = mDialogWindowContext.get(displayId);
         mDialogBuilder = new AlertDialog.Builder(dialogWindowContext);
-        mDialogBuilder.setOnCancelListener(dialog -> hideInputMethodMenu());
+        mDialogBuilder.setOnCancelListener(dialog -> hideInputMethodMenu(userId));
 
         final Context dialogContext = mDialogBuilder.getContext();
         final TypedArray a = dialogContext.obtainStyledAttributes(null,
@@ -162,7 +162,7 @@
                     isChecked, userId);
             // Ensure that the input method dialog is dismissed when changing
             // the hardware keyboard state.
-            hideInputMethodMenu();
+            hideInputMethodMenu(userId);
         });
 
         // Fill the list items with onClick listener, which takes care of IME (and subtype)
@@ -185,7 +185,7 @@
                     }
                     mService.setInputMethodLocked(im.getId(), subtypeId, userId);
                 }
-                hideInputMethodMenuLocked();
+                hideInputMethodMenuLocked(userId);
             }
         };
         mDialogBuilder.setSingleChoiceItems(adapter, checkedItem, choiceListener);
@@ -209,10 +209,10 @@
         mSwitchingDialog.show();
     }
 
-    void updateKeyboardFromSettingsLocked() {
+    void updateKeyboardFromSettingsLocked(@UserIdInt int userId) {
         mShowImeWithHardKeyboard =
                 SecureSettingsWrapper.getBoolean(Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD,
-                        false, mService.getCurrentImeUserIdLocked());
+                        false, userId);
         if (mSwitchingDialog != null && mSwitchingDialogTitleView != null
                 && mSwitchingDialog.isShowing()) {
             final Switch hardKeySwitch = mSwitchingDialogTitleView.findViewById(
@@ -223,18 +223,22 @@
 
     /**
      * Hides the input method switcher menu.
+     *
+     * @param userId user ID for this operation
      */
-    void hideInputMethodMenu() {
+    void hideInputMethodMenu(@UserIdInt int userId) {
         synchronized (ImfLock.class) {
-            hideInputMethodMenuLocked();
+            hideInputMethodMenuLocked(userId);
         }
     }
 
     /**
      * Hides the input method switcher menu, synchronised version of {@link #hideInputMethodMenu}.
+     *
+     * @param userId user ID for this operation
      */
     @GuardedBy("ImfLock.class")
-    void hideInputMethodMenuLocked() {
+    void hideInputMethodMenuLocked(@UserIdInt int userId) {
         if (DEBUG) Slog.v(TAG, "Hide switching menu");
 
         if (mSwitchingDialog != null) {
@@ -242,8 +246,6 @@
             mSwitchingDialog = null;
             mSwitchingDialogTitleView = null;
 
-            // TODO(b/305849394): Make InputMethodMenuController multi-user aware
-            final int userId = mService.getCurrentImeUserIdLocked();
             mService.updateSystemUiLocked(userId);
             mService.sendOnNavButtonFlagsChangedToAllImesLocked();
             mDialogBuilder = null;
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/HdrBrightnessModifierTest.kt b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/HdrBrightnessModifierTest.kt
index 0ed96ae..bb025cc 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/HdrBrightnessModifierTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/HdrBrightnessModifierTest.kt
@@ -16,7 +16,10 @@
 
 package com.android.server.display.brightness.clamper
 
+import android.content.Context
+import android.database.ContentObserver
 import android.hardware.display.DisplayManagerInternal
+import android.net.Uri
 import android.os.IBinder
 import android.os.PowerManager.BRIGHTNESS_MAX
 import android.util.Spline
@@ -51,7 +54,7 @@
 
     private val stoppedClock = OffsettableClock.Stopped()
     private val testHandler = TestHandler(null, stoppedClock)
-    private val testInjector = TestInjector()
+    private val testInjector = TestInjector(mock<Context>())
     private val mockChangeListener = mock<ClamperChangeListener>()
     private val mockDisplayDeviceConfig = mock<DisplayDeviceConfig>()
     private val mockDisplayBinder = mock<IBinder>()
@@ -63,14 +66,14 @@
     private val dummyData = createDisplayDeviceData(mockDisplayDeviceConfig, mockDisplayBinder)
 
     @Test
-    fun `change listener is not called on init`() {
+    fun changeListenerIsNotCalledOnInit() {
         initHdrModifier()
 
         verify(mockChangeListener, never()).onChanged()
     }
 
     @Test
-    fun `hdr listener registered on init if hdr data is present`() {
+    fun hdrListenerRegisteredOnInit_hdrDataPresent() {
         initHdrModifier()
 
         assertThat(testInjector.registeredHdrListener).isNotNull()
@@ -78,22 +81,19 @@
     }
 
     @Test
-    fun `hdr listener not registered on init if hdr data is missing`() {
-        initHdrModifier(null)
-
-        testHandler.flush()
+    fun hdrListenerNotRegisteredOnInit_hdrDataMissing() {
+        initHdrModifier(hdrBrightnessData = null)
 
         assertThat(testInjector.registeredHdrListener).isNull()
         assertThat(testInjector.registeredToken).isNull()
     }
 
     @Test
-    fun `unsubscribes hdr listener when display changed with no hdr data`() {
+    fun unsubscribeHdrListener_displayChangedWithNoHdrData() {
         initHdrModifier()
 
         whenever(mockDisplayDeviceConfig.hdrBrightnessData).thenReturn(null)
         modifier.onDisplayChanged(dummyData)
-        testHandler.flush()
 
         assertThat(testInjector.registeredHdrListener).isNull()
         assertThat(testInjector.registeredToken).isNull()
@@ -101,12 +101,11 @@
     }
 
     @Test
-    fun `resubscribes hdr listener when display changed with different token`() {
+    fun resubscribesHdrListener_displayChangedWithDifferentToken() {
         initHdrModifier()
 
         modifier.onDisplayChanged(
             createDisplayDeviceData(mockDisplayDeviceConfig, mockDisplayBinderOther))
-        testHandler.flush()
 
         assertThat(testInjector.registeredHdrListener).isNotNull()
         assertThat(testInjector.registeredToken).isEqualTo(mockDisplayBinderOther)
@@ -114,7 +113,28 @@
     }
 
     @Test
-    fun `test NO_HDR mode`() {
+    fun contentObserverNotRegisteredOnInit_hdrDataMissing() {
+        initHdrModifier(null)
+
+        assertThat(testInjector.registeredContentObserver).isNull()
+    }
+
+    @Test
+    fun contentObserverNotRegisteredOnInit_allowedInLowPowerMode() {
+        initHdrModifier(createHdrBrightnessData(allowInLowPowerMode = true))
+
+        assertThat(testInjector.registeredContentObserver).isNull()
+    }
+
+    @Test
+    fun contentObserverRegisteredOnInit_notAllowedInLowPowerMode() {
+        initHdrModifier(createHdrBrightnessData(allowInLowPowerMode = false))
+
+        assertThat(testInjector.registeredContentObserver).isNotNull()
+    }
+
+    @Test
+    fun testNoHdrMode() {
         initHdrModifier()
         // screen size = 10_000
         setupDisplay(width = 100, height = 100, hdrBrightnessData = createHdrBrightnessData(
@@ -131,7 +151,7 @@
     }
 
     @Test
-    fun `test NBM_HDR mode`() {
+    fun testNbmHdrMode() {
         initHdrModifier()
         // screen size = 10_000
         val transitionPoint = 0.55f
@@ -157,7 +177,7 @@
     }
 
     @Test
-    fun `test HBM_HDR mode`() {
+    fun testHbmHdrMode() {
         initHdrModifier()
         // screen size = 10_000
         setupDisplay(width = 100, height = 100, hdrBrightnessData = createHdrBrightnessData(
@@ -182,7 +202,7 @@
     }
 
     @Test
-    fun `test display change no HDR content`() {
+    fun testDisplayChange_noHdrContent() {
         initHdrModifier()
         setupDisplay(width = 100, height = 100)
         assertModifierState()
@@ -195,7 +215,7 @@
     }
 
     @Test
-    fun `test display change with HDR content`() {
+    fun testDisplayChange_hdrContent() {
         initHdrModifier()
         setupDisplay(width = 100, height = 100)
         setupHdrLayer(width = 100, height = 100, maxHdrRatio = 5f)
@@ -218,7 +238,7 @@
     }
 
     @Test
-    fun `test ambient lux decrease above maxBrightnessLimits no HDR`() {
+    fun testSetAmbientLux_decreaseAboveMaxBrightnessLimitNoHdr() {
         initHdrModifier()
         modifier.setAmbientLux(1000f)
         setupDisplay(width = 100, height = 100, hdrBrightnessData = createHdrBrightnessData(
@@ -234,7 +254,7 @@
     }
 
     @Test
-    fun `test ambient lux decrease above maxBrightnessLimits with HDR`() {
+    fun testSetAmbientLux_decreaseAboveMaxBrightnessLimitWithHdr() {
         initHdrModifier()
         modifier.setAmbientLux(1000f)
         setupDisplay(width = 200, height = 200, hdrBrightnessData = createHdrBrightnessData(
@@ -260,7 +280,7 @@
     }
 
     @Test
-    fun `test ambient lux decrease below maxBrightnessLimits no HDR`() {
+    fun testSetAmbientLux_decreaseBelowMaxBrightnessLimitNoHdr() {
         initHdrModifier()
         modifier.setAmbientLux(1000f)
         setupDisplay(width = 100, height = 100, hdrBrightnessData = createHdrBrightnessData(
@@ -276,7 +296,7 @@
     }
 
     @Test
-    fun `test ambient lux decrease below maxBrightnessLimits with HDR`() {
+    fun testSetAmbientLux_decreaseBelowMaxBrightnessLimitWithHdr() {
         initHdrModifier()
         modifier.setAmbientLux(1000f)
         val maxBrightness = 0.6f
@@ -322,6 +342,23 @@
         )
     }
 
+    @Test
+    fun testLowPower_notAllowedInLowPower() {
+        initHdrModifier()
+        setupDisplay(width = 100, height = 100, hdrBrightnessData = createHdrBrightnessData(
+            allowInLowPowerMode = false
+        ))
+        setupHdrLayer(width = 100, height = 100)
+        clearInvocations(mockChangeListener)
+
+        testInjector.isLowPower = true
+        testInjector.registeredContentObserver!!.onChange(true)
+
+        verify(mockChangeListener).onChanged()
+        assertModifierState()
+    }
+
+    // Helper functions
     private fun setupHdrLayer(width: Int = 100, height: Int = 100, maxHdrRatio: Float = 0.8f) {
         testInjector.registeredHdrListener!!.onHdrInfoChanged(
             mockDisplayBinder, 1, width, height, 0, maxHdrRatio
@@ -345,7 +382,6 @@
             width = width,
             height = height
         ))
-        testHandler.flush()
     }
 
     private fun initHdrModifier(hdrBrightnessData: HdrBrightnessData? = createHdrBrightnessData()) {
@@ -384,9 +420,12 @@
         assertThat(stateBuilder.customAnimationRate).isEqualTo(animationRate)
     }
 
-    internal class TestInjector : Injector() {
+    internal class TestInjector(context: Context) : Injector(context) {
         var registeredHdrListener: SurfaceControlHdrLayerInfoListener? = null
         var registeredToken: IBinder? = null
+        var registeredContentObserver: ContentObserver? = null
+
+        var isLowPower: Boolean = false
 
         override fun registerHdrListener(
             listener: SurfaceControlHdrLayerInfoListener, token: IBinder
@@ -401,5 +440,17 @@
             registeredHdrListener = null
             registeredToken = null
         }
+
+        override fun registerContentObserver(observer: ContentObserver, uri: Uri) {
+            registeredContentObserver = observer
+        }
+
+        override fun unregisterContentObserver(observer: ContentObserver) {
+            registeredContentObserver = null
+        }
+
+        override fun isLowPowerMode(): Boolean {
+            return isLowPower
+        }
     }
 }
\ No newline at end of file
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java
index 2d4dbb7..78c9372 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java
@@ -479,6 +479,15 @@
                 eq(mContext.getOpPackageName()), anyInt(), anyInt(), any());
     }
 
+    @Test
+    public void testCancelAuth_whenClientWaitingForCookie() throws RemoteException {
+        final FaceAuthenticationClient client = createClient(true);
+        client.waitForCookie(mCallback);
+        client.cancel();
+
+        verify(mCallback).onClientFinished(client, false);
+    }
+
     private FaceAuthenticationClient createClient() throws RemoteException {
         return createClient(2 /* version */, mClientMonitorCallbackConverter,
                 false /* allowBackgroundAuthentication */, true /* isBiometricPrompt */,
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java
index 6ec888c..7e1d421 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java
@@ -117,7 +117,6 @@
     private static final int TOUCH_Y = 20;
     private static final float TOUCH_MAJOR = 4.4f;
     private static final float TOUCH_MINOR = 5.5f;
-    private static final int FINGER_UP = 111;
 
     @Rule
     public final TestableContext mContext = new TestableContext(
@@ -383,6 +382,8 @@
 
     @Test
     public void subscribeContextAndStartHal() throws RemoteException {
+        when(mHal.authenticateWithContext(anyLong(), any())).thenReturn(mCancellationSignal);
+
         final FingerprintAuthenticationClient client = createClient();
         client.start(mCallback);
 
@@ -691,6 +692,17 @@
         verify(mLockoutTracker).addFailedAttemptForUser(USER_ID);
     }
 
+    @Test
+    public void testCancelAuth_whenClientWaitingForCookie() throws RemoteException {
+        final FingerprintAuthenticationClient client = createClientWithoutBackgroundAuth();
+        client.waitForCookie(mCallback);
+        client.cancel();
+        mLooper.moveTimeForward(10);
+        mLooper.dispatchAll();
+
+        verify(mCallback).onClientFinished(client, false);
+    }
+
     private FingerprintAuthenticationClient createClient() throws RemoteException {
         return createClient(100 /* version */, true /* allowBackgroundAuthentication */,
                 true /* isBiometricPrompt */,
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 0468f48..e57a95e 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -10038,6 +10038,19 @@
             "carrier_supported_satellite_notification_hysteresis_sec_int";
 
     /**
+     * An integer key holds the timeout duration in seconds used to determine whether to exit
+     * carrier-roaming NB-IOT satellite mode.
+     *
+     * The timer is started when the device screen is turned off during a satellite session.
+     * When the timer expires, the device exits Carrier Roaming NB IOT NTN.
+     *
+     * The default value is 30 seconds.
+     */
+    @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
+    public static final String KEY_SATELLITE_SCREEN_OFF_INACTIVITY_TIMEOUT_SEC_INT =
+            "satellite_screen_off_inactivity_timeout_duration_sec_int";
+
+    /**
      * Indicating whether DUN APN should be disabled when the device is roaming. In that case,
      * the default APN (i.e. internet) will be used for tethering.
      *
@@ -11197,6 +11210,7 @@
         sDefaults.putBoolean(KEY_SATELLITE_ESOS_SUPPORTED_BOOL, false);
         sDefaults.putInt(KEY_CARRIER_ROAMING_NTN_CONNECT_TYPE_INT, 0);
         sDefaults.putInt(KEY_CARRIER_SUPPORTED_SATELLITE_NOTIFICATION_HYSTERESIS_SEC_INT, 180);
+        sDefaults.putInt(KEY_SATELLITE_SCREEN_OFF_INACTIVITY_TIMEOUT_SEC_INT, 30);
         sDefaults.putString(KEY_DEFAULT_PREFERRED_APN_NAME_STRING, "");
         sDefaults.putBoolean(KEY_SUPPORTS_CALL_COMPOSER_BOOL, false);
         sDefaults.putBoolean(KEY_SUPPORTS_BUSINESS_CALL_COMPOSER_BOOL, false);