Merge "Reapply "Finalize resources for VIC"" into main
diff --git a/core/java/android/app/admin/flags/flags.aconfig b/core/java/android/app/admin/flags/flags.aconfig
index 31c9a258..18914e1 100644
--- a/core/java/android/app/admin/flags/flags.aconfig
+++ b/core/java/android/app/admin/flags/flags.aconfig
@@ -275,13 +275,23 @@
 }
 
 flag {
-    name: "headless_single_user_bad_device_admin_state_fix"
-    namespace: "enterprise"
-    description: "Fix the bad state in DPMS caused by an earlier bug related to the headless single user change"
-    bug: "332477138"
-    metadata {
-      purpose: PURPOSE_BUGFIX
-    }
+  name: "headless_single_user_bad_device_admin_state_fix"
+  namespace: "enterprise"
+  description: "Fix the bad state in DPMS caused by an earlier bug related to the headless single user change"
+  bug: "332477138"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
+
+flag {
+  name: "onboarding_bugreport_storage_bug_fix"
+  namespace: "enterprise"
+  description: "Add a separate storage limit for deferred bugreports"
+  bug: "330177040"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
 }
 
 flag {
diff --git a/core/java/android/service/dreams/flags.aconfig b/core/java/android/service/dreams/flags.aconfig
index f87cb85..a42eaff 100644
--- a/core/java/android/service/dreams/flags.aconfig
+++ b/core/java/android/service/dreams/flags.aconfig
@@ -29,6 +29,16 @@
 }
 
 flag {
+    name: "dismiss_dream_on_keyguard_dismiss"
+    namespace: "systemui"
+    description: "Dismisses the dream in the keyguard-going-away transition, preventing it from being visible"
+    bug: "333829441"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
+
+flag {
   name: "dream_tracks_focus"
   namespace: "communal"
   description: "This flag enables the ability for dreams to track whether or not they have focus"
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index 6834e6d..17121c8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
@@ -702,10 +702,12 @@
             ShellInit shellInit,
             ShellController shellController,
             Transitions transitions,
+            TaskStackListenerImpl taskStackListener,
             @ShellMainThread Handler mainHandler,
             @ShellMainThread ShellExecutor mainExecutor) {
         return new KeyguardTransitionHandler(
-                    shellInit, shellController, transitions, mainHandler, mainExecutor);
+                shellInit, shellController, transitions, taskStackListener, mainHandler,
+                mainExecutor);
     }
 
     @WMSingleton
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java
index 863a51a..9eaf7e4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java
@@ -20,6 +20,7 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.service.dreams.Flags.dismissDreamOnKeyguardDismiss;
 import static android.view.WindowManager.KEYGUARD_VISIBILITY_TRANSIT_FLAGS;
 import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY;
 import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_LOCKED;
@@ -44,10 +45,13 @@
 import android.window.IRemoteTransitionFinishedCallback;
 import android.window.TransitionInfo;
 import android.window.TransitionRequestInfo;
+import android.window.WindowContainerToken;
 import android.window.WindowContainerTransaction;
 
 import com.android.internal.protolog.common.ProtoLog;
 import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.common.TaskStackListenerCallback;
+import com.android.wm.shell.common.TaskStackListenerImpl;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
 import com.android.wm.shell.shared.annotations.ExternalThread;
 import com.android.wm.shell.sysui.KeyguardChangeListener;
@@ -62,7 +66,8 @@
  * <p>This takes the highest priority.
  */
 public class KeyguardTransitionHandler
-        implements Transitions.TransitionHandler, KeyguardChangeListener {
+        implements Transitions.TransitionHandler, KeyguardChangeListener,
+        TaskStackListenerCallback {
     private static final String TAG = "KeyguardTransition";
 
     private final Transitions mTransitions;
@@ -71,6 +76,7 @@
     private final ShellExecutor mMainExecutor;
 
     private final ArrayMap<IBinder, StartedTransition> mStartedTransitions = new ArrayMap<>();
+    private final TaskStackListenerImpl mTaskStackListener;
 
     /**
      * Local IRemoteTransition implementations registered by the keyguard service.
@@ -87,6 +93,8 @@
 
     // Last value reported by {@link KeyguardChangeListener}.
     private boolean mKeyguardShowing = true;
+    @Nullable
+    private WindowContainerToken mDreamToken;
 
     private final class StartedTransition {
         final TransitionInfo mInfo;
@@ -105,18 +113,23 @@
             @NonNull ShellInit shellInit,
             @NonNull ShellController shellController,
             @NonNull Transitions transitions,
+            @NonNull TaskStackListenerImpl taskStackListener,
             @NonNull Handler mainHandler,
             @NonNull ShellExecutor mainExecutor) {
         mTransitions = transitions;
         mShellController = shellController;
         mMainHandler = mainHandler;
         mMainExecutor = mainExecutor;
+        mTaskStackListener = taskStackListener;
         shellInit.addInitCallback(this::onInit, this);
     }
 
     private void onInit() {
         mTransitions.addHandler(this);
         mShellController.addKeyguardChangeListener(this);
+        if (dismissDreamOnKeyguardDismiss()) {
+            mTaskStackListener.addListener(this);
+        }
     }
 
     /**
@@ -142,6 +155,11 @@
     }
 
     @Override
+    public void onTaskMovedToFront(ActivityManager.RunningTaskInfo taskInfo) {
+        mDreamToken = taskInfo.getActivityType() == ACTIVITY_TYPE_DREAM ? taskInfo.token : null;
+    }
+
+    @Override
     public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
             @NonNull SurfaceControl.Transaction startTransaction,
             @NonNull SurfaceControl.Transaction finishTransaction,
@@ -271,6 +289,13 @@
     @Override
     public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
             @NonNull TransitionRequestInfo request) {
+        if (dismissDreamOnKeyguardDismiss()
+                && (request.getFlags() & TRANSIT_FLAG_KEYGUARD_GOING_AWAY) != 0
+                && mDreamToken != null) {
+            // Dismiss the dream in the same transaction, so that it isn't visible once the device
+            // is unlocked.
+            return new WindowContainerTransaction().removeTask(mDreamToken);
+        }
         return null;
     }
 
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
index 9e9350b..2e9075c 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
@@ -186,9 +186,14 @@
         sendBroadcast = sBroadcastOnRestore.contains(name);
         sendBroadcastSystemUI = sBroadcastOnRestoreSystemUI.contains(name);
 
-        if (sendBroadcast || sendBroadcastSystemUI) {
+        if (sendBroadcast) {
             // TODO: http://b/22388012
             oldValue = table.lookup(cr, name, UserHandle.USER_SYSTEM);
+        } else if (sendBroadcastSystemUI) {
+            // This is only done for broadcasts sent to system ui as the consumers are known.
+            // It would probably be correct to do it for the ones sent to the system, but consumers
+            // may be depending on the current behavior.
+            oldValue = table.lookup(cr, name, context.getUserId());
         }
 
         try {
@@ -266,7 +271,7 @@
                 if (sendBroadcastSystemUI) {
                     intent.setPackage(
                             context.getString(com.android.internal.R.string.config_systemUi));
-                    context.sendBroadcastAsUser(intent, UserHandle.SYSTEM, null);
+                    context.sendBroadcastAsUser(intent, context.getUser(), null);
                 }
             }
         }
diff --git a/packages/Shell/Android.bp b/packages/Shell/Android.bp
index c87916f..2531454 100644
--- a/packages/Shell/Android.bp
+++ b/packages/Shell/Android.bp
@@ -19,6 +19,9 @@
         include_dirs: ["frameworks/native/cmds/dumpstate/binder"],
     },
     static_libs: shell_static_libs,
+    libs: [
+        "device_policy_aconfig_flags_lib",
+    ],
     platform_apis: true,
     certificate: "platform",
     privileged: true,
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index 5ac0e44..bcfd8f6 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -16,6 +16,7 @@
 
 package com.android.shell;
 
+import static android.app.admin.flags.Flags.onboardingBugreportStorageBugFix;
 import static android.content.pm.PackageManager.FEATURE_LEANBACK;
 import static android.content.pm.PackageManager.FEATURE_TELEVISION;
 import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
@@ -89,10 +90,10 @@
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 
-import com.google.android.collect.Lists;
-
 import libcore.io.Streams;
 
+import com.google.android.collect.Lists;
+
 import java.io.BufferedOutputStream;
 import java.io.ByteArrayInputStream;
 import java.io.File;
@@ -109,6 +110,8 @@
 import java.text.NumberFormat;
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
 import java.util.Date;
 import java.util.Enumeration;
 import java.util.HashMap;
@@ -442,10 +445,14 @@
         }
     }
 
-    private static void sendRemoteBugreportFinishedBroadcast(Context context,
+    private void sendRemoteBugreportFinishedBroadcast(Context context,
             String bugreportFileName, File bugreportFile, long nonce) {
-        cleanupOldFiles(REMOTE_BUGREPORT_FILES_AMOUNT, REMOTE_MIN_KEEP_AGE,
-                bugreportFile.getParentFile());
+        // Remote bugreports are stored in the same directory as normal bugreports, meaning that
+        // the remote bugreport storage limit will get applied to normal bugreports whenever a
+        // remote bugreport is triggered. The fix in cleanupOldFiles applies the normal bugreport
+        // limit to the remote bugreports as a quick fix.
+        cleanupOldFiles(
+                REMOTE_BUGREPORT_FILES_AMOUNT, REMOTE_MIN_KEEP_AGE, bugreportFile.getParentFile());
         final Intent intent = new Intent(DevicePolicyManager.ACTION_REMOTE_BUGREPORT_DISPATCH);
         final Uri bugreportUri = getUri(context, bugreportFile);
         final String bugreportHash = generateFileHash(bugreportFileName);
@@ -496,12 +503,16 @@
         return fileHash;
     }
 
-    static void cleanupOldFiles(final int minCount, final long minAge, File bugreportsDir) {
+    void cleanupOldFiles(final int minCount, final long minAge, File bugreportsDir) {
         new AsyncTask<Void, Void, Void>() {
             @Override
             protected Void doInBackground(Void... params) {
                 try {
-                    FileUtils.deleteOlderFiles(bugreportsDir, minCount, minAge);
+                    if (onboardingBugreportStorageBugFix()) {
+                        cleanupOldBugreports();
+                    } else {
+                        FileUtils.deleteOlderFiles(bugreportsDir, minCount, minAge);
+                    }
                 } catch (RuntimeException e) {
                     Log.e(TAG, "RuntimeException deleting old files", e);
                 }
@@ -510,6 +521,42 @@
         }.execute();
     }
 
+    private void cleanupOldBugreports() {
+        final File[] files = mBugreportsDir.listFiles();
+        if (files == null) return;
+
+        // Sort with newest files first
+        Arrays.sort(files, new Comparator<File>() {
+            @Override
+            public int compare(File lhs, File rhs) {
+                return Long.compare(rhs.lastModified(), lhs.lastModified());
+            }
+        });
+
+        int normalBugreportFilesCount = 0;
+        int deferredBugreportFilesCount = 0;
+        for (int i = 0; i < files.length; i++) {
+            final File file = files[i];
+
+            // tmp files are deferred bugreports which have their separate storage limit
+            boolean isDeferredBugreportFile = file.getName().endsWith(".tmp");
+            if (isDeferredBugreportFile) {
+                deferredBugreportFilesCount++;
+            } else {
+                normalBugreportFilesCount++;
+            }
+            // Keep files newer than minAgeMs
+            final long age = System.currentTimeMillis() - file.lastModified();
+            final int count = isDeferredBugreportFile
+                    ? deferredBugreportFilesCount : normalBugreportFilesCount;
+            if (count > MIN_KEEP_COUNT  && age > MIN_KEEP_AGE) {
+                if (file.delete()) {
+                    Log.d(TAG, "Deleted old file " + file);
+                }
+            }
+        }
+    }
+
     /**
      * Main thread used to handle all requests but taking screenshots.
      */
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToOccludedTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToOccludedTransitionViewModelTest.kt
new file mode 100644
index 0000000..288dc48
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToOccludedTransitionViewModelTest.kt
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.ui.viewmodel
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectValues
+import com.android.systemui.flags.Flags
+import com.android.systemui.flags.fakeFeatureFlagsClassic
+import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.shared.model.TransitionState.RUNNING
+import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@ExperimentalCoroutinesApi
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class AlternateBouncerToOccludedTransitionViewModelTest : SysuiTestCase() {
+    val kosmos =
+        testKosmos().apply {
+            fakeFeatureFlagsClassic.apply { set(Flags.FULL_SCREEN_USER_SWITCHER, false) }
+        }
+
+    private val testScope = kosmos.testScope
+    private val keyguardTransitionRepository = kosmos.fakeKeyguardTransitionRepository
+    private val underTest by lazy { kosmos.alternateBouncerToOccludedTransitionViewModel }
+
+    @Test
+    fun deviceEntryParentViewDisappear() =
+        testScope.runTest {
+            val values by collectValues(underTest.deviceEntryParentViewAlpha)
+
+            keyguardTransitionRepository.sendTransitionSteps(
+                listOf(
+                    step(0f, TransitionState.STARTED),
+                    step(0f),
+                    step(0.1f),
+                    step(0.2f),
+                    step(0.3f),
+                    step(1f),
+                ),
+                testScope,
+            )
+
+            values.forEach { assertThat(it).isEqualTo(0f) }
+        }
+
+    private fun step(value: Float, state: TransitionState = RUNNING): TransitionStep {
+        return TransitionStep(
+            from = KeyguardState.ALTERNATE_BOUNCER,
+            to = KeyguardState.OCCLUDED,
+            value = value,
+            transitionState = state,
+            ownerName = "AlternateBouncerToOccludedTransitionViewModelTest"
+        )
+    }
+}
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 e160cfc..66f7416 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
@@ -155,7 +155,6 @@
                 mColorExtractor,
                 mDumpManager,
                 mKeyguardStateController,
-                mKosmos.getScreenOffAnimationController(),
                 mAuthController,
                 mKosmos::getShadeInteractor,
                 mShadeWindowLogger,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index ae163ea..674c128 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -17,6 +17,7 @@
 package com.android.systemui.keyguard;
 
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.service.dreams.Flags.dismissDreamOnKeyguardDismiss;
 import static android.view.RemoteAnimationTarget.MODE_OPENING;
 import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_APPEARING;
 import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY;
@@ -89,14 +90,14 @@
 import com.android.wm.shell.shared.TransitionUtil;
 import com.android.wm.shell.transition.Transitions;
 
-import kotlinx.coroutines.CoroutineScope;
-
 import java.util.ArrayList;
 import java.util.Map;
 import java.util.WeakHashMap;
 
 import javax.inject.Inject;
 
+import kotlinx.coroutines.CoroutineScope;
+
 public class KeyguardService extends Service {
     static final String TAG = "KeyguardService";
     static final String PERMISSION = android.Manifest.permission.CONTROL_KEYGUARD;
@@ -223,6 +224,20 @@
                 initAlphaForAnimationTargets(t, apps);
                 initAlphaForAnimationTargets(t, wallpapers);
 
+                // If the keyguard is going away, hide the dream if one exists.
+                if (dismissDreamOnKeyguardDismiss()
+                        && (info.getFlags() & TRANSIT_FLAG_KEYGUARD_GOING_AWAY) != 0) {
+                    for (RemoteAnimationTarget app : apps) {
+                        final boolean isDream = app.taskInfo != null
+                                && app.taskInfo.getActivityType()
+                                == WindowConfiguration.ACTIVITY_TYPE_DREAM;
+                        if (isDream && app.mode == RemoteAnimationTarget.MODE_CLOSING) {
+                            t.hide(app.leash);
+                            break;
+                        }
+                    }
+                }
+
                 t.apply();
 
                 runner.onAnimationStart(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 46aec25..5d31d1e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -21,6 +21,7 @@
 import static android.provider.Settings.Secure.LOCK_SCREEN_LOCK_AFTER_TIMEOUT;
 import static android.provider.Settings.System.LOCKSCREEN_SOUNDS_ENABLED;
 import static android.provider.Settings.System.SCREEN_OFF_TIMEOUT;
+import static android.service.dreams.Flags.dismissDreamOnKeyguardDismiss;
 import static android.view.RemoteAnimationTarget.MODE_OPENING;
 import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_NO_WINDOW_ANIMATIONS;
 import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_TO_LAUNCHER_CLEAR_SNAPSHOT;
@@ -3092,13 +3093,22 @@
                         createInteractionJankMonitorConf(
                                 CUJ_LOCKSCREEN_UNLOCK_ANIMATION, "DismissPanel"));
 
+                // Filter out any closing apps, such as the dream.
+                RemoteAnimationTarget[] openingApps = apps;
+                if (dismissDreamOnKeyguardDismiss()) {
+                    openingApps = Arrays.stream(apps)
+                            .filter(a -> a.mode == RemoteAnimationTarget.MODE_OPENING)
+                            .toArray(RemoteAnimationTarget[]::new);
+                }
+
                 // Pass the surface and metadata to the unlock animation controller.
                 RemoteAnimationTarget[] openingWallpapers = Arrays.stream(wallpapers).filter(
                         w -> w.mode == RemoteAnimationTarget.MODE_OPENING).toArray(
                         RemoteAnimationTarget[]::new);
+
                 mKeyguardUnlockAnimationControllerLazy.get()
                         .notifyStartSurfaceBehindRemoteAnimation(
-                                apps, openingWallpapers, startTime,
+                                openingApps, openingWallpapers, startTime,
                                 mSurfaceBehindRemoteAnimationRequested);
             } else {
                 mInteractionJankMonitor.begin(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt
index e441017..5a28f711 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt
@@ -193,5 +193,6 @@
         val TO_AOD_DURATION = TRANSITION_DURATION_MS
         val TO_PRIMARY_BOUNCER_DURATION = TRANSITION_DURATION_MS
         val TO_DOZING_DURATION = TRANSITION_DURATION_MS
+        val TO_OCCLUDED_DURATION = TRANSITION_DURATION_MS
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/DeviceEntryIconTransitionModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/DeviceEntryIconTransitionModule.kt
index 3b21141..a8e9041 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/DeviceEntryIconTransitionModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/DeviceEntryIconTransitionModule.kt
@@ -18,6 +18,7 @@
 import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerToAodTransitionViewModel
 import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerToDozingTransitionViewModel
 import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerToGoneTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerToOccludedTransitionViewModel
 import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerToPrimaryBouncerTransitionViewModel
 import com.android.systemui.keyguard.ui.viewmodel.AodToGoneTransitionViewModel
 import com.android.systemui.keyguard.ui.viewmodel.AodToLockscreenTransitionViewModel
@@ -71,6 +72,12 @@
 
     @Binds
     @IntoSet
+    abstract fun alternateBouncerToOccluded(
+        impl: AlternateBouncerToOccludedTransitionViewModel
+    ): DeviceEntryIconTransition
+
+    @Binds
+    @IntoSet
     abstract fun alternateBouncerToPrimaryBouncer(
         impl: AlternateBouncerToPrimaryBouncerTransitionViewModel
     ): DeviceEntryIconTransition
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToOccludedTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToOccludedTransitionViewModel.kt
new file mode 100644
index 0000000..27febd3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToOccludedTransitionViewModel.kt
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.ui.viewmodel
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.domain.interactor.FromAlternateBouncerTransitionInteractor.Companion.TO_OCCLUDED_DURATION
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER
+import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import javax.inject.Inject
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+
+/**
+ * Breaks down ALTERNATE_BOUNCER->GONE transition into discrete steps for corresponding views to
+ * consume.
+ */
+@OptIn(ExperimentalCoroutinesApi::class)
+@SysUISingleton
+class AlternateBouncerToOccludedTransitionViewModel
+@Inject
+constructor(
+    animationFlow: KeyguardTransitionAnimationFlow,
+) : DeviceEntryIconTransition {
+    private val transitionAnimation =
+        animationFlow.setup(
+            duration = TO_OCCLUDED_DURATION,
+            from = ALTERNATE_BOUNCER,
+            to = KeyguardState.OCCLUDED,
+        )
+
+    override val deviceEntryParentViewAlpha: Flow<Float> =
+        transitionAnimation.immediatelyTransitionTo(0f)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
index 30f33a3..f8086f5 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
@@ -21,19 +21,20 @@
 import android.graphics.Paint
 import android.graphics.Point
 import android.os.Handler
-import android.os.SystemClock
 import android.util.Log
 import android.util.MathUtils
 import android.view.Gravity
 import android.view.HapticFeedbackConstants
 import android.view.MotionEvent
 import android.view.VelocityTracker
+import android.view.View
 import android.view.ViewConfiguration
 import android.view.WindowManager
 import androidx.annotation.VisibleForTesting
 import androidx.core.os.postDelayed
 import androidx.core.view.isVisible
 import androidx.dynamicanimation.animation.DynamicAnimation
+import com.android.internal.jank.Cuj
 import com.android.internal.jank.InteractionJankMonitor
 import com.android.internal.util.LatencyTracker
 import com.android.systemui.dagger.qualifiers.Main
@@ -41,6 +42,7 @@
 import com.android.systemui.statusbar.VibratorHelper
 import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.util.ViewController
+import com.android.systemui.util.time.SystemClock
 import java.io.PrintWriter
 import javax.inject.Inject
 import kotlin.math.abs
@@ -84,6 +86,7 @@
     private val windowManager: WindowManager,
     private val viewConfiguration: ViewConfiguration,
     @Main private val mainHandler: Handler,
+    private val systemClock: SystemClock,
     private val vibratorHelper: VibratorHelper,
     private val configurationController: ConfigurationController,
     private val latencyTracker: LatencyTracker,
@@ -102,6 +105,7 @@
         private val windowManager: WindowManager,
         private val viewConfiguration: ViewConfiguration,
         @Main private val mainHandler: Handler,
+        private val systemClock: SystemClock,
         private val vibratorHelper: VibratorHelper,
         private val configurationController: ConfigurationController,
         private val latencyTracker: LatencyTracker,
@@ -115,6 +119,7 @@
                     windowManager,
                     viewConfiguration,
                     mainHandler,
+                    systemClock,
                     vibratorHelper,
                     configurationController,
                     latencyTracker,
@@ -158,9 +163,9 @@
     private var gestureInactiveTime = 0L
 
     private val elapsedTimeSinceInactive
-        get() = SystemClock.uptimeMillis() - gestureInactiveTime
+        get() = systemClock.uptimeMillis() - gestureInactiveTime
     private val elapsedTimeSinceEntry
-        get() = SystemClock.uptimeMillis() - gestureEntryTime
+        get() = systemClock.uptimeMillis() - gestureEntryTime
 
     private var pastThresholdWhileEntryOrInactiveTime = 0L
     private var entryToActiveDelay = 0F
@@ -178,7 +183,7 @@
     // Distance in pixels a drag can be considered for a fling event
     private var minFlingDistance = 0
 
-    private val failsafeRunnable = Runnable { onFailsafe() }
+    internal val failsafeRunnable = Runnable { onFailsafe() }
 
     internal enum class GestureState {
         /* Arrow is off the screen and invisible */
@@ -370,6 +375,7 @@
                 // Receiving a CANCEL implies that something else intercepted
                 // the gesture, i.e., the user did not cancel their gesture.
                 // Therefore, disappear immediately, with minimum fanfare.
+                interactionJankMonitor.cancel(Cuj.CUJ_BACK_PANEL_ARROW)
                 updateArrowState(GestureState.GONE)
                 velocityTracker = null
             }
@@ -692,10 +698,10 @@
         }
 
         if (isPastThresholdForFirstTime) {
-            pastThresholdWhileEntryOrInactiveTime = SystemClock.uptimeMillis()
+            pastThresholdWhileEntryOrInactiveTime = systemClock.uptimeMillis()
             entryToActiveDelay = dynamicDelay()
         }
-        val timePastThreshold = SystemClock.uptimeMillis() - pastThresholdWhileEntryOrInactiveTime
+        val timePastThreshold = systemClock.uptimeMillis() - pastThresholdWhileEntryOrInactiveTime
 
         return timePastThreshold > entryToActiveDelay
     }
@@ -881,6 +887,16 @@
         previousState = currentState
         currentState = newState
 
+        // First, update the jank tracker
+        when (currentState) {
+            GestureState.ENTRY -> {
+                interactionJankMonitor.cancel(Cuj.CUJ_BACK_PANEL_ARROW)
+                interactionJankMonitor.begin(mView, Cuj.CUJ_BACK_PANEL_ARROW)
+            }
+            GestureState.GONE -> interactionJankMonitor.end(Cuj.CUJ_BACK_PANEL_ARROW)
+            else -> {}
+        }
+
         when (currentState) {
             GestureState.CANCELLED -> {
                 backCallback.cancelBack()
@@ -912,7 +928,7 @@
                 mView.isVisible = true
 
                 updateRestingArrowDimens()
-                gestureEntryTime = SystemClock.uptimeMillis()
+                gestureEntryTime = systemClock.uptimeMillis()
             }
             GestureState.ACTIVE -> {
                 previousXTranslationOnActiveOffset = previousXTranslation
@@ -927,7 +943,7 @@
                 mView.popOffEdge(popVelocity)
             }
             GestureState.INACTIVE -> {
-                gestureInactiveTime = SystemClock.uptimeMillis()
+                gestureInactiveTime = systemClock.uptimeMillis()
 
                 // Typically entering INACTIVE means
                 // totalTouchDelta <= deactivationSwipeTriggerThreshold
@@ -1041,6 +1057,11 @@
         pw.println("  isLeftPanel=${mView.isLeftPanel}")
     }
 
+    @VisibleForTesting
+    internal fun getBackPanelView(): BackPanel {
+        return mView
+    }
+
     init {
         if (DEBUG)
             mView.drawDebugInfo = { canvas ->
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
index 8b7e11c..4a636d2 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
@@ -68,7 +68,6 @@
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
 import com.android.systemui.statusbar.phone.DozeParameters;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
-import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
 import com.android.systemui.statusbar.phone.ScrimController;
 import com.android.systemui.statusbar.phone.StatusBarWindowCallback;
 import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -131,7 +130,6 @@
             mCallbacks = new ArrayList<>();
 
     private final SysuiColorExtractor mColorExtractor;
-    private final ScreenOffAnimationController mScreenOffAnimationController;
     /**
      * Layout params would be aggregated and dispatched all at once if this is > 0.
      *
@@ -159,7 +157,6 @@
             SysuiColorExtractor colorExtractor,
             DumpManager dumpManager,
             KeyguardStateController keyguardStateController,
-            ScreenOffAnimationController screenOffAnimationController,
             AuthController authController,
             Lazy<ShadeInteractor> shadeInteractorLazy,
             ShadeWindowLogger logger,
@@ -179,7 +176,6 @@
         mKeyguardBypassController = keyguardBypassController;
         mBackgroundExecutor = backgroundExecutor;
         mColorExtractor = colorExtractor;
-        mScreenOffAnimationController = screenOffAnimationController;
         // prefix with {slow} to make sure this dumps at the END of the critical section.
         dumpManager.registerCriticalDumpable("{slow}NotificationShadeWindowControllerImpl", this);
         mAuthController = authController;
@@ -441,14 +437,8 @@
     private void applyFocusableFlag(NotificationShadeWindowState state) {
         boolean panelFocusable = state.notificationShadeFocusable && state.shadeOrQsExpanded;
         if (state.bouncerShowing && (state.keyguardOccluded || state.keyguardNeedsInput)
-                || ENABLE_REMOTE_INPUT && state.remoteInputActive
-                // Make the panel focusable if we're doing the screen off animation, since the light
-                // reveal scrim is drawing in the panel and should consume touch events so that they
-                // don't go to the app behind.
-                || mScreenOffAnimationController.shouldIgnoreKeyguardTouches()) {
-            mLpChanged.flags &= ~LayoutParams.FLAG_NOT_FOCUSABLE;
-            mLpChanged.flags &= ~LayoutParams.FLAG_ALT_FOCUSABLE_IM;
-        } else if (state.glanceableHubShowing) {
+                || (ENABLE_REMOTE_INPUT && state.remoteInputActive)
+                || state.glanceableHubShowing) {
             mLpChanged.flags &= ~LayoutParams.FLAG_NOT_FOCUSABLE;
             mLpChanged.flags &= ~LayoutParams.FLAG_ALT_FOCUSABLE_IM;
         } else if (state.isKeyguardShowingAndNotOccluded() || panelFocusable) {
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 59f7d61..325e7bf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -262,7 +262,6 @@
                 mColorExtractor,
                 mDumpManager,
                 mKeyguardStateController,
-                mScreenOffAnimationController,
                 mAuthController,
                 () -> mShadeInteractor,
                 mShadeWindowLogger,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/BackPanelControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/BackPanelControllerTest.kt
index e6c259a..f1c97dd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/BackPanelControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/BackPanelControllerTest.kt
@@ -17,7 +17,6 @@
 package com.android.systemui.navigationbar.gestural
 
 import android.os.Handler
-import android.os.Looper
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import android.view.HapticFeedbackConstants
@@ -28,6 +27,7 @@
 import android.view.ViewConfiguration
 import android.view.WindowManager
 import androidx.test.filters.SmallTest
+import com.android.internal.jank.Cuj
 import com.android.internal.util.LatencyTracker
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.jank.interactionJankMonitor
@@ -35,6 +35,7 @@
 import com.android.systemui.statusbar.VibratorHelper
 import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.testKosmos
+import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
 import org.junit.Before
 import org.junit.Test
@@ -43,6 +44,7 @@
 import org.mockito.ArgumentMatchers.eq
 import org.mockito.Mock
 import org.mockito.Mockito.clearInvocations
+import org.mockito.Mockito.never
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
 
@@ -55,6 +57,7 @@
     }
     private val kosmos = testKosmos()
     private lateinit var mBackPanelController: BackPanelController
+    private lateinit var systemClock: FakeSystemClock
     private lateinit var testableLooper: TestableLooper
     private var triggerThreshold: Float = 0.0f
     private val touchSlop = ViewConfiguration.get(context).scaledEdgeSlop
@@ -69,12 +72,15 @@
     @Before
     fun setup() {
         MockitoAnnotations.initMocks(this)
+        testableLooper = TestableLooper.get(this)
+        systemClock = FakeSystemClock()
         mBackPanelController =
             BackPanelController(
                 context,
                 windowManager,
                 ViewConfiguration.get(context),
-                Handler.createAsync(checkNotNull(Looper.myLooper())),
+                Handler.createAsync(testableLooper.looper),
+                systemClock,
                 vibratorHelper,
                 configurationController,
                 latencyTracker,
@@ -83,7 +89,6 @@
         mBackPanelController.setLayoutParams(layoutParams)
         mBackPanelController.setBackCallback(backCallback)
         mBackPanelController.setIsLeftPanel(true)
-        testableLooper = TestableLooper.get(this)
         triggerThreshold = mBackPanelController.params.staticTriggerThreshold
     }
 
@@ -103,6 +108,7 @@
 
         assertThat(mBackPanelController.currentState)
             .isEqualTo(BackPanelController.GestureState.GONE)
+        verify(interactionJankMonitor, never()).begin(any())
     }
 
     @Test
@@ -110,23 +116,37 @@
         startTouch()
         // Move once to cross the touch slop
         continueTouch(START_X + touchSlop.toFloat() + 1)
+        assertThat(mBackPanelController.currentState)
+            .isEqualTo(BackPanelController.GestureState.ENTRY)
+        verify(interactionJankMonitor).cancel(Cuj.CUJ_BACK_PANEL_ARROW)
+        verify(interactionJankMonitor)
+            .begin(mBackPanelController.getBackPanelView(), Cuj.CUJ_BACK_PANEL_ARROW)
         // Move again to cross the back trigger threshold
         continueTouch(START_X + touchSlop + triggerThreshold + 1)
         // Wait threshold duration and hold touch past trigger threshold
-        Thread.sleep((MAX_DURATION_ENTRY_BEFORE_ACTIVE_ANIMATION + 1).toLong())
+        moveTimeForward((MAX_DURATION_ENTRY_BEFORE_ACTIVE_ANIMATION + 1).toLong())
         continueTouch(START_X + touchSlop + triggerThreshold + 1)
 
         assertThat(mBackPanelController.currentState)
             .isEqualTo(BackPanelController.GestureState.ACTIVE)
         verify(backCallback).setTriggerBack(true)
-        testableLooper.moveTimeForward(100)
-        testableLooper.processAllMessages()
+        moveTimeForward(100)
         verify(vibratorHelper)
             .performHapticFeedback(any(), eq(HapticFeedbackConstants.GESTURE_THRESHOLD_ACTIVATE))
         finishTouchActionUp(START_X + touchSlop + triggerThreshold + 1)
         assertThat(mBackPanelController.currentState)
             .isEqualTo(BackPanelController.GestureState.COMMITTED)
         verify(backCallback).triggerBack()
+
+        // Because the Handler that is typically used for transitioning the arrow state from
+        // COMMITTED to GONE is used as an animation-end-listener on a SpringAnimation,
+        // there is no way to meaningfully test that the state becomes GONE and that the tracked
+        // jank interaction is ended. So instead, manually trigger the failsafe, which does
+        // the same thing:
+        mBackPanelController.failsafeRunnable.run()
+        assertThat(mBackPanelController.currentState)
+            .isEqualTo(BackPanelController.GestureState.GONE)
+        verify(interactionJankMonitor).end(Cuj.CUJ_BACK_PANEL_ARROW)
     }
 
     @Test
@@ -134,19 +154,22 @@
         startTouch()
         // Move once to cross the touch slop
         continueTouch(START_X + touchSlop.toFloat() + 1)
+        assertThat(mBackPanelController.currentState)
+            .isEqualTo(BackPanelController.GestureState.ENTRY)
         // Move again to cross the back trigger threshold
         continueTouch(
             START_X + touchSlop + triggerThreshold -
                 mBackPanelController.params.deactivationTriggerThreshold
         )
         // Wait threshold duration and hold touch before trigger threshold
-        Thread.sleep((MAX_DURATION_ENTRY_BEFORE_ACTIVE_ANIMATION + 1).toLong())
+        moveTimeForward((MAX_DURATION_ENTRY_BEFORE_ACTIVE_ANIMATION + 1).toLong())
         continueTouch(
             START_X + touchSlop + triggerThreshold -
                 mBackPanelController.params.deactivationTriggerThreshold
         )
         clearInvocations(backCallback)
-        Thread.sleep(MIN_DURATION_ACTIVE_BEFORE_INACTIVE_ANIMATION)
+        moveTimeForward(MIN_DURATION_ACTIVE_BEFORE_INACTIVE_ANIMATION)
+
         // Move in the opposite direction to cross the deactivation threshold and cancel back
         continueTouch(START_X)
 
@@ -175,4 +198,10 @@
     private fun createMotionEvent(action: Int, x: Float, y: Float): MotionEvent {
         return MotionEvent.obtain(0L, 0L, action, x, y, 0)
     }
+
+    private fun moveTimeForward(millis: Long) {
+        systemClock.advanceTime(millis)
+        testableLooper.moveTimeForward(millis)
+        testableLooper.processAllMessages()
+    }
 }
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 db998f3..2017954 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -338,8 +338,6 @@
     @Mock
     private KeyguardStateController mKeyguardStateController;
     @Mock
-    private ScreenOffAnimationController mScreenOffAnimationController;
-    @Mock
     Transitions mTransitions;
     @Mock
     private Optional<OneHandedController> mOneHandedOptional;
@@ -491,7 +489,6 @@
                 mColorExtractor,
                 mDumpManager,
                 mKeyguardStateController,
-                mScreenOffAnimationController,
                 mAuthController,
                 () -> mShadeInteractor,
                 mShadeWindowLogger,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToOccludedTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToOccludedTransitionViewModelKosmos.kt
new file mode 100644
index 0000000..71ad3c6
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToOccludedTransitionViewModelKosmos.kt
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
+package com.android.systemui.keyguard.ui.viewmodel
+
+import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+
+val Kosmos.alternateBouncerToOccludedTransitionViewModel by Fixture {
+    AlternateBouncerToOccludedTransitionViewModel(
+        animationFlow = keyguardTransitionAnimationFlow,
+    )
+}
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 d4b7937..a81ac03 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
@@ -56,7 +56,6 @@
 import com.android.systemui.shade.domain.interactor.shadeInteractor
 import com.android.systemui.shade.shadeController
 import com.android.systemui.statusbar.notification.stack.domain.interactor.sharedNotificationContainerInteractor
-import com.android.systemui.statusbar.phone.screenOffAnimationController
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepository
 import com.android.systemui.statusbar.policy.data.repository.fakeDeviceProvisioningRepository
 import com.android.systemui.statusbar.policy.domain.interactor.deviceProvisioningInteractor
@@ -90,7 +89,6 @@
     val simBouncerInteractor by lazy { kosmos.simBouncerInteractor }
     val statusBarStateController by lazy { kosmos.statusBarStateController }
     val interactionJankMonitor by lazy { kosmos.interactionJankMonitor }
-    val screenOffAnimationController by lazy { kosmos.screenOffAnimationController }
     val fakeSceneContainerConfig by lazy { kosmos.sceneContainerConfig }
     val sceneInteractor by lazy { kosmos.sceneInteractor }
     val falsingCollector by lazy { kosmos.falsingCollector }
diff --git a/services/core/java/com/android/server/audio/AudioManagerShellCommand.java b/services/core/java/com/android/server/audio/AudioManagerShellCommand.java
index e330ed5..d669c8d 100644
--- a/services/core/java/com/android/server/audio/AudioManagerShellCommand.java
+++ b/services/core/java/com/android/server/audio/AudioManagerShellCommand.java
@@ -22,7 +22,10 @@
 import static android.media.AudioManager.ADJUST_UNMUTE;
 
 import android.content.Context;
+import android.media.AudioDeviceAttributes;
+import android.media.AudioDeviceVolumeManager;
 import android.media.AudioManager;
+import android.media.VolumeInfo;
 import android.os.ShellCommand;
 
 import java.io.PrintWriter;
@@ -60,6 +63,8 @@
                 return resetSoundDoseTimeout();
             case "set-volume":
                 return setVolume();
+            case "set-device-volume":
+                return setDeviceVolume();
             case "adj-mute":
                 return adjMute();
             case "adj-unmute":
@@ -97,6 +102,8 @@
         pw.println("    Resets the sound dose timeout used for momentary exposure");
         pw.println("  set-volume STREAM_TYPE VOLUME_INDEX");
         pw.println("    Sets the volume for STREAM_TYPE to VOLUME_INDEX");
+        pw.println("  set-device-volume STREAM_TYPE VOLUME_INDEX NATIVE_DEVICE_TYPE");
+        pw.println("    Sets for NATIVE_DEVICE_TYPE the STREAM_TYPE volume to VOLUME_INDEX");
         pw.println("  adj-mute STREAM_TYPE");
         pw.println("    mutes the STREAM_TYPE");
         pw.println("  adj-unmute STREAM_TYPE");
@@ -257,6 +264,23 @@
         return 0;
     }
 
+    private int setDeviceVolume() {
+        final Context context = mService.mContext;
+        final AudioDeviceVolumeManager advm = (AudioDeviceVolumeManager) context.getSystemService(
+                Context.AUDIO_DEVICE_VOLUME_SERVICE);
+        final int stream = readIntArg();
+        final int index = readIntArg();
+        final int device = readIntArg();
+
+        final VolumeInfo volume = new VolumeInfo.Builder(stream).setVolumeIndex(index).build();
+        final AudioDeviceAttributes ada = new AudioDeviceAttributes(
+                /*native type*/ device, /*address*/ "foo");
+        getOutPrintWriter().println(
+                "calling AudioDeviceVolumeManager.setDeviceVolume(" + volume + ", " + ada + ")");
+        advm.setDeviceVolume(volume, ada);
+        return 0;
+    }
+
     private int adjMute() {
         final Context context = mService.mContext;
         final AudioManager am = context.getSystemService(AudioManager.class);
diff --git a/services/core/java/com/android/server/location/gnss/GnssConfiguration.java b/services/core/java/com/android/server/location/gnss/GnssConfiguration.java
index a5939e9..a439f16 100644
--- a/services/core/java/com/android/server/location/gnss/GnssConfiguration.java
+++ b/services/core/java/com/android/server/location/gnss/GnssConfiguration.java
@@ -386,7 +386,7 @@
             configs = CarrierConfigManager.getDefaultConfig();
         }
         for (String configKey : configs.keySet()) {
-            if (configKey.startsWith(CarrierConfigManager.Gps.KEY_PREFIX)) {
+            if (configKey != null && configKey.startsWith(CarrierConfigManager.Gps.KEY_PREFIX)) {
                 String key = configKey
                         .substring(CarrierConfigManager.Gps.KEY_PREFIX.length())
                         .toUpperCase(Locale.ROOT);
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index bff3d39..9d4ab11 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -266,7 +266,6 @@
 import android.os.UserManager;
 import android.os.WorkSource;
 import android.permission.PermissionManager;
-import android.provider.DeviceConfig;
 import android.provider.Settings;
 import android.provider.Settings.Secure;
 import android.service.notification.Adjustment;
@@ -313,7 +312,6 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.compat.IPlatformCompat;
-import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
 import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags;
 import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags;
 import com.android.internal.logging.InstanceId;
@@ -704,7 +702,6 @@
     private ConditionProviders mConditionProviders;
     private NotificationUsageStats mUsageStats;
     private boolean mLockScreenAllowSecureNotifications = true;
-    boolean mSystemExemptFromDismissal = false;
     final ArrayMap<String, ArrayMap<Integer,
             RemoteCallbackList<ICallNotificationEventCallback>>>
             mCallNotificationEventCallbacks = new ArrayMap<>();
@@ -722,7 +719,6 @@
     private GroupHelper mGroupHelper;
     private int mAutoGroupAtCount;
     private boolean mIsTelevision;
-    private DeviceConfig.OnPropertiesChangedListener mDeviceConfigChangedListener;
     protected NotificationAttentionHelper mAttentionHelper;
 
     private int mWarnRemoteViewsSizeBytes;
@@ -973,18 +969,6 @@
     }
 
     protected void setDefaultAssistantForUser(int userId) {
-        String overrideDefaultAssistantString = DeviceConfig.getProperty(
-                DeviceConfig.NAMESPACE_SYSTEMUI,
-                SystemUiDeviceConfigFlags.NAS_DEFAULT_SERVICE);
-        if (overrideDefaultAssistantString != null) {
-            ArraySet<ComponentName> approved = mAssistants.queryPackageForServices(
-                    overrideDefaultAssistantString,
-                    MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE,
-                    userId);
-            for (int i = 0; i < approved.size(); i++) {
-                if (allowAssistant(userId, approved.valueAt(i))) return;
-            }
-        }
         ArraySet<ComponentName> defaults = mAssistants.getDefaultComponents();
         // We should have only one default assistant by default
         // allowAssistant should execute once in practice
@@ -2670,10 +2654,6 @@
         mStatsManager.clearPullAtomCallback(DND_MODE_RULE);
         mAppOps.stopWatchingMode(mAppOpsListener);
         mAlarmManager.cancelAll();
-
-        if (mDeviceConfigChangedListener != null) {
-            DeviceConfig.removeOnPropertiesChangedListener(mDeviceConfigChangedListener);
-        }
     }
 
     protected String[] getStringArrayResource(int key) {
@@ -2744,27 +2724,6 @@
         publishLocalService(NotificationManagerInternal.class, mInternalService);
     }
 
-    void registerDeviceConfigChange() {
-        mDeviceConfigChangedListener = properties -> {
-            if (!DeviceConfig.NAMESPACE_SYSTEMUI.equals(properties.getNamespace())) {
-                return;
-            }
-            for (String name : properties.getKeyset()) {
-                if (SystemUiDeviceConfigFlags.NAS_DEFAULT_SERVICE.equals(name)) {
-                    mAssistants.resetDefaultAssistantsIfNecessary();
-                }
-            }
-        };
-        mSystemExemptFromDismissal = DeviceConfig.getBoolean(
-                DeviceConfig.NAMESPACE_DEVICE_POLICY_MANAGER,
-                /* name= */ "application_exemptions",
-                /* defaultValue= */ true);
-        DeviceConfig.addOnPropertiesChangedListener(
-                DeviceConfig.NAMESPACE_SYSTEMUI,
-                new HandlerExecutor(mHandler),
-                mDeviceConfigChangedListener);
-    }
-
     private void registerNotificationPreferencesPullers() {
         mPullAtomCallback = new StatsPullAtomCallbackImpl();
         mStatsManager.setPullAtomCallback(
@@ -2938,7 +2897,6 @@
             mAssistants.onBootPhaseAppsCanStart();
             mConditionProviders.onBootPhaseAppsCanStart();
             mHistoryManager.onBootPhaseAppsCanStart();
-            registerDeviceConfigChange();
             migrateDefaultNAS();
             maybeShowInitialReviewPermissionsNotification();
 
@@ -7738,7 +7696,7 @@
             return true;
         }
         // Check if an app has been given system exemption
-        return mSystemExemptFromDismissal && mAppOps.checkOpNoThrow(
+        return mAppOps.checkOpNoThrow(
                 AppOpsManager.OP_SYSTEM_EXEMPT_FROM_DISMISSIBLE_NOTIFICATIONS, ai.uid,
                 ai.packageName) == MODE_ALLOWED;
     }
diff --git a/services/core/java/com/android/server/wm/DesktopModeLaunchParamsModifier.java b/services/core/java/com/android/server/wm/DesktopModeLaunchParamsModifier.java
index f04b4af..8069a93 100644
--- a/services/core/java/com/android/server/wm/DesktopModeLaunchParamsModifier.java
+++ b/services/core/java/com/android/server/wm/DesktopModeLaunchParamsModifier.java
@@ -48,8 +48,15 @@
     /**
      * Flag to indicate whether to restrict desktop mode to supported devices.
      */
+    @VisibleForTesting
+    static final String ENFORCE_DEVICE_RESTRICTIONS_KEY =
+            "persist.wm.debug.desktop_mode_enforce_device_restrictions";
+
+    /**
+     * Flag to indicate whether to restrict desktop mode to supported devices.
+     */
     private static final boolean ENFORCE_DEVICE_RESTRICTIONS = SystemProperties.getBoolean(
-            "persist.wm.debug.desktop_mode_enforce_device_restrictions", true);
+            ENFORCE_DEVICE_RESTRICTIONS_KEY, true);
 
     private StringBuilder mLogBuilder;
 
@@ -178,24 +185,24 @@
      * Return {@code true} if desktop mode should be restricted to supported devices.
      */
     @VisibleForTesting
-    public boolean enforceDeviceRestrictions() {
+    static boolean enforceDeviceRestrictions() {
         return ENFORCE_DEVICE_RESTRICTIONS;
     }
 
     /**
      * Return {@code true} if the current device supports desktop mode.
      */
+    // TODO(b/337819319): use a companion object instead.
     @VisibleForTesting
-    public boolean isDesktopModeSupported(@NonNull Context context) {
+    static boolean isDesktopModeSupported(@NonNull Context context) {
         return context.getResources().getBoolean(R.bool.config_isDesktopModeSupported);
     }
 
     /**
      * Return {@code true} if desktop mode can be entered on the current device.
      */
-    boolean canEnterDesktopMode(@NonNull Context context) {
+    static boolean canEnterDesktopMode(@NonNull Context context) {
         return isDesktopModeEnabled()
                 && (!enforceDeviceRestrictions() || isDesktopModeSupported(context));
     }
-
 }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 6458eac..d555f1a 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -666,15 +666,6 @@
             DELEGATION_CERT_SELECTION,
     });
 
-    /**
-     * System property whose value indicates whether the device is fully owned by an organization:
-     * it can be either a device owner device, or a device with an organization-owned managed
-     * profile.
-     *
-     * <p>The state is stored as a Boolean string.
-     */
-    private static final String PROPERTY_ORGANIZATION_OWNED = "ro.organization_owned";
-
     private static final int STATUS_BAR_DISABLE_MASK =
             StatusBarManager.DISABLE_EXPAND |
             StatusBarManager.DISABLE_NOTIFICATION_ICONS |
@@ -2356,7 +2347,6 @@
     void loadOwners() {
         synchronized (getLockObject()) {
             mOwners.load();
-            setDeviceOwnershipSystemPropertyLocked();
             if (mOwners.hasDeviceOwner()) {
                 setGlobalSettingDeviceOwnerType(
                         mOwners.getDeviceOwnerType(mOwners.getDeviceOwnerPackageName()));
@@ -2720,27 +2710,6 @@
                 + defaultRestrictions);
     }
 
-    private void setDeviceOwnershipSystemPropertyLocked() {
-        final boolean deviceProvisioned =
-                mInjector.settingsGlobalGetInt(Settings.Global.DEVICE_PROVISIONED, 0) != 0;
-        final boolean hasDeviceOwner = mOwners.hasDeviceOwner();
-        final boolean hasOrgOwnedProfile = isOrganizationOwnedDeviceWithManagedProfile();
-        // If the device is not provisioned and there is currently no management, do not set the
-        // read-only system property yet, since device owner / org-owned profile may still be
-        // provisioned.
-        if (!hasDeviceOwner && !hasOrgOwnedProfile && !deviceProvisioned) {
-            return;
-        }
-        final String value = Boolean.toString(hasDeviceOwner || hasOrgOwnedProfile);
-        final String currentVal = mInjector.systemPropertiesGet(PROPERTY_ORGANIZATION_OWNED, null);
-        if (TextUtils.isEmpty(currentVal)) {
-            Slogf.i(LOG_TAG, "Set ro.organization_owned property to " + value);
-            mInjector.systemPropertiesSet(PROPERTY_ORGANIZATION_OWNED, value);
-        } else if (!value.equals(currentVal)) {
-            Slogf.w(LOG_TAG, "Cannot change existing ro.organization_owned to " + value);
-        }
-    }
-
     private void maybeStartSecurityLogMonitorOnActivityManagerReady() {
         if (!mInjector.securityLogIsLoggingEnabled()) {
             return;
@@ -9447,7 +9416,6 @@
 
             mOwners.setDeviceOwner(admin, userId);
             mOwners.writeDeviceOwner();
-            setDeviceOwnershipSystemPropertyLocked();
 
             //TODO(b/180371154): when provisionFullyManagedDevice is used in tests, remove this
             // hard-coded default value setting.
@@ -15303,8 +15271,6 @@
     private class SetupContentObserver extends ContentObserver {
         private final Uri mUserSetupComplete = Settings.Secure.getUriFor(
                 Settings.Secure.USER_SETUP_COMPLETE);
-        private final Uri mDeviceProvisioned = Settings.Global.getUriFor(
-                Settings.Global.DEVICE_PROVISIONED);
         private final Uri mPaired = Settings.Secure.getUriFor(Settings.Secure.DEVICE_PAIRED);
         private final Uri mDefaultImeChanged = Settings.Secure.getUriFor(
                 Settings.Secure.DEFAULT_INPUT_METHOD);
@@ -15318,7 +15284,6 @@
 
         void register() {
             mInjector.registerContentObserver(mUserSetupComplete, false, this, UserHandle.USER_ALL);
-            mInjector.registerContentObserver(mDeviceProvisioned, false, this, UserHandle.USER_ALL);
             if (mIsWatch) {
                 mInjector.registerContentObserver(mPaired, false, this, UserHandle.USER_ALL);
             }
@@ -15334,12 +15299,6 @@
         public void onChange(boolean selfChange, Uri uri, int userId) {
             if (mUserSetupComplete.equals(uri) || (mIsWatch && mPaired.equals(uri))) {
                 updateUserSetupCompleteAndPaired();
-            } else if (mDeviceProvisioned.equals(uri)) {
-                synchronized (getLockObject()) {
-                    // Set PROPERTY_DEVICE_OWNER_PRESENT, for the SUW case where setting the property
-                    // is delayed until device is marked as provisioned.
-                    setDeviceOwnershipSystemPropertyLocked();
-                }
             } else if (mDefaultImeChanged.equals(uri)) {
                 synchronized (getLockObject()) {
                     if (mUserIdsWithPendingChangesByOwner.contains(userId)) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java
index a0d9be54..eeb4976 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java
@@ -16,6 +16,9 @@
 
 package com.android.server.devicepolicy;
 
+import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
+import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
@@ -185,6 +188,9 @@
         }
     }
 
+    // TODO: when a local policy exists for a user, this callback will be invoked for this user
+    // individually as well as for USER_ALL. This can be optimized by separating local and global
+    // enforcement in the policy engine.
     static boolean setUserControlDisabledPackages(
             @Nullable Set<String> packages, Context context, int userId, PolicyKey policyKey) {
         Binder.withCleanCallingIdentity(() -> {
@@ -201,20 +207,35 @@
                     return;
                 }
                 final AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class);
-                for (var pkg : packages) {
-                    final var appInfo = pmi.getApplicationInfo(pkg,
-                            PackageManager.MATCH_DIRECT_BOOT_AWARE
-                                    | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
-                            Process.myUid(), userId);
-                    if (appInfo != null) {
-                        DevicePolicyManagerService.setBgUsageAppOp(appOpsManager, appInfo);
-                    }
-                }
+                resolveUsers(userId).forEach(
+                        user -> setBgUsageAppOp(packages, pmi, user, appOpsManager));
             }
         });
         return true;
     }
 
+    /** Handles USER_ALL expanding it into the list of all intact users. */
+    private static List<Integer> resolveUsers(int userId) {
+        if (userId == UserHandle.USER_ALL) {
+            UserManagerInternal userManager = LocalServices.getService(UserManagerInternal.class);
+            return userManager.getUsers(/* excludeDying= */ true)
+                    .stream().map(ui -> ui.id).toList();
+        } else {
+            return List.of(userId);
+        }
+    }
+
+    private static void setBgUsageAppOp(Set<String> packages, PackageManagerInternal pmi,
+            int userId, AppOpsManager appOpsManager) {
+        for (var pkg : packages) {
+            int packageFlags = MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE;
+            final var appInfo = pmi.getApplicationInfo(pkg, packageFlags, Process.myUid(), userId);
+            if (appInfo != null) {
+                DevicePolicyManagerService.setBgUsageAppOp(appOpsManager, appInfo);
+            }
+        }
+    }
+
     static boolean addPersistentPreferredActivity(
             @Nullable ComponentName preferredActivity, @NonNull Context context, int userId,
             @NonNull PolicyKey policyKey) {
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index ce7a0a0..74d8433 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -243,7 +243,6 @@
 import android.platform.test.flag.junit.FlagsParameterization;
 import android.platform.test.flag.junit.SetFlagsRule;
 import android.platform.test.rule.LimitDevicesRule;
-import android.provider.DeviceConfig;
 import android.provider.MediaStore;
 import android.provider.Settings;
 import android.service.notification.Adjustment;
@@ -280,7 +279,6 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.R;
-import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
 import com.android.internal.config.sysui.TestableFlagResolver;
 import com.android.internal.logging.InstanceIdSequence;
 import com.android.internal.logging.InstanceIdSequenceFake;
@@ -602,7 +600,9 @@
         when(mContext.getContentResolver()).thenReturn(cr);
         doNothing().when(cr).registerContentObserver(any(), anyBoolean(), any(), anyInt());
 
-        setDpmAppOppsExemptFromDismissal(false);
+        when(mAppOpsManager.checkOpNoThrow(
+                AppOpsManager.OP_SYSTEM_EXEMPT_FROM_DISMISSIBLE_NOTIFICATIONS, mUid,
+                mPkg)).thenReturn(AppOpsManager.MODE_IGNORED);
 
         // Use this testable looper.
         mTestableLooper = TestableLooper.get(this);
@@ -900,7 +900,6 @@
     @After
     public void tearDown() throws Exception {
         if (mFile != null) mFile.delete();
-        clearDeviceConfig();
 
         if (mActivityIntent != null) {
             mActivityIntent.cancel();
@@ -1200,19 +1199,6 @@
         return answers;
     }
 
-    private void clearDeviceConfig() {
-        DeviceConfig.resetToDefaults(
-                Settings.RESET_MODE_PACKAGE_DEFAULTS, DeviceConfig.NAMESPACE_SYSTEMUI);
-    }
-
-    private void setDefaultAssistantInDeviceConfig(String componentName) {
-        DeviceConfig.setProperty(
-                DeviceConfig.NAMESPACE_SYSTEMUI,
-                SystemUiDeviceConfigFlags.NAS_DEFAULT_SERVICE,
-                componentName,
-                false);
-    }
-
     private Notification.Builder getMessageStyleNotifBuilder(boolean addBubbleMetadata,
             String groupKey, boolean isSummary, boolean mutable) {
         // Give it a person
@@ -9092,7 +9078,6 @@
 
     @Test
     public void setDefaultAssistantForUser_fromConfigXml() {
-        clearDeviceConfig();
         ComponentName xmlConfig = new ComponentName("config", "xml");
         ArraySet<ComponentName> components = new ArraySet<>(Arrays.asList(xmlConfig));
         when(mResources
@@ -9115,51 +9100,6 @@
     }
 
     @Test
-    public void setDefaultAssistantForUser_fromDeviceConfig() {
-        ComponentName xmlConfig = new ComponentName("xml", "config");
-        ComponentName deviceConfig = new ComponentName("device", "config");
-        setDefaultAssistantInDeviceConfig(deviceConfig.flattenToString());
-        when(mResources
-                .getString(com.android.internal.R.string.config_defaultAssistantAccessComponent))
-                .thenReturn(xmlConfig.flattenToString());
-        when(mContext.getResources()).thenReturn(mResources);
-        when(mAssistants.queryPackageForServices(eq(null), anyInt(), anyInt()))
-                .thenReturn(new ArraySet<>(Arrays.asList(xmlConfig, deviceConfig)));
-        when(mAssistants.getDefaultComponents())
-                .thenReturn(new ArraySet<>(Arrays.asList(deviceConfig)));
-        mService.setNotificationAssistantAccessGrantedCallback(
-                mNotificationAssistantAccessGrantedCallback);
-
-        mService.setDefaultAssistantForUser(0);
-
-        verify(mNotificationAssistantAccessGrantedCallback)
-                .onGranted(eq(deviceConfig), eq(0), eq(true), eq(false));
-    }
-
-    @Test
-    public void setDefaultAssistantForUser_deviceConfigInvalid() {
-        ComponentName xmlConfig = new ComponentName("xml", "config");
-        ComponentName deviceConfig = new ComponentName("device", "config");
-        setDefaultAssistantInDeviceConfig(deviceConfig.flattenToString());
-        when(mResources
-                .getString(com.android.internal.R.string.config_defaultAssistantAccessComponent))
-                .thenReturn(xmlConfig.flattenToString());
-        when(mContext.getResources()).thenReturn(mResources);
-        // Only xmlConfig is valid, deviceConfig is not.
-        when(mAssistants.queryPackageForServices(eq(null), anyInt(), eq(0)))
-                .thenReturn(new ArraySet<>(Collections.singleton(xmlConfig)));
-        when(mAssistants.getDefaultComponents())
-                .thenReturn(new ArraySet<>(Arrays.asList(xmlConfig, deviceConfig)));
-        mService.setNotificationAssistantAccessGrantedCallback(
-                mNotificationAssistantAccessGrantedCallback);
-
-        mService.setDefaultAssistantForUser(0);
-
-        verify(mNotificationAssistantAccessGrantedCallback)
-                .onGranted(eq(xmlConfig), eq(0), eq(true), eq(false));
-    }
-
-    @Test
     public void clearMultipleDefaultAssistantPackagesShouldEnableOnlyOne() throws RemoteException {
         ArrayMap<Boolean, ArrayList<ComponentName>> changedListeners =
                 generateResetComponentValues();
@@ -11006,7 +10946,6 @@
         tr.addOverride(com.android.internal.R.string.config_defaultListenerAccessPackages, "");
         tr.addOverride(com.android.internal.R.string.config_defaultDndAccessPackages, "");
         tr.addOverride(com.android.internal.R.string.config_defaultAssistantAccessComponent, "");
-        setDefaultAssistantInDeviceConfig("");
 
         mService.loadDefaultApprovedServices(USER_SYSTEM);
 
@@ -13425,7 +13364,6 @@
             throws Exception {
         when(mDevicePolicyManager.isActiveDeviceOwner(mUid)).thenReturn(true);
         // Given: a notification has the flag FLAG_ONGOING_EVENT set
-        setDpmAppOppsExemptFromDismissal(false);
         Notification n = new Notification.Builder(mContext, "test")
                 .setOngoing(true)
                 .build();
@@ -13451,7 +13389,6 @@
                 AppOpsManager.OP_SYSTEM_EXEMPT_FROM_DISMISSIBLE_NOTIFICATIONS, mUid,
                 mPkg)).thenReturn(AppOpsManager.MODE_ALLOWED);
         // Given: a notification has the flag FLAG_ONGOING_EVENT set
-        setDpmAppOppsExemptFromDismissal(true);
         Notification n = new Notification.Builder(mContext, "test")
                 .setOngoing(true)
                 .build();
@@ -13459,8 +13396,8 @@
         // When: fix the notification with NotificationManagerService
         mService.fixNotification(n, mPkg, "tag", 9, 0, mUid, NOT_FOREGROUND_SERVICE, true);
 
-        // Then: the notification's flag FLAG_NO_DISMISS should be cleared
-        assertEquals(0, n.flags & Notification.FLAG_NO_DISMISS);
+        // Then: the notification's flag FLAG_NO_DISMISS should be set
+        assertNotSame(0, n.flags & Notification.FLAG_NO_DISMISS);
     }
 
     @Test
@@ -13468,9 +13405,8 @@
             throws Exception {
         when(mAppOpsManager.checkOpNoThrow(
                 AppOpsManager.OP_SYSTEM_EXEMPT_FROM_DISMISSIBLE_NOTIFICATIONS, mUid,
-                mPkg)).thenReturn(AppOpsManager.MODE_ALLOWED);
+                mPkg)).thenReturn(AppOpsManager.MODE_IGNORED);
         // Given: a notification has the flag FLAG_ONGOING_EVENT set
-        setDpmAppOppsExemptFromDismissal(false);
         Notification n = new Notification.Builder(mContext, "test")
                 .setOngoing(true)
                 .build();
@@ -15551,14 +15487,6 @@
                 PendingIntent.FLAG_MUTABLE);
     }
 
-    private void setDpmAppOppsExemptFromDismissal(boolean isOn) {
-        DeviceConfig.setProperty(
-                DeviceConfig.NAMESPACE_DEVICE_POLICY_MANAGER,
-                /* name= */ "application_exemptions",
-                String.valueOf(isOn),
-                /* makeDefault= */ false);
-    }
-
     private void allowTestPackageToToast() throws Exception {
         assertWithMessage("toast queue").that(mService.mToastQueue).isEmpty();
         mService.isSystemUid = false;
diff --git a/services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java b/services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java
index 353ba01..89ae802 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java
@@ -22,6 +22,7 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 
 import static com.android.server.wm.DesktopModeLaunchParamsModifier.DESKTOP_MODE_INITIAL_BOUNDS_SCALE;
+import static com.android.server.wm.DesktopModeLaunchParamsModifier.ENFORCE_DEVICE_RESTRICTIONS_KEY;
 import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.PHASE_BOUNDS;
 import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.PHASE_DISPLAY;
 import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.RESULT_CONTINUE;
@@ -29,18 +30,21 @@
 import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.RESULT_SKIP;
 
 import static org.junit.Assert.assertEquals;
-import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
 
+import android.content.res.Resources;
 import android.graphics.Rect;
+import android.os.SystemProperties;
 import android.platform.test.annotations.DisableFlags;
 import android.platform.test.annotations.EnableFlags;
 import android.platform.test.annotations.Presubmit;
 
 import androidx.test.filters.SmallTest;
 
+import com.android.internal.R;
 import com.android.server.wm.LaunchParamsController.LaunchParamsModifier.Result;
 import com.android.window.flags.Flags;
 
@@ -48,6 +52,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
+import org.mockito.Mockito;
 
 /**
  * Tests for desktop mode task bounds.
@@ -71,8 +76,6 @@
     @Before
     public void setUp() throws Exception {
         mActivity = new ActivityBuilder(mAtm).build();
-        mTarget = spy(new DesktopModeLaunchParamsModifier(mContext));
-        doReturn(true).when(mTarget).isDesktopModeSupported(any());
         mCurrent = new LaunchParamsController.LaunchParams();
         mCurrent.reset();
         mResult = new LaunchParamsController.LaunchParams();
@@ -82,20 +85,26 @@
     @Test
     @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
     public void testReturnsContinueIfDesktopWindowingIsDisabled() {
+        setupDesktopModeLaunchParamsModifier();
+
         assertEquals(RESULT_CONTINUE, new CalculateRequestBuilder().setTask(null).calculate());
     }
 
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
     public void testReturnsContinueIfDesktopWindowingIsEnabledOnUnsupportedDevice() {
-        doReturn(false).when(mTarget).isDesktopModeSupported(any());
+        setupDesktopModeLaunchParamsModifier(/*isDesktopModeSupported=*/ false,
+                /*enforceDeviceRestrictions=*/ true);
+
         assertEquals(RESULT_CONTINUE, new CalculateRequestBuilder().setTask(null).calculate());
     }
 
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
     public void testReturnsDoneIfDesktopWindowingIsEnabledAndUnsupportedDeviceOverridden() {
-        doReturn(false).when(mTarget).enforceDeviceRestrictions();
+        setupDesktopModeLaunchParamsModifier(/*isDesktopModeSupported=*/ true,
+                /*enforceDeviceRestrictions=*/ false);
+
         final Task task = new TaskBuilder(mSupervisor).build();
         assertEquals(RESULT_DONE, new CalculateRequestBuilder().setTask(task).calculate());
     }
@@ -103,12 +112,16 @@
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
     public void testReturnsSkipIfTaskIsNull() {
+        setupDesktopModeLaunchParamsModifier();
+
         assertEquals(RESULT_SKIP, new CalculateRequestBuilder().setTask(null).calculate());
     }
 
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
     public void testReturnsSkipIfNotBoundsPhase() {
+        setupDesktopModeLaunchParamsModifier();
+
         final Task task = new TaskBuilder(mSupervisor).build();
         assertEquals(RESULT_SKIP, new CalculateRequestBuilder().setTask(task).setPhase(
                 PHASE_DISPLAY).calculate());
@@ -117,6 +130,8 @@
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
     public void testReturnsSkipIfTaskNotUsingActivityTypeStandardOrUndefined() {
+        setupDesktopModeLaunchParamsModifier();
+
         final Task task = new TaskBuilder(mSupervisor).setActivityType(
                 ACTIVITY_TYPE_ASSISTANT).build();
         assertEquals(RESULT_SKIP, new CalculateRequestBuilder().setTask(task).calculate());
@@ -125,6 +140,8 @@
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
     public void testReturnsDoneIfTaskUsingActivityTypeStandard() {
+        setupDesktopModeLaunchParamsModifier();
+
         final Task task = new TaskBuilder(mSupervisor).setActivityType(
                 ACTIVITY_TYPE_STANDARD).build();
         assertEquals(RESULT_DONE, new CalculateRequestBuilder().setTask(task).calculate());
@@ -133,6 +150,8 @@
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
     public void testReturnsDoneIfTaskUsingActivityTypeUndefined() {
+        setupDesktopModeLaunchParamsModifier();
+
         final Task task = new TaskBuilder(mSupervisor).setActivityType(
                 ACTIVITY_TYPE_UNDEFINED).build();
         assertEquals(RESULT_DONE, new CalculateRequestBuilder().setTask(task).calculate());
@@ -141,6 +160,8 @@
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
     public void testReturnsSkipIfCurrentParamsHasBounds() {
+        setupDesktopModeLaunchParamsModifier();
+
         final Task task = new TaskBuilder(mSupervisor).setActivityType(
                 ACTIVITY_TYPE_STANDARD).build();
         mCurrent.mBounds.set(/* left */ 0, /* top */ 0, /* right */ 100, /* bottom */ 100);
@@ -150,6 +171,8 @@
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
     public void testUsesDefaultBounds() {
+        setupDesktopModeLaunchParamsModifier();
+
         final Task task = new TaskBuilder(mSupervisor).setActivityType(
                 ACTIVITY_TYPE_STANDARD).build();
         final int displayHeight = 1600;
@@ -165,6 +188,8 @@
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
     public void testUsesDisplayAreaAndWindowingModeFromSource() {
+        setupDesktopModeLaunchParamsModifier();
+
         final Task task = new TaskBuilder(mSupervisor).setActivityType(
                 ACTIVITY_TYPE_STANDARD).build();
         TaskDisplayArea mockTaskDisplayArea = mock(TaskDisplayArea.class);
@@ -176,6 +201,24 @@
         assertEquals(WINDOWING_MODE_FREEFORM, mResult.mWindowingMode);
     }
 
+    private void setupDesktopModeLaunchParamsModifier() {
+        setupDesktopModeLaunchParamsModifier(/*isDesktopModeSupported=*/ true,
+                /*enforceDeviceRestrictions=*/ true);
+    }
+
+    private void setupDesktopModeLaunchParamsModifier(boolean isDesktopModeSupported,
+            boolean enforceDeviceRestrictions) {
+        Resources mockResources = Mockito.mock(Resources.class);
+        when(mockResources.getBoolean(eq(R.bool.config_isDesktopModeSupported)))
+                .thenReturn(isDesktopModeSupported);
+        doReturn(mockResources).when(mContext).getResources();
+
+        SystemProperties.set(ENFORCE_DEVICE_RESTRICTIONS_KEY,
+                String.valueOf(enforceDeviceRestrictions));
+
+        mTarget = new DesktopModeLaunchParamsModifier(mContext);
+    }
+
     private class CalculateRequestBuilder {
         private Task mTask;
         private int mPhase = PHASE_BOUNDS;
diff --git a/tests/FlickerTests/IME/AndroidTestTemplate.xml b/tests/FlickerTests/IME/AndroidTestTemplate.xml
index 988f76f..38442db 100644
--- a/tests/FlickerTests/IME/AndroidTestTemplate.xml
+++ b/tests/FlickerTests/IME/AndroidTestTemplate.xml
@@ -10,6 +10,8 @@
     <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
         <!-- keeps the screen on during tests -->
         <option name="screen-always-on" value="on"/>
+        <!-- enable AOD -->
+        <option name="set-secure-setting" key="doze_always_on" value="1" />
         <!-- prevents the phone from restarting -->
         <option name="force-skip-system-props" value="true"/>
         <!-- set WM tracing verbose level to all -->
diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnUnlockScreenTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnUnlockScreenTest.kt
new file mode 100644
index 0000000..31506b5
--- /dev/null
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnUnlockScreenTest.kt
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.ime
+
+import android.platform.test.annotations.Presubmit
+import android.platform.test.rule.UnlockScreenRule
+import android.tools.Rotation
+import android.tools.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.flicker.legacy.FlickerBuilder
+import android.tools.flicker.legacy.LegacyFlickerTest
+import android.tools.flicker.legacy.LegacyFlickerTestFactory
+import android.tools.traces.component.ComponentNameMatcher
+import com.android.server.wm.flicker.BaseTest
+import com.android.server.wm.flicker.helpers.ImeAppHelper
+import org.junit.FixMethodOrder
+import org.junit.Ignore
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test IME window closing on lock and opening on screen unlock. To run this test: `atest
+ * FlickerTests:CloseImeWindowToHomeTest`
+ */
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class ShowImeOnUnlockScreenTest(flicker: LegacyFlickerTest) : BaseTest(flicker) {
+    private val testApp = ImeAppHelper(instrumentation)
+    private val imeOrSnapshot = ComponentNameMatcher.IME.or(ComponentNameMatcher.IME_SNAPSHOT)
+
+    /** {@inheritDoc} */
+    override val transition: FlickerBuilder.() -> Unit = {
+        setup {
+            tapl.expectedRotationCheckEnabled = false
+            testApp.launchViaIntent(wmHelper)
+            testApp.openIME(wmHelper)
+        }
+        transitions {
+            device.sleep()
+            wmHelper.StateSyncBuilder().withoutTopVisibleAppWindows().waitForAndVerify()
+            UnlockScreenRule.unlockScreen(device)
+            wmHelper.StateSyncBuilder().withImeShown().waitForAndVerify()
+        }
+        teardown { testApp.exit(wmHelper) }
+    }
+
+    @Presubmit
+    @Test
+    fun imeAndAppAnimateTogetherWhenLockingAndUnlocking() {
+        flicker.assertLayers {
+            this.isVisible(testApp)
+                .isVisible(imeOrSnapshot)
+                .then()
+                .isInvisible(testApp)
+                .isInvisible(imeOrSnapshot)
+                .then()
+                .isVisible(testApp)
+                .isVisible(imeOrSnapshot)
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Test
+    @Ignore("Not applicable to this CUJ. Display turns off during transition")
+    override fun navBarWindowIsAlwaysVisible() {}
+
+    /** {@inheritDoc} */
+    @Test
+    @Ignore("Not applicable to this CUJ. Display turns off during transition")
+    override fun statusBarWindowIsAlwaysVisible() {}
+
+    /** {@inheritDoc} */
+    @Test
+    @Ignore("Not applicable to this CUJ. Display turns off during transition")
+    override fun taskBarWindowIsAlwaysVisible() {}
+
+    companion object {
+        @Parameterized.Parameters(name = "{0}")
+        @JvmStatic
+        fun getParams() =
+            LegacyFlickerTestFactory.nonRotationTests(
+                supportedRotations = listOf(Rotation.ROTATION_0)
+            )
+    }
+}