Merge "Fix warnings in DesktopTasksController" into main
diff --git a/Android.bp b/Android.bp
index bfe749a..26d0d65 100644
--- a/Android.bp
+++ b/Android.bp
@@ -144,9 +144,6 @@
         // For the generated R.java and Manifest.java
         ":framework-res{.aapt.srcjar}",
 
-        // Java/AIDL sources to be moved out to CrashRecovery module
-        ":framework-crashrecovery-sources",
-
         // etc.
         ":framework-javastream-protos",
         ":statslog-framework-java-gen", // FrameworkStatsLog.java
@@ -432,7 +429,12 @@
     name: "framework-non-updatable-unbundled-impl-libs",
     static_libs: [
         "framework-location.impl",
-    ],
+    ] + select(soong_config_variable("ANDROID", "release_crashrecovery_module"), {
+        "true": [],
+        default: [
+            "framework-platformcrashrecovery.impl",
+        ],
+    }),
     sdk_version: "core_platform",
     installable: false,
 }
@@ -565,7 +567,12 @@
         "documents-ui-compat-config",
         "calendar-provider-compat-config",
         "contacts-provider-platform-compat-config",
-    ],
+    ] + select(soong_config_variable("ANDROID", "release_crashrecovery_module"), {
+        "true": [],
+        default: [
+            "framework-platformcrashrecovery-compat-config",
+        ],
+    }),
 }
 
 platform_compat_config {
diff --git a/api/Android.bp b/api/Android.bp
index 3c92cb2..ff674c7 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -101,7 +101,9 @@
         "true": [
             "framework-crashrecovery",
         ],
-        default: [],
+        default: [
+            "framework-platformcrashrecovery",
+        ],
     }) + select(release_flag("RELEASE_RANGING_STACK"), {
         true: [
             "framework-ranging",
@@ -436,6 +438,7 @@
     impl_library_visibility: ["//frameworks/base"],
     defaults_visibility: [
         "//frameworks/base/location",
+        "//frameworks/base/packages/CrashRecovery/framework",
         "//frameworks/base/nfc",
     ],
     plugins: ["error_prone_android_framework"],
diff --git a/api/api.go b/api/api.go
index 29083df..f32bdc3 100644
--- a/api/api.go
+++ b/api/api.go
@@ -28,6 +28,7 @@
 const i18n = "i18n.module.public.api"
 const virtualization = "framework-virtualization"
 const location = "framework-location"
+const platformCrashrecovery = "framework-platformcrashrecovery"
 
 var core_libraries_modules = []string{art, conscrypt, i18n}
 
@@ -39,7 +40,7 @@
 // APIs.
 // In addition, the modules in this list are allowed to contribute to test APIs
 // stubs.
-var non_updatable_modules = []string{virtualization, location}
+var non_updatable_modules = []string{virtualization, location, platformCrashrecovery}
 
 // The intention behind this soong plugin is to generate a number of "merged"
 // API-related modules that would otherwise require a large amount of very
diff --git a/api/api_test.go b/api/api_test.go
index 166f053..28109b5e 100644
--- a/api/api_test.go
+++ b/api/api_test.go
@@ -78,10 +78,7 @@
 		"stub-annotations",
 	}
 
-	extraSdkLibraryModules := []string{
-		"framework-virtualization",
-		"framework-location",
-	}
+	extraSdkLibraryModules := non_updatable_modules
 
 	extraSystemModules := []string{
 		"core-public-stubs-system-modules",
@@ -184,10 +181,10 @@
 
 func TestCombinedApisDefaults(t *testing.T) {
 
+	testNonUpdatableModules := append(non_updatable_modules, "framework-foo", "framework-bar")
 	result := android.GroupFixturePreparers(
 		prepareForTestWithCombinedApis,
-		java.FixtureWithLastReleaseApis(
-			"framework-location", "framework-virtualization", "framework-foo", "framework-bar"),
+		java.FixtureWithLastReleaseApis(testNonUpdatableModules...),
 		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
 			variables.VendorVars = map[string]map[string]string{
 				"boolean_var": {
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index d1a9e67..491bca2 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -14086,31 +14086,6 @@
 
 }
 
-package android.service.watchdog {
-
-  public abstract class ExplicitHealthCheckService extends android.app.Service {
-    ctor public ExplicitHealthCheckService();
-    method public final void notifyHealthCheckPassed(@NonNull String);
-    method @NonNull public final android.os.IBinder onBind(@NonNull android.content.Intent);
-    method public abstract void onCancelHealthCheck(@NonNull String);
-    method @NonNull public abstract java.util.List<java.lang.String> onGetRequestedPackages();
-    method @NonNull public abstract java.util.List<android.service.watchdog.ExplicitHealthCheckService.PackageConfig> onGetSupportedPackages();
-    method public abstract void onRequestHealthCheck(@NonNull String);
-    field public static final String BIND_PERMISSION = "android.permission.BIND_EXPLICIT_HEALTH_CHECK_SERVICE";
-    field public static final String SERVICE_INTERFACE = "android.service.watchdog.ExplicitHealthCheckService";
-  }
-
-  public static final class ExplicitHealthCheckService.PackageConfig implements android.os.Parcelable {
-    ctor public ExplicitHealthCheckService.PackageConfig(@NonNull String, long);
-    method public int describeContents();
-    method public long getHealthCheckTimeoutMillis();
-    method @NonNull public String getPackageName();
-    method public void writeToParcel(android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.service.watchdog.ExplicitHealthCheckService.PackageConfig> CREATOR;
-  }
-
-}
-
 package android.service.wearable {
 
   @FlaggedApi("android.app.wearable.enable_data_request_observer_api") public interface WearableSensingDataRequester {
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 58ab073..a0c4fdb 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -3301,14 +3301,6 @@
 
 }
 
-package android.service.watchdog {
-
-  public abstract class ExplicitHealthCheckService extends android.app.Service {
-    method public void setCallback(@Nullable android.os.RemoteCallback);
-  }
-
-}
-
 package android.speech {
 
   public abstract class RecognitionService extends android.app.Service {
diff --git a/core/java/android/window/flags/lse_desktop_experience.aconfig b/core/java/android/window/flags/lse_desktop_experience.aconfig
index c4a9e57..3f3d3d8 100644
--- a/core/java/android/window/flags/lse_desktop_experience.aconfig
+++ b/core/java/android/window/flags/lse_desktop_experience.aconfig
@@ -336,6 +336,13 @@
 }
 
 flag {
+    name: "enable_desktop_app_launch_transitions"
+    namespace: "lse_desktop_experience"
+    description: "Enables custom transitions for app launches in Desktop Mode."
+    bug: "375992828"
+}
+
+flag {
     name: "enable_move_to_next_display_shortcut"
     namespace: "lse_desktop_experience"
     description: "Add new keyboard shortcut of moving a task into next display"
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 0942e05..8d25a9a 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
@@ -18,6 +18,7 @@
 
 import static android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_ENTER_TRANSITIONS;
 import static android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_TASK_LIMIT;
+import static android.window.DesktopModeFlags.ENABLE_WINDOWING_TRANSITION_HANDLERS_OBSERVERS;
 
 import android.annotation.Nullable;
 import android.app.KeyguardManager;
@@ -153,12 +154,7 @@
  * <p>This module only defines Shell dependencies for handheld SystemUI implementation. Common
  * dependencies should go into {@link WMShellBaseModule}.
  */
-@Module(
-        includes = {
-                WMShellBaseModule.class,
-                PipModule.class,
-                ShellBackAnimationModule.class
-        })
+@Module(includes = {WMShellBaseModule.class, PipModule.class, ShellBackAnimationModule.class})
 public abstract class WMShellModule {
 
     //
@@ -173,8 +169,7 @@
 
     @WMSingleton
     @Provides
-    static BubblePositioner provideBubblePositioner(Context context,
-            WindowManager windowManager) {
+    static BubblePositioner provideBubblePositioner(Context context, WindowManager windowManager) {
         return new BubblePositioner(context, windowManager);
     }
 
@@ -186,20 +181,22 @@
 
     @WMSingleton
     @Provides
-    static BubbleData provideBubbleData(Context context,
+    static BubbleData provideBubbleData(
+            Context context,
             BubbleLogger logger,
             BubblePositioner positioner,
             BubbleEducationController educationController,
             @ShellMainThread ShellExecutor mainExecutor,
             @ShellBackgroundThread ShellExecutor bgExecutor) {
-        return new BubbleData(context, logger, positioner, educationController, mainExecutor,
-                bgExecutor);
+        return new BubbleData(
+                context, logger, positioner, educationController, mainExecutor, bgExecutor);
     }
 
     // Note: Handler needed for LauncherApps.register
     @WMSingleton
     @Provides
-    static BubbleController provideBubbleController(Context context,
+    static BubbleController provideBubbleController(
+            Context context,
             ShellInit shellInit,
             ShellCommandHandler shellCommandHandler,
             ShellController shellController,
@@ -224,14 +221,38 @@
             Transitions transitions,
             SyncTransactionQueue syncQueue,
             IWindowManager wmService) {
-        return new BubbleController(context, shellInit, shellCommandHandler, shellController, data,
-                null /* synchronizer */, floatingContentCoordinator,
-                new BubbleDataRepository(launcherApps, mainExecutor, bgExecutor,
+        return new BubbleController(
+                context,
+                shellInit,
+                shellCommandHandler,
+                shellController,
+                data,
+                null /* synchronizer */,
+                floatingContentCoordinator,
+                new BubbleDataRepository(
+                        launcherApps,
+                        mainExecutor,
+                        bgExecutor,
                         new BubblePersistentRepository(context)),
-                statusBarService, windowManager, windowManagerShellWrapper, userManager,
-                launcherApps, logger, taskStackListener, organizer, positioner, displayController,
-                oneHandedOptional, dragAndDropController, mainExecutor, mainHandler, bgExecutor,
-                taskViewTransitions, transitions, syncQueue, wmService,
+                statusBarService,
+                windowManager,
+                windowManagerShellWrapper,
+                userManager,
+                launcherApps,
+                logger,
+                taskStackListener,
+                organizer,
+                positioner,
+                displayController,
+                oneHandedOptional,
+                dragAndDropController,
+                mainExecutor,
+                mainHandler,
+                bgExecutor,
+                taskViewTransitions,
+                transitions,
+                syncQueue,
+                wmService,
                 ProdBubbleProperties.INSTANCE);
     }
 
@@ -318,9 +339,7 @@
     @WMSingleton
     @Provides
     static AppToWebGenericLinksParser provideGenericLinksParser(
-            Context context,
-            @ShellMainThread ShellExecutor mainExecutor
-    ) {
+            Context context, @ShellMainThread ShellExecutor mainExecutor) {
         return new AppToWebGenericLinksParser(context, mainExecutor);
     }
 
@@ -328,8 +347,7 @@
     static AssistContentRequester provideAssistContentRequester(
             Context context,
             @ShellMainThread ShellExecutor shellExecutor,
-            @ShellBackgroundThread ShellExecutor bgExecutor
-    ) {
+            @ShellBackgroundThread ShellExecutor bgExecutor) {
         return new AssistContentRequester(context, shellExecutor, bgExecutor);
     }
 
@@ -366,15 +384,20 @@
             Optional<DesktopRepository> desktopRepository,
             Optional<DesktopTasksController> desktopTasksController,
             LaunchAdjacentController launchAdjacentController,
-            WindowDecorViewModel windowDecorViewModel) {
+            WindowDecorViewModel windowDecorViewModel,
+            Optional<TaskChangeListener> taskChangeListener) {
         // TODO(b/238217847): Temporarily add this check here until we can remove the dynamic
         //                    override for this controller from the base module
-        ShellInit init = FreeformComponents.isFreeformEnabled(context)
-                ? shellInit
-                : null;
-        return new FreeformTaskListener(context, init, shellTaskOrganizer,
-                desktopRepository, desktopTasksController, launchAdjacentController,
-                windowDecorViewModel);
+        ShellInit init = FreeformComponents.isFreeformEnabled(context) ? shellInit : null;
+        return new FreeformTaskListener(
+                context,
+                init,
+                shellTaskOrganizer,
+                desktopRepository,
+                desktopTasksController,
+                launchAdjacentController,
+                windowDecorViewModel,
+                taskChangeListener);
     }
 
     @WMSingleton
@@ -385,10 +408,7 @@
             @ShellMainThread ShellExecutor mainExecutor,
             @ShellAnimationThread ShellExecutor animExecutor) {
         return new FreeformTaskTransitionHandler(
-                transitions,
-                displayController,
-                mainExecutor,
-                animExecutor);
+                transitions, displayController, mainExecutor, animExecutor);
     }
 
     @WMSingleton
@@ -402,8 +422,13 @@
             Optional<TaskChangeListener> taskChangeListener,
             FocusTransitionObserver focusTransitionObserver) {
         return new FreeformTaskTransitionObserver(
-                context, shellInit, transitions, desktopImmersiveController,
-                windowDecorViewModel, taskChangeListener, focusTransitionObserver);
+                context,
+                shellInit,
+                transitions,
+                desktopImmersiveController,
+                windowDecorViewModel,
+                taskChangeListener,
+                focusTransitionObserver);
     }
 
     @WMSingleton
@@ -419,8 +444,8 @@
         } else {
             transitionStarter = freeformTaskTransitionHandler;
         }
-        return new FreeformTaskTransitionStarterInitializer(shellInit, windowDecorViewModel,
-                transitionStarter);
+        return new FreeformTaskTransitionStarterInitializer(
+                shellInit, windowDecorViewModel, transitionStarter);
     }
 
     //
@@ -431,7 +456,8 @@
     @WMSingleton
     @Provides
     @DynamicOverride
-    static OneHandedController provideOneHandedController(Context context,
+    static OneHandedController provideOneHandedController(
+            Context context,
             ShellInit shellInit,
             ShellCommandHandler shellCommandHandler,
             ShellController shellController,
@@ -443,9 +469,19 @@
             InteractionJankMonitor jankMonitor,
             @ShellMainThread ShellExecutor mainExecutor,
             @ShellMainThread Handler mainHandler) {
-        return OneHandedController.create(context, shellInit, shellCommandHandler, shellController,
-                windowManager, displayController, displayLayout, taskStackListener, jankMonitor,
-                uiEventLogger, mainExecutor, mainHandler);
+        return OneHandedController.create(
+                context,
+                shellInit,
+                shellCommandHandler,
+                shellController,
+                windowManager,
+                displayController,
+                displayLayout,
+                taskStackListener,
+                jankMonitor,
+                uiEventLogger,
+                mainExecutor,
+                mainHandler);
     }
 
     //
@@ -477,12 +513,29 @@
             MultiInstanceHelper multiInstanceHelper,
             @ShellMainThread ShellExecutor mainExecutor,
             @ShellMainThread Handler mainHandler) {
-        return new SplitScreenController(context, shellInit, shellCommandHandler, shellController,
-                shellTaskOrganizer, syncQueue, rootTaskDisplayAreaOrganizer, displayController,
-                displayImeController, displayInsetsController, dragAndDropController, transitions,
-                transactionPool, iconProvider, recentTasks, launchAdjacentController,
-                windowDecorViewModel, desktopTasksController, null /* stageCoordinator */,
-                multiInstanceHelper, mainExecutor, mainHandler);
+        return new SplitScreenController(
+                context,
+                shellInit,
+                shellCommandHandler,
+                shellController,
+                shellTaskOrganizer,
+                syncQueue,
+                rootTaskDisplayAreaOrganizer,
+                displayController,
+                displayImeController,
+                displayInsetsController,
+                dragAndDropController,
+                transitions,
+                transactionPool,
+                iconProvider,
+                recentTasks,
+                launchAdjacentController,
+                windowDecorViewModel,
+                desktopTasksController,
+                null /* stageCoordinator */,
+                multiInstanceHelper,
+                mainExecutor,
+                mainHandler);
     }
 
     //
@@ -502,10 +555,16 @@
             Optional<UnfoldTransitionHandler> unfoldHandler,
             Optional<ActivityEmbeddingController> activityEmbeddingController,
             Transitions transitions) {
-        return new DefaultMixedHandler(shellInit, transitions, splitScreenOptional,
-                pipTransitionController, recentsTransitionHandler,
-                keyguardTransitionHandler, desktopTasksController,
-                unfoldHandler, activityEmbeddingController);
+        return new DefaultMixedHandler(
+                shellInit,
+                transitions,
+                splitScreenOptional,
+                pipTransitionController,
+                recentsTransitionHandler,
+                keyguardTransitionHandler,
+                desktopTasksController,
+                unfoldHandler,
+                activityEmbeddingController);
     }
 
     @WMSingleton
@@ -516,8 +575,12 @@
             Transitions transitions,
             Optional<RecentTasksController> recentTasksController,
             HomeTransitionObserver homeTransitionObserver) {
-        return new RecentsTransitionHandler(shellInit, shellTaskOrganizer, transitions,
-                recentTasksController.orElse(null), homeTransitionObserver);
+        return new RecentsTransitionHandler(
+                shellInit,
+                shellTaskOrganizer,
+                transitions,
+                recentTasksController.orElse(null),
+                homeTransitionObserver);
     }
 
     //
@@ -534,8 +597,7 @@
             FullscreenUnfoldTaskAnimator fullscreenAnimator,
             Lazy<Optional<UnfoldTransitionHandler>> unfoldTransitionHandler,
             ShellInit shellInit,
-            @ShellMainThread ShellExecutor mainExecutor
-    ) {
+            @ShellMainThread ShellExecutor mainExecutor) {
         final List<UnfoldTaskAnimator> animators = new ArrayList<>();
         animators.add(splitAnimator);
         animators.add(fullscreenAnimator);
@@ -546,8 +608,7 @@
                 progressProvider.get(),
                 animators,
                 unfoldTransitionHandler,
-                mainExecutor
-        );
+                mainExecutor);
     }
 
     @Provides
@@ -555,10 +616,9 @@
             Context context,
             UnfoldBackgroundController unfoldBackgroundController,
             ShellController shellController,
-            DisplayInsetsController displayInsetsController
-    ) {
-        return new FullscreenUnfoldTaskAnimator(context, unfoldBackgroundController,
-                shellController, displayInsetsController);
+            DisplayInsetsController displayInsetsController) {
+        return new FullscreenUnfoldTaskAnimator(
+                context, unfoldBackgroundController, shellController, displayInsetsController);
     }
 
     @Provides
@@ -568,14 +628,18 @@
             ShellController shellController,
             @ShellMainThread ShellExecutor executor,
             Lazy<Optional<SplitScreenController>> splitScreenOptional,
-            DisplayInsetsController displayInsetsController
-    ) {
+            DisplayInsetsController displayInsetsController) {
         // TODO(b/238217847): The lazy reference here causes some dependency issues since it
         // immediately registers a listener on that controller on init.  We should reference the
         // controller directly once we refactor ShellTaskOrganizer to not depend on the unfold
         // animation controller directly.
-        return new SplitTaskUnfoldAnimator(context, executor, splitScreenOptional,
-                shellController, backgroundController, displayInsetsController);
+        return new SplitTaskUnfoldAnimator(
+                context,
+                executor,
+                splitScreenOptional,
+                shellController,
+                backgroundController,
+                displayInsetsController);
     }
 
     @WMSingleton
@@ -602,8 +666,15 @@
             @ShellMainThread ShellExecutor executor,
             @ShellMainThread Handler handler,
             ShellInit shellInit) {
-        return new UnfoldTransitionHandler(shellInit, progressProvider.get(), animator,
-                unfoldAnimator, transactionPool, executor, handler, transitions);
+        return new UnfoldTransitionHandler(
+                shellInit,
+                progressProvider.get(),
+                animator,
+                unfoldAnimator,
+                transactionPool,
+                executor,
+                handler,
+                transitions);
     }
 
     @WMSingleton
@@ -652,18 +723,38 @@
             FocusTransitionObserver focusTransitionObserver,
             DesktopModeEventLogger desktopModeEventLogger,
             DesktopTilingDecorViewModel desktopTilingDecorViewModel) {
-        return new DesktopTasksController(context, shellInit, shellCommandHandler, shellController,
-                displayController, shellTaskOrganizer, syncQueue, rootTaskDisplayAreaOrganizer,
-                dragAndDropController, transitions, keyguardManager,
-                returnToDragStartAnimator, enterDesktopTransitionHandler,
-                exitDesktopTransitionHandler, desktopModeDragAndDropTransitionHandler,
+        return new DesktopTasksController(
+                context,
+                shellInit,
+                shellCommandHandler,
+                shellController,
+                displayController,
+                shellTaskOrganizer,
+                syncQueue,
+                rootTaskDisplayAreaOrganizer,
+                dragAndDropController,
+                transitions,
+                keyguardManager,
+                returnToDragStartAnimator,
+                enterDesktopTransitionHandler,
+                exitDesktopTransitionHandler,
+                desktopModeDragAndDropTransitionHandler,
                 toggleResizeDesktopTaskTransitionHandler,
-                dragToDesktopTransitionHandler, desktopImmersiveController.get(),
+                dragToDesktopTransitionHandler,
+                desktopImmersiveController.get(),
                 desktopRepository,
-                desktopModeLoggerTransitionObserver, launchAdjacentController,
-                recentsTransitionHandler, multiInstanceHelper, mainExecutor, desktopTasksLimiter,
-                recentTasksController.orElse(null), interactionJankMonitor, mainHandler,
-                inputManager, focusTransitionObserver, desktopModeEventLogger,
+                desktopModeLoggerTransitionObserver,
+                launchAdjacentController,
+                recentsTransitionHandler,
+                multiInstanceHelper,
+                mainExecutor,
+                desktopTasksLimiter,
+                recentTasksController.orElse(null),
+                interactionJankMonitor,
+                mainHandler,
+                inputManager,
+                focusTransitionObserver,
+                desktopModeEventLogger,
                 desktopTilingDecorViewModel);
     }
 
@@ -693,10 +784,11 @@
 
     @WMSingleton
     @Provides
-    static Optional<TaskChangeListener> provideDesktopTaskChangeListener(Context context) {
-        if (Flags.enableWindowingTransitionHandlersObservers() &&
-                DesktopModeStatus.canEnterDesktopMode(context)) {
-            return Optional.of(new DesktopTaskChangeListener());
+    static Optional<TaskChangeListener> provideDesktopTaskChangeListener(
+            Context context, @DynamicOverride DesktopRepository desktopRepository) {
+        if (ENABLE_WINDOWING_TRANSITION_HANDLERS_OBSERVERS.isTrue()
+                && DesktopModeStatus.canEnterDesktopMode(context)) {
+            return Optional.of(new DesktopTaskChangeListener(desktopRepository));
         }
         return Optional.empty();
     }
@@ -724,8 +816,7 @@
                         maxTaskLimit,
                         interactionJankMonitor,
                         context,
-                        handler)
-        );
+                        handler));
     }
 
     @WMSingleton
@@ -739,10 +830,7 @@
         if (DesktopModeStatus.canEnterDesktopMode(context)) {
             return Optional.of(
                     new DesktopImmersiveController(
-                            transitions,
-                            desktopRepository,
-                            displayController,
-                            shellTaskOrganizer));
+                            transitions, desktopRepository, displayController, shellTaskOrganizer));
         }
         return Optional.empty();
     }
@@ -754,7 +842,6 @@
         return new ReturnToDragStartAnimator(context, interactionJankMonitor);
     }
 
-
     @WMSingleton
     @Provides
     static DragToDesktopTransitionHandler provideDragToDesktopTransitionHandler(
@@ -762,12 +849,12 @@
             Transitions transitions,
             RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
             InteractionJankMonitor interactionJankMonitor) {
-        return (Flags.enableDesktopWindowingTransitions() ||
-            ENABLE_DESKTOP_WINDOWING_ENTER_TRANSITIONS.isTrue())
-                ? new SpringDragToDesktopTransitionHandler(context, transitions,
-                        rootTaskDisplayAreaOrganizer, interactionJankMonitor)
-                : new DefaultDragToDesktopTransitionHandler(context, transitions,
-                        rootTaskDisplayAreaOrganizer, interactionJankMonitor);
+        return (Flags.enableDesktopWindowingTransitions()
+                        || ENABLE_DESKTOP_WINDOWING_ENTER_TRANSITIONS.isTrue())
+                ? new SpringDragToDesktopTransitionHandler(
+                        context, transitions, rootTaskDisplayAreaOrganizer, interactionJankMonitor)
+                : new DefaultDragToDesktopTransitionHandler(
+                        context, transitions, rootTaskDisplayAreaOrganizer, interactionJankMonitor);
     }
 
     @WMSingleton
@@ -802,31 +889,26 @@
     static CloseDesktopTaskTransitionHandler provideCloseDesktopTaskTransitionHandler(
             Context context,
             @ShellMainThread ShellExecutor mainExecutor,
-            @ShellAnimationThread ShellExecutor animExecutor
-    ) {
+            @ShellAnimationThread ShellExecutor animExecutor) {
         return new CloseDesktopTaskTransitionHandler(context, mainExecutor, animExecutor);
     }
 
     @WMSingleton
     @Provides
     static DesktopModeDragAndDropTransitionHandler provideDesktopModeDragAndDropTransitionHandler(
-            Transitions transitions
-    ) {
+            Transitions transitions) {
         return new DesktopModeDragAndDropTransitionHandler(transitions);
     }
 
     @WMSingleton
     @Provides
     @DynamicOverride
-
     static DesktopRepository provideDesktopRepository(
             Context context,
             ShellInit shellInit,
             DesktopPersistentRepository desktopPersistentRepository,
-            @ShellMainThread CoroutineScope mainScope
-    ) {
-        return new DesktopRepository(context, shellInit, desktopPersistentRepository,
-                mainScope);
+            @ShellMainThread CoroutineScope mainScope) {
+        return new DesktopRepository(context, shellInit, desktopPersistentRepository, mainScope);
     }
 
     @WMSingleton
@@ -837,12 +919,16 @@
             ShellTaskOrganizer shellTaskOrganizer,
             TaskStackListenerImpl taskStackListener,
             ToggleResizeDesktopTaskTransitionHandler toggleResizeDesktopTaskTransitionHandler,
-            @DynamicOverride DesktopRepository desktopRepository
-    ) {
+            @DynamicOverride DesktopRepository desktopRepository) {
         if (DesktopModeStatus.canEnterDesktopMode(context)) {
-            return Optional.of(new DesktopActivityOrientationChangeHandler(
-                    context, shellInit, shellTaskOrganizer, taskStackListener,
-                    toggleResizeDesktopTaskTransitionHandler, desktopRepository));
+            return Optional.of(
+                    new DesktopActivityOrientationChangeHandler(
+                            context,
+                            shellInit,
+                            shellTaskOrganizer,
+                            taskStackListener,
+                            toggleResizeDesktopTaskTransitionHandler,
+                            desktopRepository));
         }
         return Optional.empty();
     }
@@ -854,12 +940,16 @@
             Optional<DesktopRepository> desktopRepository,
             Transitions transitions,
             ShellTaskOrganizer shellTaskOrganizer,
-            ShellInit shellInit
-    ) {
-        return desktopRepository.flatMap(repository ->
-                Optional.of(new DesktopTasksTransitionObserver(
-                        context, repository, transitions, shellTaskOrganizer, shellInit))
-        );
+            ShellInit shellInit) {
+        return desktopRepository.flatMap(
+                repository ->
+                        Optional.of(
+                                new DesktopTasksTransitionObserver(
+                                        context,
+                                        repository,
+                                        transitions,
+                                        shellTaskOrganizer,
+                                        shellInit)));
     }
 
     @WMSingleton
@@ -871,8 +961,7 @@
             FreeformTaskTransitionHandler freeformTaskTransitionHandler,
             CloseDesktopTaskTransitionHandler closeDesktopTaskTransitionHandler,
             InteractionJankMonitor interactionJankMonitor,
-            @ShellMainThread Handler handler
-    ) {
+            @ShellMainThread Handler handler) {
         if (!DesktopModeStatus.canEnterDesktopMode(context)) {
             return Optional.empty();
         }
@@ -929,12 +1018,11 @@
     @Provides
     static DesktopWindowingEducationTooltipController
             provideDesktopWindowingEducationTooltipController(
-            Context context,
-            AdditionalSystemViewContainer.Factory additionalSystemViewContainerFactory,
-            DisplayController displayController
-    ) {
-        return new DesktopWindowingEducationTooltipController(context,
-                additionalSystemViewContainerFactory, displayController);
+                    Context context,
+                    AdditionalSystemViewContainer.Factory additionalSystemViewContainerFactory,
+                    DisplayController displayController) {
+        return new DesktopWindowingEducationTooltipController(
+                context, additionalSystemViewContainerFactory, displayController);
     }
 
     @OptIn(markerClass = ExperimentalCoroutinesApi.class)
@@ -946,19 +1034,22 @@
             AppHandleEducationDatastoreRepository appHandleEducationDatastoreRepository,
             WindowDecorCaptionHandleRepository windowDecorCaptionHandleRepository,
             DesktopWindowingEducationTooltipController desktopWindowingEducationTooltipController,
-            @ShellMainThread CoroutineScope applicationScope, @ShellBackgroundThread
-            MainCoroutineDispatcher backgroundDispatcher) {
-        return new AppHandleEducationController(context, appHandleEducationFilter,
-                appHandleEducationDatastoreRepository, windowDecorCaptionHandleRepository,
-                desktopWindowingEducationTooltipController, applicationScope,
+            @ShellMainThread CoroutineScope applicationScope,
+            @ShellBackgroundThread MainCoroutineDispatcher backgroundDispatcher) {
+        return new AppHandleEducationController(
+                context,
+                appHandleEducationFilter,
+                appHandleEducationDatastoreRepository,
+                windowDecorCaptionHandleRepository,
+                desktopWindowingEducationTooltipController,
+                applicationScope,
                 backgroundDispatcher);
     }
 
     @WMSingleton
     @Provides
     static DesktopPersistentRepository provideDesktopPersistentRepository(
-            Context context,
-            @ShellBackgroundThread CoroutineScope bgScope) {
+            Context context, @ShellBackgroundThread CoroutineScope bgScope) {
         return new DesktopPersistentRepository(context, bgScope);
     }
 
@@ -969,14 +1060,14 @@
     @WMSingleton
     @Provides
     static GlobalDragListener provideGlobalDragListener(
-            IWindowManager wmService,
-            @ShellMainThread ShellExecutor mainExecutor) {
+            IWindowManager wmService, @ShellMainThread ShellExecutor mainExecutor) {
         return new GlobalDragListener(wmService, mainExecutor);
     }
 
     @WMSingleton
     @Provides
-    static DragAndDropController provideDragAndDropController(Context context,
+    static DragAndDropController provideDragAndDropController(
+            Context context,
             ShellInit shellInit,
             ShellController shellController,
             ShellCommandHandler shellCommandHandler,
@@ -987,9 +1078,18 @@
             GlobalDragListener globalDragListener,
             Transitions transitions,
             @ShellMainThread ShellExecutor mainExecutor) {
-        return new DragAndDropController(context, shellInit, shellController, shellCommandHandler,
-                shellTaskOrganizer, displayController, uiEventLogger, iconProvider,
-                globalDragListener, transitions, mainExecutor);
+        return new DragAndDropController(
+                context,
+                shellInit,
+                shellController,
+                shellCommandHandler,
+                shellTaskOrganizer,
+                displayController,
+                uiEventLogger,
+                iconProvider,
+                globalDragListener,
+                transitions,
+                mainExecutor);
     }
 
     //
@@ -1003,8 +1103,7 @@
     @Provides
     static Object provideIndependentShellComponentsToCreate(
             DragAndDropController dragAndDropController,
-            Optional<DesktopTasksTransitionObserver> desktopTasksTransitionObserverOptional
-    ) {
+            Optional<DesktopTasksTransitionObserver> desktopTasksTransitionObserverOptional) {
         return new Object();
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListener.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListener.kt
index 1ee2de9..237a465 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListener.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListener.kt
@@ -17,21 +17,60 @@
 package com.android.wm.shell.desktopmode
 
 import android.app.ActivityManager.RunningTaskInfo
+import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
+import android.window.DesktopModeFlags
 import com.android.wm.shell.freeform.TaskChangeListener
 
 /** Manages tasks handling specific to Android Desktop Mode. */
-class DesktopTaskChangeListener: TaskChangeListener {
+class DesktopTaskChangeListener(
+    private val desktopRepository: DesktopRepository,
+) : TaskChangeListener {
 
   override fun onTaskOpening(taskInfo: RunningTaskInfo) {
-    // TODO: b/367268953 - Connect this with DesktopRepository.
+    if (!isFreeformTask(taskInfo) && desktopRepository.isActiveTask(taskInfo.taskId)) {
+      desktopRepository.removeFreeformTask(taskInfo.displayId, taskInfo.taskId)
+      return
+    }
+    if (isFreeformTask(taskInfo)) {
+      desktopRepository.addOrMoveFreeformTaskToTop(taskInfo.displayId, taskInfo.taskId)
+      if (taskInfo.isVisible) {
+        desktopRepository.addActiveTask(taskInfo.displayId, taskInfo.taskId)
+        desktopRepository.updateTaskVisibility(taskInfo.displayId, taskInfo.taskId, visible = true)
+      }
+    }
   }
 
   override fun onTaskChanging(taskInfo: RunningTaskInfo) {
-    // TODO: b/367268953 - Connect this with DesktopRepository.
+    if (!desktopRepository.isActiveTask(taskInfo.taskId)) return
+
+    // Case 1: Freeform task is changed in Desktop Mode.
+    if (isFreeformTask(taskInfo)) {
+      if (taskInfo.isVisible) {
+        desktopRepository.addActiveTask(taskInfo.displayId, taskInfo.taskId)
+      }
+      desktopRepository.updateTaskVisibility(
+          taskInfo.displayId, taskInfo.taskId, taskInfo.isVisible)
+    } else {
+      // Case 2: Freeform task is changed outside Desktop Mode.
+      desktopRepository.removeFreeformTask(taskInfo.displayId, taskInfo.taskId)
+    }
+  }
+
+  // This method should only be used for scenarios where the task info changes are not propagated to
+  // [DesktopTaskChangeListener#onTaskChanging] via [TransitionsObserver].
+  // Any changes to [DesktopRepository] from this method should be made carefully to minimize risk
+  // of race conditions and possible duplications with [onTaskChanging].
+  override fun onNonTransitionTaskChanging(taskInfo: RunningTaskInfo) {
+    // TODO: b/367268953 - Propapagate usages from FreeformTaskListener to this method.
   }
 
   override fun onTaskMovingToFront(taskInfo: RunningTaskInfo) {
-    // TODO: b/367268953 - Connect this with DesktopRepository.
+    if (!desktopRepository.isActiveTask(taskInfo.taskId)) return
+    if (!isFreeformTask(taskInfo)) {
+      desktopRepository.removeFreeformTask(taskInfo.displayId, taskInfo.taskId)
+    }
+    // TODO: b/367268953 - Connect this with DesktopRepository for handling
+    // task moving to front for tasks in windowing mode.
   }
 
   override fun onTaskMovingToBack(taskInfo: RunningTaskInfo) {
@@ -39,6 +78,20 @@
   }
 
   override fun onTaskClosing(taskInfo: RunningTaskInfo) {
-    // TODO: b/367268953 - Connect this with DesktopRepository.
+    if (!desktopRepository.isActiveTask(taskInfo.taskId)) return
+    // TODO: b/370038902 - Handle Activity#finishAndRemoveTask.
+    if (!DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION.isTrue() ||
+        desktopRepository.isClosingTask(taskInfo.taskId)) {
+      // A task that's vanishing should be removed:
+      // - If it's closed by the X button which means it's marked as a closing task.
+      desktopRepository.removeClosingTask(taskInfo.taskId)
+      desktopRepository.removeFreeformTask(taskInfo.displayId, taskInfo.taskId)
+    } else {
+      desktopRepository.updateTaskVisibility(taskInfo.displayId, taskInfo.taskId, visible = false)
+      desktopRepository.minimizeTask(taskInfo.displayId, taskInfo.taskId)
+    }
   }
+
+  private fun isFreeformTask(taskInfo: RunningTaskInfo): Boolean =
+      taskInfo.windowingMode == WINDOWING_MODE_FREEFORM
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
index a16446ff..af87ab7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
@@ -17,7 +17,6 @@
 package com.android.wm.shell.freeform;
 
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
-
 import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_FREEFORM;
 
 import android.app.ActivityManager.RunningTaskInfo;
@@ -54,6 +53,7 @@
     private final Optional<DesktopTasksController> mDesktopTasksController;
     private final WindowDecorViewModel mWindowDecorationViewModel;
     private final LaunchAdjacentController mLaunchAdjacentController;
+    private final Optional<TaskChangeListener> mTaskChangeListener;
 
     private final SparseArray<State> mTasks = new SparseArray<>();
 
@@ -69,13 +69,15 @@
             Optional<DesktopRepository> desktopRepository,
             Optional<DesktopTasksController> desktopTasksController,
             LaunchAdjacentController launchAdjacentController,
-            WindowDecorViewModel windowDecorationViewModel) {
+            WindowDecorViewModel windowDecorationViewModel,
+            Optional<TaskChangeListener> taskChangeListener) {
         mContext = context;
         mShellTaskOrganizer = shellTaskOrganizer;
         mWindowDecorationViewModel = windowDecorationViewModel;
         mDesktopRepository = desktopRepository;
         mDesktopTasksController = desktopTasksController;
         mLaunchAdjacentController = launchAdjacentController;
+        mTaskChangeListener = taskChangeListener;
         if (shellInit != null) {
             shellInit.addInitCallback(this::onInit, this);
         }
@@ -100,7 +102,8 @@
         state.mLeash = leash;
         mTasks.put(taskInfo.taskId, state);
 
-        if (DesktopModeStatus.canEnterDesktopMode(mContext)) {
+        if (!DesktopModeFlags.ENABLE_WINDOWING_TRANSITION_HANDLERS_OBSERVERS.isTrue() &&
+                DesktopModeStatus.canEnterDesktopMode(mContext)) {
             mDesktopRepository.ifPresent(repository -> {
                 repository.addOrMoveFreeformTaskToTop(taskInfo.displayId, taskInfo.taskId);
                 if (taskInfo.isVisible) {
@@ -119,7 +122,8 @@
                 taskInfo.taskId);
         mTasks.remove(taskInfo.taskId);
 
-        if (DesktopModeStatus.canEnterDesktopMode(mContext)) {
+        if (!DesktopModeFlags.ENABLE_WINDOWING_TRANSITION_HANDLERS_OBSERVERS.isTrue() &&
+                DesktopModeStatus.canEnterDesktopMode(mContext)) {
             mDesktopRepository.ifPresent(repository -> {
                 // TODO: b/370038902 - Handle Activity#finishAndRemoveTask.
                 if (!DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION.isTrue()
@@ -148,13 +152,20 @@
         mWindowDecorationViewModel.onTaskInfoChanged(taskInfo);
         state.mTaskInfo = taskInfo;
         if (DesktopModeStatus.canEnterDesktopMode(mContext)) {
-            mDesktopRepository.ifPresent(repository -> {
-                if (taskInfo.isVisible) {
-                    repository.addActiveTask(taskInfo.displayId, taskInfo.taskId);
-                }
-                repository.updateTaskVisibility(taskInfo.displayId, taskInfo.taskId,
+            if (DesktopModeFlags.ENABLE_WINDOWING_TRANSITION_HANDLERS_OBSERVERS.isTrue()) {
+                // Pass task info changes to the [TaskChangeListener] since [TransitionsObserver]
+                // does not propagate all task info changes.
+                mTaskChangeListener.ifPresent(listener ->
+                    listener.onNonTransitionTaskChanging(taskInfo));
+            } else {
+                mDesktopRepository.ifPresent(repository -> {
+                    if (taskInfo.isVisible) {
+                        repository.addActiveTask(taskInfo.displayId, taskInfo.taskId);
+                    }
+                    repository.updateTaskVisibility(taskInfo.displayId, taskInfo.taskId,
                         taskInfo.isVisible);
-            });
+                });
+            }
         }
         updateLaunchAdjacentController();
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java
index 7631ece..18f9cc7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java
@@ -42,9 +42,9 @@
 import java.util.Optional;
 
 /**
- * The {@link Transitions.TransitionHandler} that handles freeform task launches, closes,
- * maximizing and restoring transitions. It also reports transitions so that window decorations can
- * be a part of transitions.
+ * The {@link Transitions.TransitionHandler} that handles freeform task launches, closes, maximizing
+ * and restoring transitions. It also reports transitions so that window decorations can be a part
+ * of transitions.
  */
 public class FreeformTaskTransitionObserver implements Transitions.TransitionObserver {
     private final Transitions mTransitions;
@@ -89,8 +89,8 @@
             // TODO(b/367268953): Remove when DesktopTaskListener is introduced and the repository
             //  is updated from there **before** the |mWindowDecorViewModel| methods are invoked.
             //  Otherwise window decoration relayout won't run with the immersive state up to date.
-            mDesktopImmersiveController.ifPresent(h ->
-                    h.onTransitionReady(transition, info, startT, finishT));
+            mDesktopImmersiveController.ifPresent(
+                    h -> h.onTransitionReady(transition, info, startT, finishT));
         }
         // Update focus state first to ensure the correct state can be queried from listeners.
         // TODO(371503964): Remove this once the unified task repository is ready.
@@ -147,33 +147,28 @@
             TransitionInfo.Change change,
             SurfaceControl.Transaction startT,
             SurfaceControl.Transaction finishT) {
-        mTaskChangeListener.ifPresent(
-            listener -> listener.onTaskOpening(change.getTaskInfo()));
+        mTaskChangeListener.ifPresent(listener -> listener.onTaskOpening(change.getTaskInfo()));
         mWindowDecorViewModel.onTaskOpening(
-            change.getTaskInfo(), change.getLeash(), startT, finishT);
+                change.getTaskInfo(), change.getLeash(), startT, finishT);
     }
 
     private void onCloseTransitionReady(
             TransitionInfo.Change change,
             SurfaceControl.Transaction startT,
             SurfaceControl.Transaction finishT) {
-        mTaskChangeListener.ifPresent(
-            listener -> listener.onTaskClosing(change.getTaskInfo()));
+        mTaskChangeListener.ifPresent(listener -> listener.onTaskClosing(change.getTaskInfo()));
         mWindowDecorViewModel.onTaskClosing(change.getTaskInfo(), startT, finishT);
-
     }
 
     private void onChangeTransitionReady(
             TransitionInfo.Change change,
             SurfaceControl.Transaction startT,
             SurfaceControl.Transaction finishT) {
-        mTaskChangeListener.ifPresent(listener ->
-            listener.onTaskChanging(change.getTaskInfo()));
+        mTaskChangeListener.ifPresent(listener -> listener.onTaskChanging(change.getTaskInfo()));
         mWindowDecorViewModel.onTaskChanging(
                 change.getTaskInfo(), change.getLeash(), startT, finishT);
     }
 
-
     private void onToFrontTransitionReady(
             TransitionInfo.Change change,
             SurfaceControl.Transaction startT,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/TaskChangeListener.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/TaskChangeListener.kt
index f07c069..fb86a9f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/TaskChangeListener.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/TaskChangeListener.kt
@@ -16,7 +16,7 @@
 
 package com.android.wm.shell.freeform
 
-import android.app.ActivityManager.RunningTaskInfo;
+import android.app.ActivityManager.RunningTaskInfo
 
 /**
  * Interface used by [FreeformTaskTransitionObserver] to manage freeform tasks.
@@ -24,18 +24,27 @@
  * The implementations are responsible for handle all the task management.
  */
 interface TaskChangeListener {
-    /** Notifies a task opening in freeform mode. */
-    fun onTaskOpening(taskInfo: RunningTaskInfo)
+  /** Notifies a task opening in freeform mode. */
+  fun onTaskOpening(taskInfo: RunningTaskInfo)
 
-    /** Notifies a task info update on the given task. */
-    fun onTaskChanging(taskInfo: RunningTaskInfo)
+  /** Notifies a task info update on the given task from Shell Transitions framework. */
+  fun onTaskChanging(taskInfo: RunningTaskInfo)
 
-    /** Notifies a task moving to the front. */
-    fun onTaskMovingToFront(taskInfo: RunningTaskInfo)
+  /**
+   * Notifies a task info update on the given task from [FreeformTaskListener].
+   *
+   * This is used to propagate task info changes since not all task changes are propagated from
+   * [TransitionObserver] in [onTaskChanging]. It is recommended to use [onTaskChanging] instead of
+   * this method where possible.
+   */
+  fun onNonTransitionTaskChanging(taskInfo: RunningTaskInfo)
 
-    /** Notifies a task moving to the back. */
-    fun onTaskMovingToBack(taskInfo: RunningTaskInfo)
+  /** Notifies a task moving to the front. */
+  fun onTaskMovingToFront(taskInfo: RunningTaskInfo)
 
-    /** Notifies a task is closing. */
-    fun onTaskClosing(taskInfo: RunningTaskInfo)
-}
\ No newline at end of file
+  /** Notifies a task moving to the back. */
+  fun onTaskMovingToBack(taskInfo: RunningTaskInfo)
+
+  /** Notifies a task is closing. */
+  fun onTaskClosing(taskInfo: RunningTaskInfo)
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListenerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListenerTest.kt
new file mode 100644
index 0000000..8ae8b0f
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListenerTest.kt
@@ -0,0 +1,180 @@
+/*
+ * 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.wm.shell.desktopmode
+
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.SetFlagsRule
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION
+import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFreeformTask
+import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFullscreenTask
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.never
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+
+/**
+ * Tests for {@link DesktopTaskChangeListener}
+ *
+ * Build/Install/Run: atest WMShellUnitTests:DesktopTaskChangeListenerTest
+ */
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class DesktopTaskChangeListenerTest : ShellTestCase() {
+
+  @JvmField @Rule val setFlagsRule = SetFlagsRule()
+
+  private lateinit var desktopTaskChangeListener: DesktopTaskChangeListener
+
+  private val desktopRepository = mock<DesktopRepository>()
+
+  @Before
+  fun setUp() {
+    desktopTaskChangeListener = DesktopTaskChangeListener(desktopRepository)
+  }
+
+  @Test
+  fun onTaskOpening_fullscreenTask_notActiveDesktopTask_noop() {
+    val task = createFullscreenTask().apply { isVisible = true }
+    whenever(desktopRepository.isActiveTask(task.taskId)).thenReturn(false)
+
+    desktopTaskChangeListener.onTaskOpening(task)
+
+    verify(desktopRepository, never()).addOrMoveFreeformTaskToTop(task.displayId, task.taskId)
+    verify(desktopRepository, never()).removeFreeformTask(task.displayId, task.taskId)
+  }
+
+  @Test
+  fun onTaskOpening_freeformTask_activeDesktopTask_removesTaskFromRepo() {
+    val task = createFullscreenTask().apply { isVisible = true }
+    whenever(desktopRepository.isActiveTask(task.taskId)).thenReturn(true)
+
+    desktopTaskChangeListener.onTaskOpening(task)
+
+    verify(desktopRepository).removeFreeformTask(task.displayId, task.taskId)
+  }
+
+  @Test
+  fun onTaskOpening_freeformTask_visibleDesktopTask_addsTaskToRepository() {
+    val task = createFreeformTask().apply { isVisible = true }
+    whenever(desktopRepository.isActiveTask(task.taskId)).thenReturn(false)
+
+    desktopTaskChangeListener.onTaskOpening(task)
+
+    verify(desktopRepository).addOrMoveFreeformTaskToTop(task.displayId, task.taskId)
+    verify(desktopRepository).addActiveTask(task.displayId, task.taskId)
+    verify(desktopRepository).updateTaskVisibility(task.displayId, task.taskId, visible = true)
+  }
+
+  @Test
+  fun onTaskOpening_freeformTask_nonVisibleDesktopTask_addsTaskToRepository() {
+    val task = createFreeformTask().apply { isVisible = false }
+    whenever(desktopRepository.isActiveTask(task.taskId)).thenReturn(true)
+
+    desktopTaskChangeListener.onTaskOpening(task)
+
+    verify(desktopRepository).addOrMoveFreeformTaskToTop(task.displayId, task.taskId)
+  }
+
+  @Test
+  fun onTaskChanging_freeformTaskOutsideDesktop_removesTaskFromRepo() {
+    val task = createFullscreenTask().apply { isVisible = true }
+    whenever(desktopRepository.isActiveTask(task.taskId)).thenReturn(true)
+
+    desktopTaskChangeListener.onTaskChanging(task)
+
+    verify(desktopRepository).removeFreeformTask(task.displayId, task.taskId)
+  }
+
+  @Test
+  fun onTaskChanging_visibleTaskInDesktop_updatesTaskVisibility() {
+    val task = createFreeformTask().apply { isVisible = true }
+    whenever(desktopRepository.isActiveTask(task.taskId)).thenReturn(true)
+
+    desktopTaskChangeListener.onTaskChanging(task)
+
+    verify(desktopRepository).addActiveTask(task.displayId, task.taskId)
+    verify(desktopRepository).updateTaskVisibility(task.displayId, task.taskId, task.isVisible)
+  }
+
+  @Test
+  fun onTaskChanging_nonVisibleTask_updatesTaskVisibility() {
+    val task = createFreeformTask().apply { isVisible = false }
+    whenever(desktopRepository.isActiveTask(task.taskId)).thenReturn(true)
+
+    desktopTaskChangeListener.onTaskChanging(task)
+
+    verify(desktopRepository).updateTaskVisibility(task.displayId, task.taskId, task.isVisible)
+  }
+
+  @Test
+  fun onTaskMovingToFront_freeformTaskOutsideDesktop_removesTaskFromRepo() {
+    val task = createFullscreenTask().apply { isVisible = true }
+    whenever(desktopRepository.isActiveTask(task.taskId)).thenReturn(true)
+
+    desktopTaskChangeListener.onTaskMovingToFront(task)
+
+    verify(desktopRepository).removeFreeformTask(task.displayId, task.taskId)
+  }
+
+  @Test
+  @EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION)
+  fun onTaskClosing_backNavEnabled_nonClosingTask_minimizesTaskInRepo() {
+    val task = createFreeformTask().apply { isVisible = true }
+    whenever(desktopRepository.isActiveTask(task.taskId)).thenReturn(true)
+    whenever(desktopRepository.isClosingTask(task.taskId)).thenReturn(false)
+
+    desktopTaskChangeListener.onTaskClosing(task)
+
+    verify(desktopRepository).updateTaskVisibility(task.displayId, task.taskId, visible = false)
+    verify(desktopRepository).minimizeTask(task.displayId, task.taskId)
+  }
+
+  @Test
+  @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION)
+  fun onTaskClosing_backNavDisabled_closingTask_removesTaskInRepo() {
+    val task = createFreeformTask().apply { isVisible = true }
+    whenever(desktopRepository.isActiveTask(task.taskId)).thenReturn(true)
+    whenever(desktopRepository.isClosingTask(task.taskId)).thenReturn(true)
+
+    desktopTaskChangeListener.onTaskClosing(task)
+
+    verify(desktopRepository, never()).minimizeTask(task.displayId, task.taskId)
+    verify(desktopRepository).removeClosingTask(task.taskId)
+    verify(desktopRepository).removeFreeformTask(task.displayId, task.taskId)
+  }
+
+  @Test
+  @EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION)
+  fun onTaskClosing_backNavEnabled_closingTask_removesTaskFromRepo() {
+    val task = createFreeformTask().apply { isVisible = true }
+    whenever(desktopRepository.isActiveTask(task.taskId)).thenReturn(true)
+    whenever(desktopRepository.isClosingTask(task.taskId)).thenReturn(true)
+
+    desktopTaskChangeListener.onTaskClosing(task)
+
+    verify(desktopRepository).removeClosingTask(task.taskId)
+    verify(desktopRepository).removeFreeformTask(task.displayId, task.taskId)
+  }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskListenerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskListenerTests.java
index 8dd1545..0a5397a 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskListenerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskListenerTests.java
@@ -23,13 +23,17 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
 import static com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION;
+import static com.android.window.flags.Flags.FLAG_ENABLE_WINDOWING_TRANSITION_HANDLERS_OBSERVERS;
+import static com.android.window.flags.Flags.FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION;
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.app.ActivityManager;
+import android.platform.test.annotations.DisableFlags;
 import android.platform.test.annotations.EnableFlags;
 import android.platform.test.flag.junit.SetFlagsRule;
 import android.view.SurfaceControl;
@@ -59,9 +63,8 @@
 import java.util.Optional;
 
 /**
- * Tests for {@link FreeformTaskListener}
- * Build/Install/Run:
- * atest WMShellUnitTests:FreeformTaskListenerTests
+ * Tests for {@link FreeformTaskListener} Build/Install/Run: atest
+ * WMShellUnitTests:FreeformTaskListenerTests
  */
 @SmallTest
 @RunWith(AndroidJUnit4.class)
@@ -84,41 +87,96 @@
     private DesktopTasksController mDesktopTasksController;
     @Mock
     private LaunchAdjacentController mLaunchAdjacentController;
+    @Mock
+    private TaskChangeListener mTaskChangeListener;
+
     private FreeformTaskListener mFreeformTaskListener;
     private StaticMockitoSession mMockitoSession;
 
     @Before
     public void setup() {
-        mMockitoSession = mockitoSession().initMocks(this)
-                .strictness(Strictness.LENIENT).mockStatic(DesktopModeStatus.class).startMocking();
+        mMockitoSession =
+                mockitoSession()
+                        .initMocks(this)
+                        .strictness(Strictness.LENIENT)
+                        .mockStatic(DesktopModeStatus.class)
+                        .startMocking();
         doReturn(true).when(() -> DesktopModeStatus.canEnterDesktopMode(any()));
 
-        mFreeformTaskListener = new FreeformTaskListener(
-                mContext,
-                mShellInit,
-                mTaskOrganizer,
-                Optional.of(mDesktopRepository),
-                Optional.of(mDesktopTasksController),
-                mLaunchAdjacentController,
-                mWindowDecorViewModel);
+        mFreeformTaskListener =
+                new FreeformTaskListener(
+                        mContext,
+                        mShellInit,
+                        mTaskOrganizer,
+                        Optional.of(mDesktopRepository),
+                        Optional.of(mDesktopTasksController),
+                        mLaunchAdjacentController,
+                        mWindowDecorViewModel,
+                        Optional.of(mTaskChangeListener));
     }
 
     @Test
-    public void testFocusTaskChanged_freeformTaskIsAddedToRepo() {
-        ActivityManager.RunningTaskInfo task = new TestRunningTaskInfoBuilder()
-                .setWindowingMode(WINDOWING_MODE_FREEFORM).build();
+    @DisableFlags(FLAG_ENABLE_WINDOWING_TRANSITION_HANDLERS_OBSERVERS)
+    public void onTaskAppeared_noTransitionObservers_visibleTask_addsTaskToRepo() {
+        ActivityManager.RunningTaskInfo task =
+                new TestRunningTaskInfoBuilder().setWindowingMode(WINDOWING_MODE_FREEFORM).build();
+        task.isVisible = true;
+
+        mFreeformTaskListener.onTaskAppeared(task, mMockSurfaceControl);
+
+        verify(mDesktopRepository).addOrMoveFreeformTaskToTop(task.displayId, task.taskId);
+        verify(mDesktopRepository).addActiveTask(task.displayId, task.taskId);
+        verify(mDesktopRepository)
+                .updateTaskVisibility(task.displayId, task.taskId, task.isVisible);
+    }
+
+    @Test
+    @DisableFlags(FLAG_ENABLE_WINDOWING_TRANSITION_HANDLERS_OBSERVERS)
+    public void onTaskAppeared_noTransitionObservers_nonVisibleTask_addsTaskToRepo() {
+        ActivityManager.RunningTaskInfo task =
+                new TestRunningTaskInfoBuilder().setWindowingMode(WINDOWING_MODE_FREEFORM).build();
+        task.isVisible = false;
+
+        mFreeformTaskListener.onTaskAppeared(task, mMockSurfaceControl);
+
+        verify(mDesktopRepository).addOrMoveFreeformTaskToTop(task.displayId, task.taskId);
+        verify(mDesktopRepository, never()).addActiveTask(task.displayId, task.taskId);
+        verify(mDesktopRepository, never())
+                .updateTaskVisibility(task.displayId, task.taskId, task.isVisible);
+    }
+
+    @Test
+    @EnableFlags(FLAG_ENABLE_WINDOWING_TRANSITION_HANDLERS_OBSERVERS)
+    public void onTaskAppeared_useTransitionObserver_noopInRepository() {
+        ActivityManager.RunningTaskInfo task =
+                new TestRunningTaskInfoBuilder().setWindowingMode(WINDOWING_MODE_FREEFORM).build();
+        task.isVisible = true;
+
+        mFreeformTaskListener.onTaskAppeared(task, mMockSurfaceControl);
+
+        verify(mDesktopRepository, never()).addOrMoveFreeformTaskToTop(task.displayId, task.taskId);
+        verify(mDesktopRepository, never()).addActiveTask(task.displayId, task.taskId);
+        verify(mDesktopRepository, never())
+                .updateTaskVisibility(task.displayId, task.taskId, task.isVisible);
+    }
+
+    @Test
+    public void focusTaskChanged_addsFreeformTaskToRepo() {
+        ActivityManager.RunningTaskInfo task =
+                new TestRunningTaskInfoBuilder().setWindowingMode(WINDOWING_MODE_FREEFORM).build();
         task.isFocused = true;
 
         mFreeformTaskListener.onFocusTaskChanged(task);
 
-        verify(mDesktopRepository)
-            .addOrMoveFreeformTaskToTop(task.displayId, task.taskId);
+        verify(mDesktopRepository).addOrMoveFreeformTaskToTop(task.displayId, task.taskId);
     }
 
     @Test
-    public void testFocusTaskChanged_fullscreenTaskIsNotAddedToRepo() {
-        ActivityManager.RunningTaskInfo fullscreenTask = new TestRunningTaskInfoBuilder()
-                .setWindowingMode(WINDOWING_MODE_FULLSCREEN).build();
+    public void focusTaskChanged_fullscreenTaskNotAddedToRepo() {
+        ActivityManager.RunningTaskInfo fullscreenTask =
+                new TestRunningTaskInfoBuilder()
+                        .setWindowingMode(WINDOWING_MODE_FULLSCREEN)
+                        .build();
         fullscreenTask.isFocused = true;
 
         mFreeformTaskListener.onFocusTaskChanged(fullscreenTask);
@@ -128,9 +186,9 @@
     }
 
     @Test
-    public void testVisibilityTaskChanged_visible_setLaunchAdjacentDisabled() {
-        ActivityManager.RunningTaskInfo task = new TestRunningTaskInfoBuilder()
-                .setWindowingMode(WINDOWING_MODE_FREEFORM).build();
+    public void visibilityTaskChanged_visible_setLaunchAdjacentDisabled() {
+        ActivityManager.RunningTaskInfo task =
+                new TestRunningTaskInfoBuilder().setWindowingMode(WINDOWING_MODE_FREEFORM).build();
         task.isVisible = true;
 
         mFreeformTaskListener.onTaskAppeared(task, mMockSurfaceControl);
@@ -139,9 +197,9 @@
     }
 
     @Test
-    public void testVisibilityTaskChanged_NotVisible_setLaunchAdjacentEnabled() {
-        ActivityManager.RunningTaskInfo task = new TestRunningTaskInfoBuilder()
-                .setWindowingMode(WINDOWING_MODE_FREEFORM).build();
+    public void visibilityTaskChanged_notVisible_setLaunchAdjacentEnabled() {
+        ActivityManager.RunningTaskInfo task =
+                new TestRunningTaskInfoBuilder().setWindowingMode(WINDOWING_MODE_FREEFORM).build();
         task.isVisible = true;
 
         mFreeformTaskListener.onTaskAppeared(task, mMockSurfaceControl);
@@ -154,9 +212,10 @@
 
     @Test
     @EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION)
-    public void onTaskVanished_nonClosingTask_isMinimized() {
-        ActivityManager.RunningTaskInfo task = new TestRunningTaskInfoBuilder()
-                .setWindowingMode(WINDOWING_MODE_FREEFORM).build();
+    @DisableFlags(FLAG_ENABLE_WINDOWING_TRANSITION_HANDLERS_OBSERVERS)
+    public void onTaskVanished_nonClosingTask_noTransitionObservers_isMinimized() {
+        ActivityManager.RunningTaskInfo task =
+                new TestRunningTaskInfoBuilder().setWindowingMode(WINDOWING_MODE_FREEFORM).build();
         task.isVisible = true;
 
         mFreeformTaskListener.onTaskAppeared(task, mMockSurfaceControl);
@@ -169,10 +228,11 @@
     }
 
     @Test
+    @DisableFlags(FLAG_ENABLE_WINDOWING_TRANSITION_HANDLERS_OBSERVERS)
     @EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION)
-    public void onTaskVanished_closingTask_isNotMinimized() {
-        ActivityManager.RunningTaskInfo task = new TestRunningTaskInfoBuilder()
-                .setWindowingMode(WINDOWING_MODE_FREEFORM).build();
+    public void onTaskVanished_closingTask_noTransitionObservers_isNotMinimized() {
+        ActivityManager.RunningTaskInfo task =
+                new TestRunningTaskInfoBuilder().setWindowingMode(WINDOWING_MODE_FREEFORM).build();
         task.isVisible = true;
 
         mFreeformTaskListener.onTaskAppeared(task, mMockSurfaceControl);
@@ -188,9 +248,23 @@
     }
 
     @Test
+    @EnableFlags(FLAG_ENABLE_WINDOWING_TRANSITION_HANDLERS_OBSERVERS)
+    public void onTaskVanished_usesTransitionObservers_noopInRepo() {
+        ActivityManager.RunningTaskInfo task =
+                new TestRunningTaskInfoBuilder().setWindowingMode(WINDOWING_MODE_FREEFORM).build();
+        mFreeformTaskListener.onTaskAppeared(task, mMockSurfaceControl);
+
+        mFreeformTaskListener.onTaskVanished(task);
+
+        verify(mDesktopRepository, never()).minimizeTask(task.displayId, task.taskId);
+        verify(mDesktopRepository, never()).removeClosingTask(task.taskId);
+        verify(mDesktopRepository, never()).removeFreeformTask(task.displayId, task.taskId);
+    }
+
+    @Test
     public void onTaskInfoChanged_withDesktopController_forwards() {
-        ActivityManager.RunningTaskInfo task = new TestRunningTaskInfoBuilder()
-                .setWindowingMode(WINDOWING_MODE_FREEFORM).build();
+        ActivityManager.RunningTaskInfo task =
+                new TestRunningTaskInfoBuilder().setWindowingMode(WINDOWING_MODE_FREEFORM).build();
         task.isVisible = true;
         mFreeformTaskListener.onTaskAppeared(task, mMockSurfaceControl);
 
@@ -199,6 +273,38 @@
         verify(mDesktopTasksController).onTaskInfoChanged(task);
     }
 
+    @Test
+    @DisableFlags(FLAG_ENABLE_WINDOWING_TRANSITION_HANDLERS_OBSERVERS)
+    public void onTaskInfoChanged_noTransitionObservers_updatesTaskVisibility() {
+        ActivityManager.RunningTaskInfo task =
+                new TestRunningTaskInfoBuilder().setWindowingMode(WINDOWING_MODE_FREEFORM).build();
+        task.isVisible = true;
+        mFreeformTaskListener.onTaskAppeared(task, mMockSurfaceControl);
+
+        mFreeformTaskListener.onTaskInfoChanged(task);
+
+        verify(mTaskChangeListener, never()).onTaskChanging(any());
+        verify(mDesktopRepository, times(2)).addActiveTask(task.displayId, task.taskId);
+        verify(mDesktopRepository, times(2))
+                .updateTaskVisibility(task.displayId, task.taskId, task.isVisible);
+    }
+
+    @Test
+    @EnableFlags(FLAG_ENABLE_WINDOWING_TRANSITION_HANDLERS_OBSERVERS)
+    @DisableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION)
+    public void onTaskInfoChanged_useTransitionObserver_noopInRepository() {
+        ActivityManager.RunningTaskInfo task =
+                new TestRunningTaskInfoBuilder().setWindowingMode(WINDOWING_MODE_FREEFORM).build();
+        task.isVisible = true;
+        mFreeformTaskListener.onTaskAppeared(task, mMockSurfaceControl);
+
+        mFreeformTaskListener.onTaskInfoChanged(task);
+
+        verify(mTaskChangeListener).onNonTransitionTaskChanging(any());
+        verify(mDesktopRepository, never())
+                .updateTaskVisibility(task.displayId, task.taskId, task.isVisible);
+    }
+
     @After
     public void tearDown() {
         mMockitoSession.finishMocking();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java
index 90ab2b8..5aed461 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java
@@ -58,25 +58,17 @@
 
 import java.util.Optional;
 
-/**
- * Tests for {@link FreeformTaskTransitionObserver}.
- */
+/** Tests for {@link FreeformTaskTransitionObserver}. */
 @SmallTest
 public class FreeformTaskTransitionObserverTest {
 
     public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
-    @Mock
-    private ShellInit mShellInit;
-    @Mock
-    private Transitions mTransitions;
-    @Mock
-    private DesktopImmersiveController mDesktopImmersiveController;
-    @Mock
-    private WindowDecorViewModel mWindowDecorViewModel;
-    @Mock
-    private TaskChangeListener mTaskChangeListener;
-    @Mock
-    private FocusTransitionObserver mFocusTransitionObserver;
+    @Mock private ShellInit mShellInit;
+    @Mock private Transitions mTransitions;
+    @Mock private DesktopImmersiveController mDesktopImmersiveController;
+    @Mock private WindowDecorViewModel mWindowDecorViewModel;
+    @Mock private TaskChangeListener mTaskChangeListener;
+    @Mock private FocusTransitionObserver mFocusTransitionObserver;
 
     private FreeformTaskTransitionObserver mTransitionObserver;
 
@@ -85,20 +77,22 @@
         MockitoAnnotations.initMocks(this);
 
         PackageManager pm = mock(PackageManager.class);
-        doReturn(true).when(pm).hasSystemFeature(
-            PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT);
+        doReturn(true).when(pm).hasSystemFeature(PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT);
         final Context context = mock(Context.class);
         doReturn(pm).when(context).getPackageManager();
 
-        mTransitionObserver = new FreeformTaskTransitionObserver(
-                context, mShellInit, mTransitions,
-                Optional.of(mDesktopImmersiveController),
-                mWindowDecorViewModel, Optional.of(mTaskChangeListener), mFocusTransitionObserver);
+        mTransitionObserver =
+                new FreeformTaskTransitionObserver(
+                        context,
+                        mShellInit,
+                        mTransitions,
+                        Optional.of(mDesktopImmersiveController),
+                        mWindowDecorViewModel,
+                        Optional.of(mTaskChangeListener),
+                        mFocusTransitionObserver);
 
-        final ArgumentCaptor<Runnable> initRunnableCaptor = ArgumentCaptor.forClass(
-                Runnable.class);
-        verify(mShellInit).addInitCallback(initRunnableCaptor.capture(),
-                same(mTransitionObserver));
+        final ArgumentCaptor<Runnable> initRunnableCaptor = ArgumentCaptor.forClass(Runnable.class);
+        verify(mShellInit).addInitCallback(initRunnableCaptor.capture(), same(mTransitionObserver));
         initRunnableCaptor.getValue().run();
     }
 
@@ -109,10 +103,9 @@
 
     @Test
     public void openTransition_createsWindowDecor() {
-        final TransitionInfo.Change change =
-                createChange(TRANSIT_OPEN, 1, WINDOWING_MODE_FREEFORM);
-        final TransitionInfo info = new TransitionInfoBuilder(TRANSIT_OPEN, 0)
-                .addChange(change).build();
+        final TransitionInfo.Change change = createChange(TRANSIT_OPEN, 1, WINDOWING_MODE_FREEFORM);
+        final TransitionInfo info =
+                new TransitionInfoBuilder(TRANSIT_OPEN, 0).addChange(change).build();
 
         final IBinder transition = mock(IBinder.class);
         final SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class);
@@ -120,16 +113,15 @@
         mTransitionObserver.onTransitionReady(transition, info, startT, finishT);
         mTransitionObserver.onTransitionStarting(transition);
 
-        verify(mWindowDecorViewModel).onTaskOpening(
-                change.getTaskInfo(), change.getLeash(), startT, finishT);
+        verify(mWindowDecorViewModel)
+                .onTaskOpening(change.getTaskInfo(), change.getLeash(), startT, finishT);
     }
 
     @Test
     public void openTransition_notifiesOnTaskOpening() {
-        final TransitionInfo.Change change =
-                createChange(TRANSIT_OPEN, 1, WINDOWING_MODE_FREEFORM);
-        final TransitionInfo info = new TransitionInfoBuilder(TRANSIT_OPEN, 0)
-                .addChange(change).build();
+        final TransitionInfo.Change change = createChange(TRANSIT_OPEN, 1, WINDOWING_MODE_FREEFORM);
+        final TransitionInfo info =
+                new TransitionInfoBuilder(TRANSIT_OPEN, 0).addChange(change).build();
 
         final IBinder transition = mock(IBinder.class);
         final SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class);
@@ -144,8 +136,10 @@
     public void toFrontTransition_notifiesOnTaskMovingToFront() {
         final TransitionInfo.Change change =
                 createChange(TRANSIT_TO_FRONT, /* taskId= */ 1, WINDOWING_MODE_FREEFORM);
-        final TransitionInfo info = new TransitionInfoBuilder(TRANSIT_TO_FRONT, /* flags= */ 0)
-                .addChange(change).build();
+        final TransitionInfo info =
+                new TransitionInfoBuilder(TRANSIT_TO_FRONT, /* flags= */ 0)
+                        .addChange(change)
+                        .build();
 
         final IBinder transition = mock(IBinder.class);
         final SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class);
@@ -160,8 +154,10 @@
     public void toBackTransition_notifiesOnTaskMovingToBack() {
         final TransitionInfo.Change change =
                 createChange(TRANSIT_TO_BACK, /* taskId= */ 1, WINDOWING_MODE_FREEFORM);
-        final TransitionInfo info = new TransitionInfoBuilder(TRANSIT_TO_BACK, /* flags= */ 0)
-                .addChange(change).build();
+        final TransitionInfo info =
+                new TransitionInfoBuilder(TRANSIT_TO_BACK, /* flags= */ 0)
+                        .addChange(change)
+                        .build();
 
         final IBinder transition = mock(IBinder.class);
         final SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class);
@@ -173,11 +169,11 @@
     }
 
     @Test
-    public void changeTransition_notifiesOnTaskChanging() {
+    public void changeTransition_notifiesOnTaskChange() {
         final TransitionInfo.Change change =
                 createChange(TRANSIT_CHANGE, /* taskId= */ 1, WINDOWING_MODE_FREEFORM);
-        final TransitionInfo info = new TransitionInfoBuilder(TRANSIT_CHANGE, /* flags= */ 0)
-                .addChange(change).build();
+        final TransitionInfo info =
+                new TransitionInfoBuilder(TRANSIT_CHANGE, /* flags= */ 0).addChange(change).build();
 
         final IBinder transition = mock(IBinder.class);
         final SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class);
@@ -192,8 +188,8 @@
     public void closeTransition_preparesWindowDecor() {
         final TransitionInfo.Change change =
                 createChange(TRANSIT_CLOSE, 1, WINDOWING_MODE_FREEFORM);
-        final TransitionInfo info = new TransitionInfoBuilder(TRANSIT_CLOSE, 0)
-                .addChange(change).build();
+        final TransitionInfo info =
+                new TransitionInfoBuilder(TRANSIT_CLOSE, 0).addChange(change).build();
 
         final IBinder transition = mock(IBinder.class);
         final SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class);
@@ -201,16 +197,15 @@
         mTransitionObserver.onTransitionReady(transition, info, startT, finishT);
         mTransitionObserver.onTransitionStarting(transition);
 
-        verify(mWindowDecorViewModel).onTaskClosing(
-                change.getTaskInfo(), startT, finishT);
+        verify(mWindowDecorViewModel).onTaskClosing(change.getTaskInfo(), startT, finishT);
     }
 
     @Test
     public void closeTransition_notifiesOnTaskClosing() {
         final TransitionInfo.Change change =
                 createChange(TRANSIT_CLOSE, 1, WINDOWING_MODE_FREEFORM);
-        final TransitionInfo info = new TransitionInfoBuilder(TRANSIT_CLOSE, 0)
-                .addChange(change).build();
+        final TransitionInfo info =
+                new TransitionInfoBuilder(TRANSIT_CLOSE, 0).addChange(change).build();
 
         final IBinder transition = mock(IBinder.class);
         final SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class);
@@ -225,8 +220,8 @@
     public void closeTransition_doesntCloseWindowDecorDuringTransition() throws Exception {
         final TransitionInfo.Change change =
                 createChange(TRANSIT_CLOSE, 1, WINDOWING_MODE_FREEFORM);
-        final TransitionInfo info = new TransitionInfoBuilder(TRANSIT_CLOSE, 0)
-                .addChange(change).build();
+        final TransitionInfo info =
+                new TransitionInfoBuilder(TRANSIT_CLOSE, 0).addChange(change).build();
 
         final IBinder transition = mock(IBinder.class);
         final SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class);
@@ -241,8 +236,8 @@
     public void closeTransition_closesWindowDecorAfterTransition() throws Exception {
         final TransitionInfo.Change change =
                 createChange(TRANSIT_CLOSE, 1, WINDOWING_MODE_FREEFORM);
-        final TransitionInfo info = new TransitionInfoBuilder(TRANSIT_CLOSE, 0)
-                .addChange(change).build();
+        final TransitionInfo info =
+                new TransitionInfoBuilder(TRANSIT_CLOSE, 0).addChange(change).build();
 
         final AutoCloseable windowDecor = mock(AutoCloseable.class);
 
@@ -261,8 +256,8 @@
         // The playing transition
         final TransitionInfo.Change change1 =
                 createChange(TRANSIT_OPEN, 1, WINDOWING_MODE_FREEFORM);
-        final TransitionInfo info1 = new TransitionInfoBuilder(TRANSIT_OPEN, 0)
-                .addChange(change1).build();
+        final TransitionInfo info1 =
+                new TransitionInfoBuilder(TRANSIT_OPEN, 0).addChange(change1).build();
 
         final IBinder transition1 = mock(IBinder.class);
         final SurfaceControl.Transaction startT1 = mock(SurfaceControl.Transaction.class);
@@ -273,8 +268,8 @@
         // The merged transition
         final TransitionInfo.Change change2 =
                 createChange(TRANSIT_CLOSE, 2, WINDOWING_MODE_FREEFORM);
-        final TransitionInfo info2 = new TransitionInfoBuilder(TRANSIT_CLOSE, 0)
-                .addChange(change2).build();
+        final TransitionInfo info2 =
+                new TransitionInfoBuilder(TRANSIT_CLOSE, 0).addChange(change2).build();
 
         final IBinder transition2 = mock(IBinder.class);
         final SurfaceControl.Transaction startT2 = mock(SurfaceControl.Transaction.class);
@@ -292,8 +287,8 @@
         // The playing transition
         final TransitionInfo.Change change1 =
                 createChange(TRANSIT_CLOSE, 1, WINDOWING_MODE_FREEFORM);
-        final TransitionInfo info1 = new TransitionInfoBuilder(TRANSIT_CLOSE, 0)
-                .addChange(change1).build();
+        final TransitionInfo info1 =
+                new TransitionInfoBuilder(TRANSIT_CLOSE, 0).addChange(change1).build();
 
         final IBinder transition1 = mock(IBinder.class);
         final SurfaceControl.Transaction startT1 = mock(SurfaceControl.Transaction.class);
@@ -304,8 +299,8 @@
         // The merged transition
         final TransitionInfo.Change change2 =
                 createChange(TRANSIT_CLOSE, 2, WINDOWING_MODE_FREEFORM);
-        final TransitionInfo info2 = new TransitionInfoBuilder(TRANSIT_CLOSE, 0)
-                .addChange(change2).build();
+        final TransitionInfo info2 =
+                new TransitionInfoBuilder(TRANSIT_CLOSE, 0).addChange(change2).build();
 
         final IBinder transition2 = mock(IBinder.class);
         final SurfaceControl.Transaction startT2 = mock(SurfaceControl.Transaction.class);
@@ -368,9 +363,10 @@
         taskInfo.taskId = taskId;
         taskInfo.configuration.windowConfiguration.setWindowingMode(windowingMode);
 
-        final TransitionInfo.Change change = new TransitionInfo.Change(
-                new WindowContainerToken(mock(IWindowContainerToken.class)),
-                mock(SurfaceControl.class));
+        final TransitionInfo.Change change =
+                new TransitionInfo.Change(
+                        new WindowContainerToken(mock(IWindowContainerToken.class)),
+                        mock(SurfaceControl.class));
         change.setMode(mode);
         change.setTaskInfo(taskInfo);
         return change;
diff --git a/packages/CrashRecovery/framework/Android.bp b/packages/CrashRecovery/framework/Android.bp
index 1be776d..2beffda 100644
--- a/packages/CrashRecovery/framework/Android.bp
+++ b/packages/CrashRecovery/framework/Android.bp
@@ -6,7 +6,25 @@
     ],
     path: "java",
     visibility: [
-        "//frameworks/base:__subpackages__",
         "//packages/modules/CrashRecovery/framework",
     ],
 }
+
+java_sdk_library {
+    name: "framework-platformcrashrecovery",
+    srcs: [":framework-crashrecovery-sources"],
+    defaults: ["framework-non-updatable-unbundled-defaults"],
+    aidl: {
+        include_dirs: [
+            "frameworks/base/core/java",
+        ],
+    },
+    impl_library_visibility: [
+        "//frameworks/base:__subpackages__",
+    ],
+}
+
+platform_compat_config {
+    name: "framework-platformcrashrecovery-compat-config",
+    src: ":framework-platformcrashrecovery",
+}
diff --git a/packages/CrashRecovery/framework/api/current.txt b/packages/CrashRecovery/framework/api/current.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/packages/CrashRecovery/framework/api/current.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/packages/CrashRecovery/framework/api/module-lib-current.txt b/packages/CrashRecovery/framework/api/module-lib-current.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/packages/CrashRecovery/framework/api/module-lib-current.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/packages/CrashRecovery/framework/api/module-lib-removed.txt b/packages/CrashRecovery/framework/api/module-lib-removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/packages/CrashRecovery/framework/api/module-lib-removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/packages/CrashRecovery/framework/api/removed.txt b/packages/CrashRecovery/framework/api/removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/packages/CrashRecovery/framework/api/removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/packages/CrashRecovery/framework/api/system-current.txt b/packages/CrashRecovery/framework/api/system-current.txt
new file mode 100644
index 0000000..3a48a4a
--- /dev/null
+++ b/packages/CrashRecovery/framework/api/system-current.txt
@@ -0,0 +1,26 @@
+// Signature format: 2.0
+package android.service.watchdog {
+
+  public abstract class ExplicitHealthCheckService extends android.app.Service {
+    ctor public ExplicitHealthCheckService();
+    method public final void notifyHealthCheckPassed(@NonNull String);
+    method @NonNull public final android.os.IBinder onBind(@NonNull android.content.Intent);
+    method public abstract void onCancelHealthCheck(@NonNull String);
+    method @NonNull public abstract java.util.List<java.lang.String> onGetRequestedPackages();
+    method @NonNull public abstract java.util.List<android.service.watchdog.ExplicitHealthCheckService.PackageConfig> onGetSupportedPackages();
+    method public abstract void onRequestHealthCheck(@NonNull String);
+    field public static final String BIND_PERMISSION = "android.permission.BIND_EXPLICIT_HEALTH_CHECK_SERVICE";
+    field public static final String SERVICE_INTERFACE = "android.service.watchdog.ExplicitHealthCheckService";
+  }
+
+  public static final class ExplicitHealthCheckService.PackageConfig implements android.os.Parcelable {
+    ctor public ExplicitHealthCheckService.PackageConfig(@NonNull String, long);
+    method public int describeContents();
+    method public long getHealthCheckTimeoutMillis();
+    method @NonNull public String getPackageName();
+    method public void writeToParcel(android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.service.watchdog.ExplicitHealthCheckService.PackageConfig> CREATOR;
+  }
+
+}
+
diff --git a/packages/CrashRecovery/framework/api/system-lint-baseline.txt b/packages/CrashRecovery/framework/api/system-lint-baseline.txt
new file mode 100644
index 0000000..f52be7c
--- /dev/null
+++ b/packages/CrashRecovery/framework/api/system-lint-baseline.txt
@@ -0,0 +1,47 @@
+// Baseline format: 1.0
+InvalidNullabilityOverride: android.service.watchdog.ExplicitHealthCheckService#onBind(android.content.Intent):
+    Invalid nullability on type android.content.Intent in parameter `intent` in method `onBind`. Parameter in method override cannot use a non-null type when the corresponding type from the super method is platform-nullness.
+InvalidNullabilityOverride: android.service.watchdog.ExplicitHealthCheckService#onBind(android.content.Intent) parameter #0:
+    Invalid nullability on type android.content.Intent in parameter `intent` in method `onBind`. Parameter in method override cannot use a non-null type when the corresponding type from the super method is platform-nullness.
+
+
+MissingNullability: android.service.watchdog.ExplicitHealthCheckService.PackageConfig#writeToParcel(android.os.Parcel,int):
+    Missing nullability on parameter `parcel` in method `writeToParcel`
+
+
+UnflaggedApi: android.service.watchdog.ExplicitHealthCheckService:
+    New API must be flagged with @FlaggedApi: class android.service.watchdog.ExplicitHealthCheckService
+UnflaggedApi: android.service.watchdog.ExplicitHealthCheckService#BIND_PERMISSION:
+    New API must be flagged with @FlaggedApi: field android.service.watchdog.ExplicitHealthCheckService.BIND_PERMISSION
+UnflaggedApi: android.service.watchdog.ExplicitHealthCheckService#SERVICE_INTERFACE:
+    New API must be flagged with @FlaggedApi: field android.service.watchdog.ExplicitHealthCheckService.SERVICE_INTERFACE
+UnflaggedApi: android.service.watchdog.ExplicitHealthCheckService#notifyHealthCheckPassed(String):
+    New API must be flagged with @FlaggedApi: method android.service.watchdog.ExplicitHealthCheckService.notifyHealthCheckPassed(String)
+UnflaggedApi: android.service.watchdog.ExplicitHealthCheckService#onBind(android.content.Intent):
+    New API must be flagged with @FlaggedApi: method android.service.watchdog.ExplicitHealthCheckService.onBind(android.content.Intent)
+UnflaggedApi: android.service.watchdog.ExplicitHealthCheckService#onCancelHealthCheck(String):
+    New API must be flagged with @FlaggedApi: method android.service.watchdog.ExplicitHealthCheckService.onCancelHealthCheck(String)
+UnflaggedApi: android.service.watchdog.ExplicitHealthCheckService#onGetRequestedPackages():
+    New API must be flagged with @FlaggedApi: method android.service.watchdog.ExplicitHealthCheckService.onGetRequestedPackages()
+UnflaggedApi: android.service.watchdog.ExplicitHealthCheckService#onGetSupportedPackages():
+    New API must be flagged with @FlaggedApi: method android.service.watchdog.ExplicitHealthCheckService.onGetSupportedPackages()
+UnflaggedApi: android.service.watchdog.ExplicitHealthCheckService#onRequestHealthCheck(String):
+    New API must be flagged with @FlaggedApi: method android.service.watchdog.ExplicitHealthCheckService.onRequestHealthCheck(String)
+UnflaggedApi: android.service.watchdog.ExplicitHealthCheckService.PackageConfig:
+    New API must be flagged with @FlaggedApi: class android.service.watchdog.ExplicitHealthCheckService.PackageConfig
+UnflaggedApi: android.service.watchdog.ExplicitHealthCheckService.PackageConfig#CREATOR:
+    New API must be flagged with @FlaggedApi: field android.service.watchdog.ExplicitHealthCheckService.PackageConfig.CREATOR
+UnflaggedApi: android.service.watchdog.ExplicitHealthCheckService.PackageConfig#PackageConfig(String, long):
+    New API must be flagged with @FlaggedApi: constructor android.service.watchdog.ExplicitHealthCheckService.PackageConfig(String,long)
+UnflaggedApi: android.service.watchdog.ExplicitHealthCheckService.PackageConfig#PackageConfig(String,long):
+    New API must be flagged with @FlaggedApi: constructor android.service.watchdog.ExplicitHealthCheckService.PackageConfig(String,long)
+UnflaggedApi: android.service.watchdog.ExplicitHealthCheckService.PackageConfig#describeContents():
+    New API must be flagged with @FlaggedApi: method android.service.watchdog.ExplicitHealthCheckService.PackageConfig.describeContents()
+UnflaggedApi: android.service.watchdog.ExplicitHealthCheckService.PackageConfig#getHealthCheckTimeoutMillis():
+    New API must be flagged with @FlaggedApi: method android.service.watchdog.ExplicitHealthCheckService.PackageConfig.getHealthCheckTimeoutMillis()
+UnflaggedApi: android.service.watchdog.ExplicitHealthCheckService.PackageConfig#getPackageName():
+    New API must be flagged with @FlaggedApi: method android.service.watchdog.ExplicitHealthCheckService.PackageConfig.getPackageName()
+UnflaggedApi: android.service.watchdog.ExplicitHealthCheckService.PackageConfig#writeToParcel(android.os.Parcel, int):
+    New API must be flagged with @FlaggedApi: method android.service.watchdog.ExplicitHealthCheckService.PackageConfig.writeToParcel(android.os.Parcel,int)
+UnflaggedApi: android.service.watchdog.ExplicitHealthCheckService.PackageConfig#writeToParcel(android.os.Parcel,int):
+    New API must be flagged with @FlaggedApi: method android.service.watchdog.ExplicitHealthCheckService.PackageConfig.writeToParcel(android.os.Parcel,int)
\ No newline at end of file
diff --git a/packages/CrashRecovery/framework/api/system-removed.txt b/packages/CrashRecovery/framework/api/system-removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/packages/CrashRecovery/framework/api/system-removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/packages/CrashRecovery/framework/api/test-current.txt b/packages/CrashRecovery/framework/api/test-current.txt
new file mode 100644
index 0000000..54f501f
--- /dev/null
+++ b/packages/CrashRecovery/framework/api/test-current.txt
@@ -0,0 +1,9 @@
+// Signature format: 2.0
+package android.service.watchdog {
+
+  public abstract class ExplicitHealthCheckService extends android.app.Service {
+    method public void setCallback(@Nullable android.os.RemoteCallback);
+  }
+
+}
+
diff --git a/packages/CrashRecovery/framework/api/test-removed.txt b/packages/CrashRecovery/framework/api/test-removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/packages/CrashRecovery/framework/api/test-removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/packages/CrashRecovery/services/module/java/com/android/server/PackageWatchdog.java b/packages/CrashRecovery/services/module/java/com/android/server/PackageWatchdog.java
index 8e5ae20..fbf51fd 100644
--- a/packages/CrashRecovery/services/module/java/com/android/server/PackageWatchdog.java
+++ b/packages/CrashRecovery/services/module/java/com/android/server/PackageWatchdog.java
@@ -19,6 +19,7 @@
 import static android.content.Intent.ACTION_REBOOT;
 import static android.content.Intent.ACTION_SHUTDOWN;
 import static android.service.watchdog.ExplicitHealthCheckService.PackageConfig;
+import static android.util.Xml.Encoding.UTF_8;
 
 import static com.android.server.crashrecovery.CrashRecoveryUtils.dumpCrashRecoveryEvents;
 
@@ -58,13 +59,14 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.FastXmlSerializer;
 import com.android.modules.utils.BackgroundThread;
-import com.android.modules.utils.TypedXmlPullParser;
-import com.android.modules.utils.TypedXmlSerializer;
 
 import libcore.io.IoUtils;
 
+import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
 
 import java.io.BufferedReader;
 import java.io.BufferedWriter;
@@ -1152,7 +1154,8 @@
         mAllObservers.clear();
         try {
             infile = mPolicyFile.openRead();
-            final TypedXmlPullParser parser = Xml.resolvePullParser(infile);
+            final XmlPullParser parser = Xml.newPullParser();
+            parser.setInput(infile, UTF_8.name());
             XmlUtils.beginDocument(parser, TAG_PACKAGE_WATCHDOG);
             int outerDepth = parser.getDepth();
             while (XmlUtils.nextElementWithin(parser, outerDepth)) {
@@ -1163,7 +1166,7 @@
             }
         } catch (FileNotFoundException e) {
             // Nothing to monitor
-        } catch (IOException | NumberFormatException | XmlPullParserException e) {
+        } catch (Exception e) {
             Slog.wtf(TAG, "Unable to read monitored packages, deleting file", e);
             mPolicyFile.delete();
         } finally {
@@ -1237,10 +1240,11 @@
             }
 
             try {
-                TypedXmlSerializer out = Xml.resolveSerializer(stream);
+                XmlSerializer out = new FastXmlSerializer();
+                out.setOutput(stream, UTF_8.name());
                 out.startDocument(null, true);
                 out.startTag(null, TAG_PACKAGE_WATCHDOG);
-                out.attributeInt(null, ATTR_VERSION, DB_VERSION);
+                out.attribute(null, ATTR_VERSION, Integer.toString(DB_VERSION));
                 for (int oIndex = 0; oIndex < mAllObservers.size(); oIndex++) {
                     mAllObservers.valueAt(oIndex).writeLocked(out);
                 }
@@ -1356,12 +1360,12 @@
          * Does not persist any package failure thresholds.
          */
         @GuardedBy("mLock")
-        public boolean writeLocked(TypedXmlSerializer out) {
+        public boolean writeLocked(XmlSerializer out) {
             try {
                 out.startTag(null, TAG_OBSERVER);
                 out.attribute(null, ATTR_NAME, name);
                 if (Flags.recoverabilityDetection()) {
-                    out.attributeInt(null, ATTR_MITIGATION_COUNT, mMitigationCount);
+                    out.attribute(null, ATTR_MITIGATION_COUNT, Integer.toString(mMitigationCount));
                 }
                 for (int i = 0; i < mPackages.size(); i++) {
                     MonitoredPackage p = mPackages.valueAt(i);
@@ -1486,7 +1490,7 @@
          * #loadFromFile which in turn is only called on construction of the
          * singleton PackageWatchdog.
          **/
-        public static ObserverInternal read(TypedXmlPullParser parser, PackageWatchdog watchdog) {
+        public static ObserverInternal read(XmlPullParser parser, PackageWatchdog watchdog) {
             String observerName = null;
             int observerMitigationCount = 0;
             if (TAG_OBSERVER.equals(parser.getName())) {
@@ -1501,9 +1505,9 @@
             try {
                 if (Flags.recoverabilityDetection()) {
                     try {
-                        observerMitigationCount =
-                                parser.getAttributeInt(null, ATTR_MITIGATION_COUNT);
-                    } catch (XmlPullParserException e) {
+                        observerMitigationCount = Integer.parseInt(
+                                parser.getAttributeValue(null, ATTR_MITIGATION_COUNT));
+                    } catch (Exception e) {
                         Slog.i(
                             TAG,
                             "ObserverInternal mitigation count was not present.");
@@ -1579,13 +1583,14 @@
                 hasPassedHealthCheck, mitigationCalls);
     }
 
-    MonitoredPackage parseMonitoredPackage(TypedXmlPullParser parser)
+    MonitoredPackage parseMonitoredPackage(XmlPullParser parser)
             throws XmlPullParserException {
         String packageName = parser.getAttributeValue(null, ATTR_NAME);
-        long duration = parser.getAttributeLong(null, ATTR_DURATION);
-        long healthCheckDuration = parser.getAttributeLong(null,
-                        ATTR_EXPLICIT_HEALTH_CHECK_DURATION);
-        boolean hasPassedHealthCheck = parser.getAttributeBoolean(null, ATTR_PASSED_HEALTH_CHECK);
+        long duration = Long.parseLong(parser.getAttributeValue(null, ATTR_DURATION));
+        long healthCheckDuration = Long.parseLong(parser.getAttributeValue(null,
+                ATTR_EXPLICIT_HEALTH_CHECK_DURATION));
+        boolean hasPassedHealthCheck = Boolean.parseBoolean(parser.getAttributeValue(null,
+                ATTR_PASSED_HEALTH_CHECK));
         LongArrayQueue mitigationCalls = parseLongArrayQueue(
                 parser.getAttributeValue(null, ATTR_MITIGATION_CALLS));
         return newMonitoredPackage(packageName,
@@ -1643,12 +1648,13 @@
          * @hide
          */
         @GuardedBy("mLock")
-        public void writeLocked(TypedXmlSerializer out) throws IOException {
+        public void writeLocked(XmlSerializer out) throws IOException {
             out.startTag(null, TAG_PACKAGE);
             out.attribute(null, ATTR_NAME, getName());
-            out.attributeLong(null, ATTR_DURATION, mDurationMs);
-            out.attributeLong(null, ATTR_EXPLICIT_HEALTH_CHECK_DURATION, mHealthCheckDurationMs);
-            out.attributeBoolean(null, ATTR_PASSED_HEALTH_CHECK, mHasPassedHealthCheck);
+            out.attribute(null, ATTR_DURATION, Long.toString(mDurationMs));
+            out.attribute(null, ATTR_EXPLICIT_HEALTH_CHECK_DURATION,
+                    Long.toString(mHealthCheckDurationMs));
+            out.attribute(null, ATTR_PASSED_HEALTH_CHECK, Boolean.toString(mHasPassedHealthCheck));
             LongArrayQueue normalizedCalls = normalizeMitigationCalls();
             out.attribute(null, ATTR_MITIGATION_CALLS, longArrayQueueToString(normalizedCalls));
             out.endTag(null, TAG_PACKAGE);
diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/HandlerExecutor.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/HandlerExecutor.kt
new file mode 100644
index 0000000..be08606
--- /dev/null
+++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/HandlerExecutor.kt
@@ -0,0 +1,45 @@
+/*
+ * 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.settingslib.datastore
+
+import android.os.Handler
+import android.os.Looper
+import java.util.concurrent.Executor
+
+/**
+ * Adapter of [Handler] and [Executor], where the task is executed on handler with given looper.
+ *
+ * When current looper is same with the given looper, task passed to [Executor.execute] will be
+ * executed immediately to improve better performance.
+ *
+ * @param looper Looper of the handler.
+ */
+open class HandlerExecutor(looper: Looper) : Handler(looper), Executor {
+
+    override fun execute(command: Runnable) {
+        if (looper == Looper.myLooper()) {
+            command.run()
+        } else {
+            post(command)
+        }
+    }
+
+    companion object {
+        /** The main thread [HandlerExecutor]. */
+        val main = HandlerExecutor(Looper.getMainLooper())
+    }
+}
diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SettingsStore.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SettingsStore.kt
index 62d3fc3..04d4bfe 100644
--- a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SettingsStore.kt
+++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SettingsStore.kt
@@ -19,8 +19,6 @@
 import android.content.ContentResolver
 import android.database.ContentObserver
 import android.net.Uri
-import android.os.Handler
-import android.os.Looper
 import android.util.Log
 import java.util.concurrent.Executor
 import java.util.concurrent.atomic.AtomicInteger
@@ -39,7 +37,7 @@
     private val counter = AtomicInteger()
 
     private val contentObserver =
-        object : ContentObserver(Handler(Looper.getMainLooper())) {
+        object : ContentObserver(HandlerExecutor.main) {
             override fun onChange(selfChange: Boolean) {
                 super.onChange(selfChange, null)
             }
diff --git a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt
index d99d470..ad4cc33 100644
--- a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt
+++ b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt
@@ -19,12 +19,11 @@
 import android.content.Context
 import android.content.Intent
 import android.os.Bundle
-import android.os.Handler
-import android.os.Looper
 import androidx.preference.Preference
 import androidx.preference.PreferenceDataStore
 import androidx.preference.PreferenceGroup
 import androidx.preference.PreferenceScreen
+import com.android.settingslib.datastore.HandlerExecutor
 import com.android.settingslib.datastore.KeyValueStore
 import com.android.settingslib.datastore.KeyedDataObservable
 import com.android.settingslib.datastore.KeyedObservable
@@ -37,7 +36,6 @@
 import com.android.settingslib.metadata.PreferenceScreenRegistry
 import com.google.common.collect.ImmutableMap
 import com.google.common.collect.ImmutableMultimap
-import java.util.concurrent.Executor
 
 /**
  * Helper to bind preferences on given [preferenceScreen].
@@ -54,13 +52,7 @@
     private val preferenceHierarchy: PreferenceHierarchy,
 ) : KeyedDataObservable<String>() {
 
-    private val handler = Handler(Looper.getMainLooper())
-    private val executor =
-        object : Executor {
-            override fun execute(command: Runnable) {
-                handler.post(command)
-            }
-        }
+    private val mainExecutor = HandlerExecutor.main
 
     private val preferenceLifecycleContext =
         object : PreferenceLifecycleContext(context) {
@@ -121,8 +113,8 @@
         this.lifecycleAwarePreferences = lifecycleAwarePreferences.toTypedArray()
 
         preferenceObserver = KeyedObserver { key, reason -> onPreferenceChange(key, reason) }
-        addObserver(preferenceObserver, executor)
-        for (storage in storages) storage.addObserver(storageObserver, executor)
+        addObserver(preferenceObserver, mainExecutor)
+        for (storage in storages) storage.addObserver(storageObserver, mainExecutor)
     }
 
     private fun onPreferenceChange(key: String?, reason: Int) {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/slider/SeekbarHapticPluginTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/slider/HapticSliderPluginTest.kt
similarity index 92%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/slider/SeekbarHapticPluginTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/slider/HapticSliderPluginTest.kt
index 587d3d9..0881010 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/slider/SeekbarHapticPluginTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/slider/HapticSliderPluginTest.kt
@@ -45,14 +45,14 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 @OptIn(ExperimentalCoroutinesApi::class)
-class SeekbarHapticPluginTest : SysuiTestCase() {
+class HapticSliderPluginTest : SysuiTestCase() {
 
     private val kosmos = Kosmos()
 
     @Rule @JvmField val mMockitoRule: MockitoRule = MockitoJUnit.rule()
     @Mock private lateinit var vibratorHelper: VibratorHelper
     private val seekBar = SeekBar(mContext)
-    private lateinit var plugin: SeekbarHapticPlugin
+    private lateinit var plugin: HapticSliderPlugin
 
     @Before
     fun setup() {
@@ -95,7 +95,7 @@
         // GIVEN an onKeyDown that starts the wait and a program progress change that advances the
         // slider state to ARROW_HANDLE_MOVED_ONCE
         plugin.onKeyDown()
-        plugin.onProgressChanged(seekBar, 50, false)
+        plugin.onProgressChanged(50, false)
         testScheduler.runCurrent()
         assertThat(plugin.trackerState).isEqualTo(SliderState.ARROW_HANDLE_MOVED_ONCE)
 
@@ -112,7 +112,7 @@
         // GIVEN an onKeyDown that starts the wait and a program progress change that advances the
         // slider state to ARROW_HANDLE_MOVED_ONCE
         plugin.onKeyDown()
-        plugin.onProgressChanged(seekBar, 50, false)
+        plugin.onProgressChanged(50, false)
         testScheduler.runCurrent()
         assertThat(plugin.trackerState).isEqualTo(SliderState.ARROW_HANDLE_MOVED_ONCE)
 
@@ -142,7 +142,13 @@
         }
 
     private fun createPlugin() {
-        plugin = SeekbarHapticPlugin(vibratorHelper, kosmos.msdlPlayer, kosmos.fakeSystemClock)
+        plugin =
+            HapticSliderPlugin(
+                vibratorHelper,
+                kosmos.msdlPlayer,
+                kosmos.fakeSystemClock,
+                HapticSlider.SeekBar(seekBar),
+            )
     }
 
     companion object {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/settings/brightness/BrightnessSliderControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/settings/brightness/BrightnessSliderControllerTest.kt
index 080f46f..119abd1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/settings/brightness/BrightnessSliderControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/settings/brightness/BrightnessSliderControllerTest.kt
@@ -24,7 +24,8 @@
 import com.android.settingslib.RestrictedLockUtils
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.classifier.FalsingManagerFake
-import com.android.systemui.haptics.slider.SeekbarHapticPlugin
+import com.android.systemui.haptics.slider.HapticSlider
+import com.android.systemui.haptics.slider.HapticSliderPlugin
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.statusbar.VibratorHelper
 import com.android.systemui.statusbar.policy.BrightnessMirrorController
@@ -86,7 +87,12 @@
                 brightnessSliderView,
                 mFalsingManager,
                 uiEventLogger,
-                SeekbarHapticPlugin(vibratorHelper, msdlPlayer, systemClock),
+                HapticSliderPlugin(
+                    vibratorHelper,
+                    msdlPlayer,
+                    systemClock,
+                    HapticSlider.SeekBar(seekBar),
+                ),
                 activityStarter,
             )
         mController.init()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModelTest.kt
index 46f3a6b..1adfc2b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModelTest.kt
@@ -18,6 +18,7 @@
 
 package com.android.systemui.statusbar.notification.footer.ui.viewmodel
 
+import android.platform.test.annotations.DisableFlags
 import android.platform.test.annotations.EnableFlags
 import android.platform.test.flag.junit.FlagsParameterization
 import android.provider.Settings
@@ -40,6 +41,7 @@
 import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository
 import com.android.systemui.statusbar.notification.emptyshade.shared.ModesEmptyShadeFix
 import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor
+import com.android.systemui.statusbar.notification.footer.shared.NotifRedesignFooter
 import com.android.systemui.testKosmos
 import com.android.systemui.util.ui.isAnimating
 import com.android.systemui.util.ui.value
@@ -230,6 +232,7 @@
         }
 
     @Test
+    @DisableFlags(NotifRedesignFooter.FLAG_NAME)
     fun manageButton_whenHistoryDisabled() =
         testScope.runTest {
             val buttonLabel by collectLastValue(underTest.manageOrHistoryButton.labelId)
@@ -243,6 +246,7 @@
         }
 
     @Test
+    @DisableFlags(NotifRedesignFooter.FLAG_NAME)
     fun historyButton_whenHistoryEnabled() =
         testScope.runTest {
             val buttonLabel by collectLastValue(underTest.manageOrHistoryButton.labelId)
@@ -255,8 +259,9 @@
             assertThat(buttonLabel).isEqualTo(R.string.manage_notifications_history_text)
         }
 
-    @EnableFlags(ModesEmptyShadeFix.FLAG_NAME)
     @Test
+    @EnableFlags(ModesEmptyShadeFix.FLAG_NAME)
+    @DisableFlags(NotifRedesignFooter.FLAG_NAME)
     fun manageButtonOnClick_whenHistoryDisabled() =
         testScope.runTest {
             val onClick by collectLastValue(underTest.manageOrHistoryButtonClick)
@@ -271,8 +276,9 @@
             assertThat(onClick?.backStack).isEmpty()
         }
 
-    @EnableFlags(ModesEmptyShadeFix.FLAG_NAME)
     @Test
+    @EnableFlags(ModesEmptyShadeFix.FLAG_NAME)
+    @DisableFlags(NotifRedesignFooter.FLAG_NAME)
     fun historyButtonOnClick_whenHistoryEnabled() =
         testScope.runTest {
             val onClick by collectLastValue(underTest.manageOrHistoryButtonClick)
@@ -289,6 +295,7 @@
         }
 
     @Test
+    @DisableFlags(NotifRedesignFooter.FLAG_NAME)
     fun manageButtonVisible_whenMessageVisible() =
         testScope.runTest {
             val visible by collectLastValue(underTest.manageOrHistoryButton.isVisible)
@@ -299,6 +306,7 @@
         }
 
     @Test
+    @DisableFlags(NotifRedesignFooter.FLAG_NAME)
     fun manageButtonVisible_whenMessageNotVisible() =
         testScope.runTest {
             val visible by collectLastValue(underTest.manageOrHistoryButton.isVisible)
@@ -307,4 +315,30 @@
 
             assertThat(visible?.value).isTrue()
         }
+
+    @Test
+    @EnableFlags(NotifRedesignFooter.FLAG_NAME)
+    fun settingsAndHistoryButtonsNotVisible_whenMessageVisible() =
+        testScope.runTest {
+            val settingsVisible by collectLastValue(underTest.settingsButtonVisible)
+            val historyVisible by collectLastValue(underTest.historyButtonVisible)
+
+            activeNotificationListRepository.hasFilteredOutSeenNotifications.value = true
+
+            assertThat(settingsVisible).isFalse()
+            assertThat(historyVisible).isFalse()
+        }
+
+    @Test
+    @EnableFlags(NotifRedesignFooter.FLAG_NAME)
+    fun settingsAndHistoryButtonsNotVisible_whenMessageNotVisible() =
+        testScope.runTest {
+            val settingsVisible by collectLastValue(underTest.settingsButtonVisible)
+            val historyVisible by collectLastValue(underTest.historyButtonVisible)
+
+            activeNotificationListRepository.hasFilteredOutSeenNotifications.value = false
+
+            assertThat(settingsVisible).isTrue()
+            assertThat(historyVisible).isTrue()
+        }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java
index 81c40dc..8ec17da 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java
@@ -261,6 +261,7 @@
         when(enr.getPrivateLayout()).thenReturn(privateLayout);
         when(enr.getEntry()).thenReturn(enrEntry);
         when(enr.isChildInGroup()).thenReturn(false);
+        when(enr.isPinned()).thenReturn(false);
         when(enr.isExpanded()).thenReturn(false);
 
         // WHEN
@@ -287,6 +288,7 @@
         when(enr.getPrivateLayout()).thenReturn(privateLayout);
         when(enr.getEntry()).thenReturn(enrEntry);
         when(enr.isChildInGroup()).thenReturn(false);
+        when(enr.isPinned()).thenReturn(false);
         when(enr.isExpanded()).thenReturn(true);
 
         // WHEN
@@ -299,4 +301,58 @@
         verify(enr, never()).setUserExpanded(anyBoolean());
         verify(mGroupExpansionManager, never()).toggleGroupExpansion(any());
     }
+
+    @Test
+    @EnableFlags(ExpandHeadsUpOnInlineReply.FLAG_NAME)
+    public void onMakeExpandedVisibleForRemoteInput_notExpandedPinnedHUN_toggleExpansion() {
+        // GIVEN
+        final Runnable onExpandedVisibleRunner = mock(Runnable.class);
+
+        final ExpandableNotificationRow enr = mock(ExpandableNotificationRow.class);
+        final NotificationContentView privateLayout = mock(NotificationContentView.class);
+        final NotificationEntry enrEntry = mock(NotificationEntry.class);
+
+        when(enr.getPrivateLayout()).thenReturn(privateLayout);
+        when(enr.getEntry()).thenReturn(enrEntry);
+        when(enr.isChildInGroup()).thenReturn(false);
+        when(enr.isPinned()).thenReturn(true);
+        when(enr.isPinnedAndExpanded()).thenReturn(false);
+
+        // WHEN
+        mRemoteInputCallback.onMakeExpandedVisibleForRemoteInput(
+                enr, mock(View.class), false, onExpandedVisibleRunner);
+
+        // THEN
+        verify(enr).toggleExpansionState();
+        verify(privateLayout).setOnExpandedVisibleListener(onExpandedVisibleRunner);
+        verify(enr, never()).setUserExpanded(anyBoolean());
+        verify(mGroupExpansionManager, never()).toggleGroupExpansion(any());
+    }
+
+    @Test
+    @EnableFlags(ExpandHeadsUpOnInlineReply.FLAG_NAME)
+    public void onMakeExpandedVisibleForRemoteInput_expandedPinnedHUN_notToggleExpansion() {
+        // GIVEN
+        final Runnable onExpandedVisibleRunner = mock(Runnable.class);
+
+        final ExpandableNotificationRow enr = mock(ExpandableNotificationRow.class);
+        final NotificationContentView privateLayout = mock(NotificationContentView.class);
+        final NotificationEntry enrEntry = mock(NotificationEntry.class);
+
+        when(enr.getPrivateLayout()).thenReturn(privateLayout);
+        when(enr.getEntry()).thenReturn(enrEntry);
+        when(enr.isChildInGroup()).thenReturn(false);
+        when(enr.isPinned()).thenReturn(true);
+        when(enr.isPinnedAndExpanded()).thenReturn(true);
+
+        // WHEN
+        mRemoteInputCallback.onMakeExpandedVisibleForRemoteInput(
+                enr, mock(View.class), false, onExpandedVisibleRunner);
+
+        // THEN
+        verify(enr, never()).toggleExpansionState();
+        verify(privateLayout, never()).setOnExpandedVisibleListener(onExpandedVisibleRunner);
+        verify(enr, never()).setUserExpanded(anyBoolean());
+        verify(mGroupExpansionManager, never()).toggleGroupExpansion(any());
+    }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index a408211..e564626 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -1102,8 +1102,11 @@
         }
 
         mView.initMode(mode, mGlobalSettings, mFalsingManager, mUserSwitcherController,
-                () -> showMessage(getContext().getString(R.string.keyguard_unlock_to_continue),
-                        /* colorState= */ null, /* animated= */ true), mFalsingA11yDelegate);
+                () -> {
+                        String msg = getContext().getString(R.string.keyguard_unlock_to_continue);
+                        showMessage(msg, /* colorState= */ null, /* animated= */ true);
+                        mBouncerMessageInteractor.setUnlockToContinueMessage(msg);
+                }, mFalsingA11yDelegate);
     }
 
     public void reportFailedUnlockAttempt(int userId, int timeoutMs) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 8b2cf7a..8b59370 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -585,62 +585,68 @@
     }
 
     private void handleSimSubscriptionInfoChanged() {
-        Assert.isMainThread();
         mSimLogger.v("onSubscriptionInfoChanged()");
-        List<SubscriptionInfo> subscriptionInfos = getSubscriptionInfo(true /* forceReload */);
-        if (!subscriptionInfos.isEmpty()) {
-            for (SubscriptionInfo subInfo : subscriptionInfos) {
-                mSimLogger.logSubInfo(subInfo);
-            }
-        } else {
-            mSimLogger.v("onSubscriptionInfoChanged: list is null");
-        }
+        mBackgroundExecutor.execute(() -> {
+            final List<SubscriptionInfo> subscriptionInfos =
+                    getSubscriptionInfo(true /* forceReload */);
+            mMainExecutor.execute(() -> {
+                if (!subscriptionInfos.isEmpty()) {
+                    for (SubscriptionInfo subInfo : subscriptionInfos) {
+                        mSimLogger.logSubInfo(subInfo);
+                    }
+                } else {
+                    mSimLogger.v("onSubscriptionInfoChanged: list is null");
+                }
 
-        // Hack level over 9000: Because the subscription id is not yet valid when we see the
-        // first update in handleSimStateChange, we need to force refresh all SIM states
-        // so the subscription id for them is consistent.
-        ArrayList<SubscriptionInfo> changedSubscriptions = new ArrayList<>();
-        Set<Integer> activeSubIds = new HashSet<>();
-        for (int i = 0; i < subscriptionInfos.size(); i++) {
-            SubscriptionInfo info = subscriptionInfos.get(i);
-            activeSubIds.add(info.getSubscriptionId());
-            boolean changed = refreshSimState(info.getSubscriptionId(), info.getSimSlotIndex());
-            if (changed) {
-                changedSubscriptions.add(info);
-            }
-        }
+                // Hack level over 9000: Because the subscription id is not yet valid when we see
+                // the first update in handleSimStateChange, we need to force refresh all SIM states
+                // so the subscription id for them is consistent.
+                ArrayList<SubscriptionInfo> changedSubscriptions = new ArrayList<>();
+                Set<Integer> activeSubIds = new HashSet<>();
+                for (int i = 0; i < subscriptionInfos.size(); i++) {
+                    SubscriptionInfo info = subscriptionInfos.get(i);
+                    activeSubIds.add(info.getSubscriptionId());
+                    boolean changed = refreshSimState(info.getSubscriptionId(),
+                            info.getSimSlotIndex());
+                    if (changed) {
+                        changedSubscriptions.add(info);
+                    }
+                }
 
-        // It is possible for active subscriptions to become invalid (-1), and these will not be
-        // present in the subscriptionInfo list
-        synchronized (mSimDataLockObject) {
-            Iterator<Map.Entry<Integer, SimData>> iter = mSimDatas.entrySet().iterator();
-            while (iter.hasNext()) {
-                Map.Entry<Integer, SimData> simData = iter.next();
-                if (!activeSubIds.contains(simData.getKey())) {
-                    mSimLogger.logInvalidSubId(simData.getKey());
-                    iter.remove();
+                // It is possible for active subscriptions to become invalid (-1), and these will
+                // not be present in the subscriptionInfo list
+                synchronized (mSimDataLockObject) {
+                    Iterator<Map.Entry<Integer, SimData>> iter = mSimDatas.entrySet().iterator();
+                    while (iter.hasNext()) {
+                        Map.Entry<Integer, SimData> simData = iter.next();
+                        if (!activeSubIds.contains(simData.getKey())) {
+                            mSimLogger.logInvalidSubId(simData.getKey());
+                            iter.remove();
 
-                    SimData data = simData.getValue();
-                    for (int j = 0; j < mCallbacks.size(); j++) {
-                        KeyguardUpdateMonitorCallback cb = mCallbacks.get(j).get();
-                        if (cb != null) {
-                            cb.onSimStateChanged(data.subId, data.slotId, data.simState);
+                            SimData data = simData.getValue();
+                            for (int j = 0; j < mCallbacks.size(); j++) {
+                                KeyguardUpdateMonitorCallback cb = mCallbacks.get(j).get();
+                                if (cb != null) {
+                                    cb.onSimStateChanged(data.subId, data.slotId, data.simState);
+                                }
+                            }
                         }
                     }
-                }
-            }
 
-            for (int i = 0; i < changedSubscriptions.size(); i++) {
-                SimData data = mSimDatas.get(changedSubscriptions.get(i).getSubscriptionId());
-                for (int j = 0; j < mCallbacks.size(); j++) {
-                    KeyguardUpdateMonitorCallback cb = mCallbacks.get(j).get();
-                    if (cb != null) {
-                        cb.onSimStateChanged(data.subId, data.slotId, data.simState);
+                    for (int i = 0; i < changedSubscriptions.size(); i++) {
+                        SimData data = mSimDatas.get(
+                                changedSubscriptions.get(i).getSubscriptionId());
+                        for (int j = 0; j < mCallbacks.size(); j++) {
+                            KeyguardUpdateMonitorCallback cb = mCallbacks.get(j).get();
+                            if (cb != null) {
+                                cb.onSimStateChanged(data.subId, data.slotId, data.simState);
+                            }
+                        }
                     }
+                    callbacksRefreshCarrierInfo();
                 }
-            }
-            callbacksRefreshCarrierInfo();
-        }
+            });
+        });
     }
 
     private void handleAirplaneModeChanged() {
@@ -2523,6 +2529,11 @@
         if (mUserTracker.isUserSwitching()) {
             handleUserSwitching(mUserTracker.getUserId(), () -> {});
         }
+
+        // Force the cache to be initialized
+        mBackgroundExecutor.execute(() -> {
+            getSubscriptionInfo(/* forceReload= */ true);
+        });
     }
 
     @VisibleForTesting
@@ -3851,11 +3862,14 @@
      * @see #isSimPinSecure(int)
      */
     public boolean isSimPinSecure() {
-        // True if any SIM is pin secure
-        for (SubscriptionInfo info : getSubscriptionInfo(false /* forceReload */)) {
-            if (isSimPinSecure(getSimState(info.getSubscriptionId()))) return true;
+        synchronized (mSimDataLockObject) {
+            for (SimData data : mSimDatas.values()) {
+                if (isSimPinSecure(data.simState)) {
+                    return true;
+                }
+            }
+            return false;
         }
-        return false;
     }
 
     public int getSimState(int subId) {
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractor.kt
index d125c36..e52ddb2 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractor.kt
@@ -81,7 +81,7 @@
         deviceEntryBiometricsAllowedInteractor.isFingerprintCurrentlyAllowedOnBouncer.stateIn(
             applicationScope,
             SharingStarted.Eagerly,
-            false
+            false,
         )
 
     private val currentSecurityMode
@@ -114,13 +114,13 @@
                         BiometricSourceType.FACE ->
                             BouncerMessageStrings.incorrectFaceInput(
                                     currentSecurityMode.toAuthModel(),
-                                    isFingerprintAuthCurrentlyAllowedOnBouncer.value
+                                    isFingerprintAuthCurrentlyAllowedOnBouncer.value,
                                 )
                                 .toMessage()
                         else ->
                             BouncerMessageStrings.defaultMessage(
                                     currentSecurityMode.toAuthModel(),
-                                    isFingerprintAuthCurrentlyAllowedOnBouncer.value
+                                    isFingerprintAuthCurrentlyAllowedOnBouncer.value,
                                 )
                                 .toMessage()
                     },
@@ -130,7 +130,7 @@
 
             override fun onBiometricAcquired(
                 biometricSourceType: BiometricSourceType?,
-                acquireInfo: Int
+                acquireInfo: Int,
             ) {
                 if (
                     repository.getMessageSource() == BiometricSourceType.FACE &&
@@ -143,7 +143,7 @@
             override fun onBiometricAuthenticated(
                 userId: Int,
                 biometricSourceType: BiometricSourceType?,
-                isStrongBiometric: Boolean
+                isStrongBiometric: Boolean,
             ) {
                 repository.setMessage(defaultMessage, biometricSourceType)
             }
@@ -169,7 +169,7 @@
                 deviceEntryBiometricsAllowedInteractor.isFingerprintLockedOut,
                 deviceEntryBiometricsAllowedInteractor.isFaceLockedOut,
                 isFingerprintAuthCurrentlyAllowedOnBouncer,
-                ::Septuple
+                ::Septuple,
             )
             .map { (_, flags, _, biometricsEnrolledAndEnabled, fpLockedOut, faceLockedOut, _) ->
                 val isTrustUsuallyManaged = trustRepository.isCurrentUserTrustUsuallyManaged.value
@@ -220,14 +220,14 @@
                     } else {
                         BouncerMessageStrings.faceLockedOut(
                                 currentSecurityMode.toAuthModel(),
-                                isFingerprintAuthCurrentlyAllowedOnBouncer.value
+                                isFingerprintAuthCurrentlyAllowedOnBouncer.value,
                             )
                             .toMessage()
                     }
                 } else if (flags.isSomeAuthRequiredAfterAdaptiveAuthRequest) {
                     BouncerMessageStrings.authRequiredAfterAdaptiveAuthRequest(
                             currentSecurityMode.toAuthModel(),
-                            isFingerprintAuthCurrentlyAllowedOnBouncer.value
+                            isFingerprintAuthCurrentlyAllowedOnBouncer.value,
                         )
                         .toMessage()
                 } else if (
@@ -236,19 +236,19 @@
                 ) {
                     BouncerMessageStrings.nonStrongAuthTimeout(
                             currentSecurityMode.toAuthModel(),
-                            isFingerprintAuthCurrentlyAllowedOnBouncer.value
+                            isFingerprintAuthCurrentlyAllowedOnBouncer.value,
                         )
                         .toMessage()
                 } else if (isTrustUsuallyManaged && flags.someAuthRequiredAfterUserRequest) {
                     BouncerMessageStrings.trustAgentDisabled(
                             currentSecurityMode.toAuthModel(),
-                            isFingerprintAuthCurrentlyAllowedOnBouncer.value
+                            isFingerprintAuthCurrentlyAllowedOnBouncer.value,
                         )
                         .toMessage()
                 } else if (isTrustUsuallyManaged && flags.someAuthRequiredAfterTrustAgentExpired) {
                     BouncerMessageStrings.trustAgentDisabled(
                             currentSecurityMode.toAuthModel(),
-                            isFingerprintAuthCurrentlyAllowedOnBouncer.value
+                            isFingerprintAuthCurrentlyAllowedOnBouncer.value,
                         )
                         .toMessage()
                 } else if (trustOrBiometricsAvailable && flags.isInUserLockdown) {
@@ -292,7 +292,7 @@
         repository.setMessage(
             BouncerMessageStrings.incorrectSecurityInput(
                     currentSecurityMode.toAuthModel(),
-                    isFingerprintAuthCurrentlyAllowedOnBouncer.value
+                    isFingerprintAuthCurrentlyAllowedOnBouncer.value,
                 )
                 .toMessage()
         )
@@ -304,19 +304,30 @@
             defaultMessage(
                 currentSecurityMode,
                 value,
-                isFingerprintAuthCurrentlyAllowedOnBouncer.value
+                isFingerprintAuthCurrentlyAllowedOnBouncer.value,
             ),
             BiometricSourceType.FINGERPRINT,
         )
     }
 
+    fun setUnlockToContinueMessage(value: String) {
+        if (!Flags.revampedBouncerMessages()) return
+        repository.setMessage(
+            defaultMessage(
+                currentSecurityMode,
+                value,
+                isFingerprintAuthCurrentlyAllowedOnBouncer.value,
+            )
+        )
+    }
+
     fun setFaceAcquisitionMessage(value: String?) {
         if (!Flags.revampedBouncerMessages()) return
         repository.setMessage(
             defaultMessage(
                 currentSecurityMode,
                 value,
-                isFingerprintAuthCurrentlyAllowedOnBouncer.value
+                isFingerprintAuthCurrentlyAllowedOnBouncer.value,
             ),
             BiometricSourceType.FACE,
         )
@@ -329,7 +340,7 @@
             defaultMessage(
                 currentSecurityMode,
                 value,
-                isFingerprintAuthCurrentlyAllowedOnBouncer.value
+                isFingerprintAuthCurrentlyAllowedOnBouncer.value,
             )
         )
     }
@@ -338,7 +349,7 @@
         get() =
             BouncerMessageStrings.defaultMessage(
                     currentSecurityMode.toAuthModel(),
-                    isFingerprintAuthCurrentlyAllowedOnBouncer.value
+                    isFingerprintAuthCurrentlyAllowedOnBouncer.value,
                 )
                 .toMessage()
 
@@ -400,7 +411,7 @@
 private fun defaultMessage(
     securityMode: SecurityMode,
     secondaryMessage: String?,
-    fpAuthIsAllowed: Boolean
+    fpAuthIsAllowed: Boolean,
 ): BouncerMessageModel {
     return BouncerMessageModel(
         message =
@@ -408,21 +419,21 @@
                 messageResId =
                     BouncerMessageStrings.defaultMessage(
                             securityMode.toAuthModel(),
-                            fpAuthIsAllowed
+                            fpAuthIsAllowed,
                         )
                         .toMessage()
                         .message
                         ?.messageResId,
-                animate = false
+                animate = false,
             ),
-        secondaryMessage = Message(message = secondaryMessage, animate = false)
+        secondaryMessage = Message(message = secondaryMessage, animate = false),
     )
 }
 
 private fun Pair<Int, Int>.toMessage(): BouncerMessageModel {
     return BouncerMessageModel(
         message = Message(messageResId = this.first, animate = false),
-        secondaryMessage = Message(messageResId = this.second, animate = false)
+        secondaryMessage = Message(messageResId = this.second, animate = false),
     )
 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/complication/dagger/RegisteredComplicationsModule.java b/packages/SystemUI/src/com/android/systemui/complication/dagger/RegisteredComplicationsModule.java
index 6f1b098..9970c5d 100644
--- a/packages/SystemUI/src/com/android/systemui/complication/dagger/RegisteredComplicationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/complication/dagger/RegisteredComplicationsModule.java
@@ -20,7 +20,6 @@
 import android.view.ViewGroup;
 
 import com.android.systemui.complication.ComplicationLayoutParams;
-import com.android.systemui.dagger.SystemUIBinder;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.flags.Flags;
@@ -32,8 +31,7 @@
 import javax.inject.Named;
 
 /**
- * Module for all components with corresponding dream layer complications registered in
- * {@link SystemUIBinder}.
+ * Module for all components with corresponding dream layer complications.
  */
 @Module(
         subcomponents = {
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSysUIComponent.java
index b71af69..b34a240 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSysUIComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSysUIComponent.java
@@ -32,7 +32,6 @@
         DependencyProvider.class,
         NotificationInsetsModule.class,
         QsFrameTranslateModule.class,
-        SystemUIBinder.class,
         SystemUIModule.class,
         SystemUICoreStartableModule.class,
         SysUIUnfoldModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
index b966ad4..bf93469 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
@@ -38,6 +38,7 @@
 import com.android.systemui.emergency.EmergencyGestureModule;
 import com.android.systemui.inputdevice.tutorial.KeyboardTouchpadTutorialModule;
 import com.android.systemui.keyboard.shortcut.ShortcutHelperModule;
+import com.android.systemui.keyguard.dagger.KeyguardModule;
 import com.android.systemui.keyguard.ui.composable.blueprint.DefaultBlueprintModule;
 import com.android.systemui.keyguard.ui.view.layout.blueprints.KeyguardBlueprintModule;
 import com.android.systemui.keyguard.ui.view.layout.sections.KeyguardSectionsModule;
@@ -53,6 +54,7 @@
 import com.android.systemui.reardisplay.RearDisplayModule;
 import com.android.systemui.recents.Recents;
 import com.android.systemui.recents.RecentsImplementation;
+import com.android.systemui.recents.RecentsModule;
 import com.android.systemui.rotationlock.RotationLockModule;
 import com.android.systemui.rotationlock.RotationLockNewModule;
 import com.android.systemui.scene.SceneContainerFrameworkModule;
@@ -68,6 +70,7 @@
 import com.android.systemui.statusbar.NotificationLockscreenUserManagerImpl;
 import com.android.systemui.statusbar.NotificationShadeWindowController;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
+import com.android.systemui.statusbar.dagger.CentralSurfacesModule;
 import com.android.systemui.statusbar.dagger.StartCentralSurfacesModule;
 import com.android.systemui.statusbar.phone.CentralSurfaces;
 import com.android.systemui.statusbar.phone.DozeServiceHost;
@@ -120,6 +123,7 @@
         AccessibilityRepositoryModule.class,
         AospPolicyModule.class,
         BatterySaverModule.class,
+        CentralSurfacesModule.class,
         ClipboardOverlaySuppressionModule.class,
         CollapsedStatusBarFragmentStartableModule.class,
         ConnectingDisplayViewModel.StartableModule.class,
@@ -127,6 +131,7 @@
         EmergencyGestureModule.class,
         GestureModule.class,
         HeadsUpModule.class,
+        KeyguardModule.class,
         KeyboardShortcutsModule.class,
         KeyguardBlueprintModule.class,
         KeyguardSectionsModule.class,
@@ -139,6 +144,7 @@
         PowerModule.class,
         QSModule.class,
         RearDisplayModule.class,
+        RecentsModule.class,
         ReferenceScreenshotModule.class,
         RotationLockModule.class,
         RotationLockNewModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
index 17f1961..580896c 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
@@ -65,7 +65,6 @@
         DependencyProvider.class,
         NotificationInsetsModule.class,
         QsFrameTranslateModule.class,
-        SystemUIBinder.class,
         SystemUIModule.class,
         SystemUICoreStartableModule.class,
         ReferenceSystemUIModule.class})
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
deleted file mode 100644
index 2f041ac..0000000
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2019 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.dagger;
-
-import com.android.systemui.keyguard.dagger.KeyguardModule;
-import com.android.systemui.recents.RecentsModule;
-import com.android.systemui.statusbar.dagger.CentralSurfacesModule;
-
-import dagger.Module;
-
-/**
- * SystemUI objects that are injectable should go here.
- */
-@Module(includes = {
-        RecentsModule.class,
-        CentralSurfacesModule.class,
-        KeyguardModule.class,
-})
-public abstract class SystemUIBinder {
-}
diff --git a/packages/SystemUI/src/com/android/systemui/haptics/slider/HapticSlider.kt b/packages/SystemUI/src/com/android/systemui/haptics/slider/HapticSlider.kt
new file mode 100644
index 0000000..1d226fd
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/haptics/slider/HapticSlider.kt
@@ -0,0 +1,41 @@
+/*
+ * 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.haptics.slider
+
+sealed interface HapticSlider {
+
+    val min: Float
+    val max: Float
+
+    class SeekBar(val seekBar: android.widget.SeekBar) : HapticSlider {
+
+        override val min: Float
+            get() = seekBar.min.toFloat()
+
+        override val max: Float
+            get() = seekBar.max.toFloat()
+    }
+
+    class Slider(val slider: com.google.android.material.slider.Slider) : HapticSlider {
+
+        override val min: Float
+            get() = slider.valueFrom
+
+        override val max: Float
+            get() = slider.valueTo
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/haptics/slider/SeekbarHapticPlugin.kt b/packages/SystemUI/src/com/android/systemui/haptics/slider/HapticSliderPlugin.kt
similarity index 83%
rename from packages/SystemUI/src/com/android/systemui/haptics/slider/SeekbarHapticPlugin.kt
rename to packages/SystemUI/src/com/android/systemui/haptics/slider/HapticSliderPlugin.kt
index f6d7e15..16c53b1 100644
--- a/packages/SystemUI/src/com/android/systemui/haptics/slider/SeekbarHapticPlugin.kt
+++ b/packages/SystemUI/src/com/android/systemui/haptics/slider/HapticSliderPlugin.kt
@@ -18,30 +18,30 @@
 
 import android.view.MotionEvent
 import android.view.VelocityTracker
-import android.widget.SeekBar
 import androidx.annotation.VisibleForTesting
+import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.systemui.statusbar.VibratorHelper
 import com.android.systemui.util.time.SystemClock
 import com.google.android.msdl.domain.MSDLPlayer
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Job
 import kotlinx.coroutines.delay
-import com.android.app.tracing.coroutines.launchTraced as launch
 
 /**
- * A plugin added to a manager of a [android.widget.SeekBar] that adds dynamic haptic feedback.
+ * A plugin added to a manager of a [HapticSlider] that adds dynamic haptic feedback.
  *
  * A [SliderStateProducer] is used as the producer of slider events, a
  * [SliderHapticFeedbackProvider] is used as the listener of slider states to play haptic feedback
  * depending on the state, and a [SliderStateTracker] is used as the state machine handler that
  * tracks and manipulates the slider state.
  */
-class SeekbarHapticPlugin
+class HapticSliderPlugin
 @JvmOverloads
 constructor(
     vibratorHelper: VibratorHelper,
     msdlPlayer: MSDLPlayer,
     systemClock: SystemClock,
+    private val slider: HapticSlider,
     sliderHapticFeedbackConfig: SliderHapticFeedbackConfig = SliderHapticFeedbackConfig(),
     private val sliderTrackerConfig: SeekableSliderTrackerConfig = SeekableSliderTrackerConfig(),
 ) {
@@ -128,46 +128,42 @@
         }
     }
 
-    /** onStartTrackingTouch event from the slider's [android.widget.SeekBar] */
-    fun onStartTrackingTouch(seekBar: SeekBar) {
+    /** onStartTrackingTouch event from the slider. */
+    fun onStartTrackingTouch() {
         if (isTracking) {
             sliderEventProducer.onStartTracking(true)
         }
     }
 
-    /** onProgressChanged event from the slider's [android.widget.SeekBar] */
-    fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
+    /** onProgressChanged event from the slider's. */
+    fun onProgressChanged(progress: Int, fromUser: Boolean) {
         if (isTracking) {
             if (sliderTracker?.currentState == SliderState.IDLE && !fromUser) {
                 // This case translates to the slider starting to track program changes
-                sliderEventProducer.resetWithProgress(normalizeProgress(seekBar, progress))
+                sliderEventProducer.resetWithProgress(normalizeProgress(slider, progress))
                 sliderEventProducer.onStartTracking(false)
             } else {
-                sliderEventProducer.onProgressChanged(
-                    fromUser,
-                    normalizeProgress(seekBar, progress),
-                )
+                sliderEventProducer.onProgressChanged(fromUser, normalizeProgress(slider, progress))
             }
         }
     }
 
     /**
-     * Normalize the integer progress of a SeekBar to the range from 0F to 1F.
+     * Normalize the integer progress of a HapticSlider to the range from 0F to 1F.
      *
-     * @param[seekBar] The SeekBar that reports a progress.
-     * @param[progress] The integer progress of the SeekBar within its min and max values.
+     * @param[slider] The HapticSlider that reports a progress.
+     * @param[progress] The integer progress of the HapticSlider within its min and max values.
      * @return The progress in the range from 0F to 1F.
      */
-    private fun normalizeProgress(seekBar: SeekBar, progress: Int): Float {
-        if (seekBar.max == seekBar.min) {
+    private fun normalizeProgress(slider: HapticSlider, progress: Int): Float {
+        if (slider.max == slider.min) {
             return 1.0f
         }
-        val range = seekBar.max - seekBar.min
-        return (progress - seekBar.min) / range.toFloat()
+        return (progress - slider.min) / (slider.max - slider.min)
     }
 
-    /** onStopTrackingTouch event from the slider's [android.widget.SeekBar] */
-    fun onStopTrackingTouch(seekBar: SeekBar) {
+    /** onStopTrackingTouch event from the slider. */
+    fun onStopTrackingTouch() {
         if (isTracking) {
             sliderEventProducer.onStopTracking(true)
         }
diff --git a/packages/SystemUI/src/com/android/systemui/haptics/slider/HapticSliderViewBinder.kt b/packages/SystemUI/src/com/android/systemui/haptics/slider/HapticSliderViewBinder.kt
index ca6c8da..f43682d 100644
--- a/packages/SystemUI/src/com/android/systemui/haptics/slider/HapticSliderViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/haptics/slider/HapticSliderViewBinder.kt
@@ -23,11 +23,11 @@
 
 object HapticSliderViewBinder {
     /**
-     * Binds a [SeekbarHapticPlugin] to a [View]. The binded view should be a
+     * Binds a [HapticSliderPlugin] to a [View]. The binded view should be a
      * [android.widget.SeekBar] or a container of a [android.widget.SeekBar]
      */
     @JvmStatic
-    fun bind(view: View?, plugin: SeekbarHapticPlugin) {
+    fun bind(view: View?, plugin: HapticSliderPlugin) {
         view?.repeatWhenAttached {
             plugin.startInScope(lifecycleScope)
             try {
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java
index 8703f68..2f7df21 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java
@@ -30,8 +30,9 @@
 import com.android.settingslib.RestrictedLockUtils;
 import com.android.systemui.Gefingerpoken;
 import com.android.systemui.classifier.Classifier;
+import com.android.systemui.haptics.slider.HapticSlider;
+import com.android.systemui.haptics.slider.HapticSliderPlugin;
 import com.android.systemui.haptics.slider.HapticSliderViewBinder;
-import com.android.systemui.haptics.slider.SeekbarHapticPlugin;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.res.R;
@@ -39,7 +40,6 @@
 import com.android.systemui.statusbar.policy.BrightnessMirrorController;
 import com.android.systemui.util.ViewController;
 import com.android.systemui.util.time.SystemClock;
-
 import com.google.android.msdl.domain.MSDLPlayer;
 
 import javax.inject.Inject;
@@ -65,7 +65,7 @@
     private final FalsingManager mFalsingManager;
     private final UiEventLogger mUiEventLogger;
 
-    private final SeekbarHapticPlugin mBrightnessSliderHapticPlugin;
+    private final HapticSliderPlugin mBrightnessSliderHapticPlugin;
     private final ActivityStarter mActivityStarter;
 
     private final Gefingerpoken mOnInterceptListener = new Gefingerpoken() {
@@ -89,7 +89,7 @@
             BrightnessSliderView brightnessSliderView,
             FalsingManager falsingManager,
             UiEventLogger uiEventLogger,
-            SeekbarHapticPlugin brightnessSliderHapticPlugin,
+            HapticSliderPlugin brightnessSliderHapticPlugin,
             ActivityStarter activityStarter) {
         super(brightnessSliderView);
         mFalsingManager = falsingManager;
@@ -240,7 +240,7 @@
             if (mListener != null) {
                 mListener.onChanged(mTracking, progress, false);
                 if (fromUser) {
-                    mBrightnessSliderHapticPlugin.onProgressChanged(seekBar, progress, fromUser);
+                    mBrightnessSliderHapticPlugin.onProgressChanged(progress, true);
                 }
             }
         }
@@ -251,7 +251,7 @@
             mUiEventLogger.log(BrightnessSliderEvent.BRIGHTNESS_SLIDER_STARTED_TRACKING_TOUCH);
             if (mListener != null) {
                 mListener.onChanged(mTracking, getValue(), false);
-                mBrightnessSliderHapticPlugin.onStartTrackingTouch(seekBar);
+                mBrightnessSliderHapticPlugin.onStartTrackingTouch();
             }
 
             if (mMirrorController != null) {
@@ -266,7 +266,7 @@
             mUiEventLogger.log(BrightnessSliderEvent.BRIGHTNESS_SLIDER_STOPPED_TRACKING_TOUCH);
             if (mListener != null) {
                 mListener.onChanged(mTracking, getValue(), true);
-                mBrightnessSliderHapticPlugin.onStopTrackingTouch(seekBar);
+                mBrightnessSliderHapticPlugin.onStopTrackingTouch();
             }
 
             if (mMirrorController != null) {
@@ -317,10 +317,11 @@
             int layout = getLayout();
             BrightnessSliderView root = (BrightnessSliderView) LayoutInflater.from(context)
                     .inflate(layout, viewRoot, false);
-            SeekbarHapticPlugin plugin = new SeekbarHapticPlugin(
+            HapticSliderPlugin plugin = new HapticSliderPlugin(
                     mVibratorHelper,
                     mMSDLPlayer,
-                    mSystemClock);
+                    mSystemClock,
+                    new HapticSlider.SeekBar(root.requireViewById(R.id.slider)));
             HapticSliderViewBinder.bind(viewRoot, plugin);
             return new BrightnessSliderController(
                     root, mFalsingManager, mUiEventLogger, plugin, mActivityStarter);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/shared/NotifRedesignFooter.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/shared/NotifRedesignFooter.kt
new file mode 100644
index 0000000..0307d90
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/shared/NotifRedesignFooter.kt
@@ -0,0 +1,61 @@
+/*
+ * 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.statusbar.notification.footer.shared
+
+import com.android.systemui.Flags
+import com.android.systemui.flags.FlagToken
+import com.android.systemui.flags.RefactorFlagUtils
+
+/** Helper for reading or using the flag state for the footer redesign. */
+@Suppress("NOTHING_TO_INLINE")
+object NotifRedesignFooter {
+    /** The aconfig flag name */
+    const val FLAG_NAME = Flags.FLAG_NOTIFICATIONS_REDESIGN_FOOTER_VIEW
+
+    /** A token used for dependency declaration */
+    val token: FlagToken
+        get() = FlagToken(FLAG_NAME, isEnabled)
+
+    /** Is the refactor enabled */
+    @JvmStatic
+    inline val isEnabled
+        get() = Flags.notificationsRedesignFooterView()
+
+    /**
+     * Called to ensure code is only run when the flag is enabled. This protects users from the
+     * unintended behaviors caused by accidentally running new logic, while also crashing on an eng
+     * build to ensure that the refactor author catches issues in testing.
+     */
+    @JvmStatic
+    inline fun isUnexpectedlyInLegacyMode() =
+        RefactorFlagUtils.isUnexpectedlyInLegacyMode(isEnabled, FLAG_NAME)
+
+    /**
+     * Called to ensure code is only run when the flag is enabled. This will throw an exception if
+     * the flag is not enabled to ensure that the refactor author catches issues in testing.
+     * Caution!! Using this check incorrectly will cause crashes in nextfood builds!
+     */
+    @JvmStatic
+    inline fun assertInNewMode() = RefactorFlagUtils.assertInNewMode(isEnabled, FLAG_NAME)
+
+    /**
+     * Called to ensure code is only run when the flag is disabled. This will throw an exception if
+     * the flag is enabled to ensure that the refactor author catches issues in testing.
+     */
+    @JvmStatic
+    inline fun assertInLegacyMode() = RefactorFlagUtils.assertInLegacyMode(isEnabled, FLAG_NAME)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java
index bf30322..96f47e5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java
@@ -40,10 +40,10 @@
 import androidx.annotation.NonNull;
 
 import com.android.settingslib.Utils;
-import com.android.systemui.Flags;
 import com.android.systemui.res.R;
 import com.android.systemui.statusbar.notification.ColorUpdateLogger;
 import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor;
+import com.android.systemui.statusbar.notification.footer.shared.NotifRedesignFooter;
 import com.android.systemui.statusbar.notification.row.FooterViewButton;
 import com.android.systemui.statusbar.notification.row.StackScrollerDecorView;
 import com.android.systemui.statusbar.notification.stack.AnimationProperties;
@@ -107,11 +107,31 @@
         setClearAllButtonVisible(visible, animate, /* onAnimationEnded = */ null);
     }
 
-    /** Set the visibility of the "Manage"/"History" button to {@code visible}. */
+    /**
+     * Set the visibility of the "Manage"/"History" button to {@code visible}. This is replaced by
+     * two separate buttons in the redesign.
+     */
     public void setManageOrHistoryButtonVisible(boolean visible) {
+        NotifRedesignFooter.assertInLegacyMode();
         mManageOrHistoryButton.setVisibility(visible ? View.VISIBLE : View.GONE);
     }
 
+    /** Set the visibility of the Settings button to {@code visible}. */
+    public void setSettingsButtonVisible(boolean visible) {
+        if (NotifRedesignFooter.isUnexpectedlyInLegacyMode()) {
+            return;
+        }
+        mSettingsButton.setVisibility(visible ? View.VISIBLE : View.GONE);
+    }
+
+    /** Set the visibility of the History button to {@code visible}. */
+    public void setHistoryButtonVisible(boolean visible) {
+        if (NotifRedesignFooter.isUnexpectedlyInLegacyMode()) {
+            return;
+        }
+        mHistoryButton.setVisibility(visible ? View.VISIBLE : View.GONE);
+    }
+
     /**
      * Set the visibility of the "Clear all" button to {@code visible}. Animate the change if
      * {@code animate} is true.
@@ -187,6 +207,7 @@
 
     /** Set the text label for the "Manage"/"History" button. */
     public void setManageOrHistoryButtonText(@StringRes int textId) {
+        NotifRedesignFooter.assertInLegacyMode();
         if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) return;
         if (mManageOrHistoryButtonTextId == textId) {
             return; // nothing changed
@@ -196,6 +217,7 @@
     }
 
     private void updateManageOrHistoryButtonText() {
+        NotifRedesignFooter.assertInLegacyMode();
         if (mManageOrHistoryButtonTextId == 0) {
             return; // not initialized yet
         }
@@ -204,6 +226,7 @@
 
     /** Set the accessibility content description for the "Clear all" button. */
     public void setManageOrHistoryButtonDescription(@StringRes int contentDescriptionId) {
+        NotifRedesignFooter.assertInLegacyMode();
         if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) {
             return;
         }
@@ -215,6 +238,7 @@
     }
 
     private void updateManageOrHistoryButtonDescription() {
+        NotifRedesignFooter.assertInLegacyMode();
         if (mManageOrHistoryButtonDescriptionId == 0) {
             return; // not initialized yet
         }
@@ -273,7 +297,7 @@
         }
         super.onFinishInflate();
         mClearAllButton = (FooterViewButton) findSecondaryView();
-        if (Flags.notificationsRedesignFooterView()) {
+        if (NotifRedesignFooter.isEnabled()) {
             mSettingsButton = findViewById(R.id.settings_button);
             mHistoryButton = findViewById(R.id.history_button);
         } else {
@@ -311,11 +335,17 @@
 
     /** Set onClickListener for the notification settings button. */
     public void setSettingsButtonClickListener(OnClickListener listener) {
+        if (NotifRedesignFooter.isUnexpectedlyInLegacyMode()) {
+            return;
+        }
         mSettingsButton.setOnClickListener(listener);
     }
 
     /** Set onClickListener for the notification history button. */
     public void setHistoryButtonClickListener(OnClickListener listener) {
+        if (NotifRedesignFooter.isUnexpectedlyInLegacyMode()) {
+            return;
+        }
         mHistoryButton.setOnClickListener(listener);
     }
 
@@ -324,6 +354,7 @@
      * in the redesign.
      */
     public void setManageButtonClickListener(OnClickListener listener) {
+        NotifRedesignFooter.assertInLegacyMode();
         mManageOrHistoryButton.setOnClickListener(listener);
     }
 
@@ -364,7 +395,7 @@
             updateClearAllButtonText();
             updateClearAllButtonDescription();
 
-            if (!Flags.notificationsRedesignFooterView()) {
+            if (!NotifRedesignFooter.isEnabled()) {
                 updateManageOrHistoryButtonText();
                 updateManageOrHistoryButtonDescription();
             }
@@ -444,7 +475,7 @@
         }
         mClearAllButton.setBackground(clearAllBg);
         mClearAllButton.setTextColor(onSurface);
-        if (Flags.notificationsRedesignFooterView()) {
+        if (NotifRedesignFooter.isEnabled()) {
             mSettingsButton.setBackground(manageBg);
             mSettingsButton.setCompoundDrawableTintList(ColorStateList.valueOf(onSurface));
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewbinder/FooterViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewbinder/FooterViewBinder.kt
index 34894a2..3383ce9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewbinder/FooterViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewbinder/FooterViewBinder.kt
@@ -20,11 +20,11 @@
 import androidx.lifecycle.lifecycleScope
 import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.internal.jank.InteractionJankMonitor
-import com.android.systemui.Flags
 import com.android.systemui.lifecycle.repeatWhenAttached
 import com.android.systemui.statusbar.notification.NotificationActivityStarter
 import com.android.systemui.statusbar.notification.NotificationActivityStarter.SettingsIntent
 import com.android.systemui.statusbar.notification.emptyshade.shared.ModesEmptyShadeFix
+import com.android.systemui.statusbar.notification.footer.shared.NotifRedesignFooter
 import com.android.systemui.statusbar.notification.footer.ui.view.FooterView
 import com.android.systemui.statusbar.notification.footer.ui.viewmodel.FooterViewModel
 import com.android.systemui.util.ui.isAnimating
@@ -66,7 +66,7 @@
         notificationActivityStarter: NotificationActivityStarter,
     ) = coroutineScope {
         launch { bindClearAllButton(footer, viewModel, clearAllNotifications) }
-        if (!Flags.notificationsRedesignFooterView()) {
+        if (!NotifRedesignFooter.isEnabled) {
             launch {
                 bindManageOrHistoryButton(
                     footer,
@@ -77,8 +77,8 @@
                 )
             }
         } else {
-            bindSettingsButtonListener(footer, notificationActivityStarter)
-            bindHistoryButtonListener(footer, notificationActivityStarter)
+            launch { bindSettingsButton(footer, viewModel, notificationActivityStarter) }
+            launch { bindHistoryButton(footer, viewModel, notificationActivityStarter) }
         }
         launch { bindMessage(footer, viewModel) }
     }
@@ -122,10 +122,11 @@
         }
     }
 
-    private fun bindSettingsButtonListener(
+    private suspend fun bindSettingsButton(
         footer: FooterView,
+        viewModel: FooterViewModel,
         notificationActivityStarter: NotificationActivityStarter,
-    ) {
+    ) = coroutineScope {
         val settingsIntent =
             SettingsIntent.forNotificationSettings(
                 cujType = InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_HISTORY_BUTTON
@@ -134,12 +135,20 @@
             notificationActivityStarter.startSettingsIntent(view, settingsIntent)
         }
         footer.setSettingsButtonClickListener(onClickListener)
+
+        launch {
+            // NOTE: This visibility change is never animated. We also don't need to do anything
+            // special about the onClickListener here, since we're changing the visibility to
+            // GONE so it won't be clickable anyway.
+            viewModel.settingsButtonVisible.collect { footer.setSettingsButtonVisible(it) }
+        }
     }
 
-    private fun bindHistoryButtonListener(
+    private suspend fun bindHistoryButton(
         footer: FooterView,
+        viewModel: FooterViewModel,
         notificationActivityStarter: NotificationActivityStarter,
-    ) {
+    ) = coroutineScope {
         val settingsIntent =
             SettingsIntent.forNotificationHistory(
                 cujType = InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_HISTORY_BUTTON
@@ -148,6 +157,13 @@
             notificationActivityStarter.startSettingsIntent(view, settingsIntent)
         }
         footer.setHistoryButtonClickListener(onClickListener)
+
+        launch {
+            // NOTE: This visibility change is never animated. We also don't need to do anything
+            // special about the onClickListener here, since we're changing the visibility to
+            // GONE so it won't be clickable anyway.
+            viewModel.historyButtonVisible.collect { footer.setHistoryButtonVisible(it) }
+        }
     }
 
     private suspend fun bindManageOrHistoryButton(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModel.kt
index d8021fa..e724935 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModel.kt
@@ -95,6 +95,10 @@
                     .toAnimatedValueFlow(),
         )
 
+    // Settings buttons are not visible when the message is.
+    val settingsButtonVisible: Flow<Boolean> = message.isVisible.map { !it }
+    val historyButtonVisible: Flow<Boolean> = message.isVisible.map { !it }
+
     val manageButtonShouldLaunchHistory =
         notificationSettingsInteractor.isNotificationHistoryEnabled
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index a4c43a1..aa2a08f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -116,7 +116,6 @@
 import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainerLogger;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
 import com.android.systemui.statusbar.notification.stack.SwipeableView;
-import com.android.systemui.statusbar.phone.ExpandHeadsUpOnInlineReply;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.statusbar.policy.InflatedSmartReplyState;
@@ -2948,18 +2947,10 @@
     }
 
     public boolean isExpanded(boolean allowOnKeyguard) {
-        final boolean isHeadsUpState = ExpandHeadsUpOnInlineReply.isEnabled()
-                && canShowHeadsUp() && isHeadsUpState();
-        // System expanded should be ignored in pinned heads up state
-        final boolean isPinned = isHeadsUpState && isPinned();
-        // Heads Up Notification can be expanded when it is pinned.
-        final boolean isPinnedAndExpanded =
-                isHeadsUpState && isPinnedAndExpanded();
-
         return (!shouldShowPublic()) && (!mOnKeyguard || allowOnKeyguard)
-                && (!hasUserChangedExpansion() && !isPinned
+                && (!hasUserChangedExpansion()
                 && (isSystemExpanded() || isSystemChildExpanded())
-                || isUserExpanded() || isPinnedAndExpanded);
+                || isUserExpanded());
     }
 
     private boolean isSystemChildExpanded() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt
index fd19f1f..f75c89a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt
@@ -22,7 +22,6 @@
 import com.android.app.tracing.TraceUtils.traceAsync
 import com.android.internal.logging.MetricsLogger
 import com.android.internal.logging.nano.MetricsProto
-import com.android.systemui.Flags
 import com.android.systemui.common.ui.ConfigurationState
 import com.android.systemui.common.ui.view.setImportantForAccessibilityYesNo
 import com.android.systemui.dagger.qualifiers.Background
@@ -40,6 +39,7 @@
 import com.android.systemui.statusbar.notification.emptyshade.ui.viewbinder.EmptyShadeViewBinder
 import com.android.systemui.statusbar.notification.emptyshade.ui.viewmodel.EmptyShadeViewModel
 import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor
+import com.android.systemui.statusbar.notification.footer.shared.NotifRedesignFooter
 import com.android.systemui.statusbar.notification.footer.ui.view.FooterView
 import com.android.systemui.statusbar.notification.footer.ui.viewbinder.FooterViewBinder
 import com.android.systemui.statusbar.notification.footer.ui.viewmodel.FooterViewModel
@@ -146,7 +146,7 @@
             // The footer needs to be re-inflated every time the theme or the font size changes.
             configuration
                 .inflateLayout<FooterView>(
-                    if (Flags.notificationsRedesignFooterView())
+                    if (NotifRedesignFooter.isEnabled)
                         R.layout.status_bar_notification_footer_redesign
                     else R.layout.status_bar_notification_footer,
                     parentView,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index dc4d66d..e7c6fb4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -446,6 +446,7 @@
                             mStatusBarKeyguardViewManager.onKeyguardFadedAway();
                         }
                         dispatchScrimsVisible();
+                        dispatchBackScrimState(mScrimBehind.getViewAlpha());
                     }
                 };
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
index 200f080..1dc9de4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
@@ -216,10 +216,19 @@
                 if (row.isChildInGroup() && !row.areChildrenExpanded()) {
                     // The group isn't expanded, let's make sure it's visible!
                     mGroupExpansionManager.toggleGroupExpansion(row.getEntry());
-                } else if (!row.isChildInGroup() && !row.isExpanded()) {
-                    // notification isn't expanded, let's make sure it's visible!
-                    row.toggleExpansionState();
-                    row.getPrivateLayout().setOnExpandedVisibleListener(runnable);
+                } else if (!row.isChildInGroup()) {
+                    final boolean expandNotification;
+                    if (row.isPinned()) {
+                        expandNotification = !row.isPinnedAndExpanded();
+                    } else {
+                        expandNotification = !row.isExpanded();
+                    }
+
+                    if (expandNotification) {
+                        // notification isn't expanded, let's make sure it's expanded!
+                        row.toggleExpansionState();
+                        row.getPrivateLayout().setOnExpandedVisibleListener(runnable);
+                    }
                 }
             } else {
                 if (row.isChildInGroup() && !row.areChildrenExpanded()) {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 07509e6..639b46a 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -120,9 +120,10 @@
 import com.android.systemui.Flags;
 import com.android.systemui.Prefs;
 import com.android.systemui.dump.DumpManager;
+import com.android.systemui.haptics.slider.HapticSlider;
 import com.android.systemui.haptics.slider.HapticSliderViewBinder;
 import com.android.systemui.haptics.slider.SeekableSliderTrackerConfig;
-import com.android.systemui.haptics.slider.SeekbarHapticPlugin;
+import com.android.systemui.haptics.slider.HapticSliderPlugin;
 import com.android.systemui.haptics.slider.SliderHapticFeedbackConfig;
 import com.android.systemui.media.dialog.MediaOutputDialogManager;
 import com.android.systemui.plugins.VolumeDialog;
@@ -939,7 +940,7 @@
     }
 
     private void addSliderHapticsToRow(VolumeRow row) {
-        row.createPlugin(mVibratorHelper, mMSDLPlayer, mSystemClock);
+        row.createPlugin(row.slider, mVibratorHelper, mMSDLPlayer, mSystemClock);
         HapticSliderViewBinder.bind(row.slider, row.mHapticPlugin);
     }
 
@@ -2638,7 +2639,7 @@
             if (D.BUG) Log.d(TAG, "onStartTrackingTouch"+ " " + mRow.stream);
             Events.writeEvent(Events.EVENT_SLIDER_TOUCH_TRACKING, /* startedTracking= */true);
             if (mRow.mHapticPlugin != null) {
-                mRow.mHapticPlugin.onStartTrackingTouch(seekBar);
+                mRow.mHapticPlugin.onStartTrackingTouch();
             }
             mController.setActiveStream(mRow.stream);
             mRow.tracking = true;
@@ -2649,7 +2650,7 @@
             if (D.BUG) Log.d(TAG, "onStopTrackingTouch"+ " " + mRow.stream);
             Events.writeEvent(Events.EVENT_SLIDER_TOUCH_TRACKING, /* startedTracking= */false);
             if (mRow.mHapticPlugin != null) {
-                mRow.mHapticPlugin.onStopTrackingTouch(seekBar);
+                mRow.mHapticPlugin.onStopTrackingTouch();
             }
             mRow.tracking = false;
             mRow.userAttempt = SystemClock.uptimeMillis();
@@ -2726,7 +2727,7 @@
         private ObjectAnimator anim;  // slider progress animation for non-touch-related updates
         private int animTargetProgress;
         private int lastAudibleLevel = 1;
-        private SeekbarHapticPlugin mHapticPlugin;
+        private HapticSliderPlugin mHapticPlugin;
 
         void setIcon(int iconRes, Resources.Theme theme) {
             if (icon != null) {
@@ -2739,15 +2740,17 @@
         }
 
         void createPlugin(
+                SeekBar seekBar,
                 VibratorHelper vibratorHelper,
                 MSDLPlayer msdlPlayer,
                 com.android.systemui.util.time.SystemClock systemClock) {
             if (mHapticPlugin != null) return;
 
-            mHapticPlugin = new SeekbarHapticPlugin(
+            mHapticPlugin = new HapticSliderPlugin(
                 vibratorHelper,
                 msdlPlayer,
                 systemClock,
+                new HapticSlider.SeekBar(seekBar),
                 sSliderHapticFeedbackConfig,
                 sSliderTrackerConfig);
         }
@@ -2782,7 +2785,7 @@
         boolean deliverOnProgressChangedHaptics(boolean fromUser, int progress) {
             if (mHapticPlugin == null) return false;
 
-            mHapticPlugin.onProgressChanged(slider, progress, fromUser);
+            mHapticPlugin.onProgressChanged(progress, fromUser);
             if (!fromUser) {
                 // Consider a change from program as the volume key being continuously pressed
                 mHapticPlugin.onKeyDown();
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt
index 5c4d53a..3bf8c54 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt
@@ -73,6 +73,7 @@
     }
 
     private suspend fun VolumeDialogStreamModel.bindToSlider(slider: Slider) {
+        slider.setOnScrollChangeListener { v, scrollX, scrollY, oldScrollX, oldScrollY -> }
         with(slider) {
             valueFrom = levelMin.toFloat()
             valueTo = levelMax.toFloat()
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index b941fde..3d9eb53 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -2267,6 +2267,23 @@
     }
 
     @Test
+    public void isSimPinSecureReturnsFalseWhenEmpty() {
+        assertThat(mKeyguardUpdateMonitor.isSimPinSecure()).isFalse();
+    }
+
+    @Test
+    public void isSimPinSecureReturnsTrueWhenOneSlotIsLocked() {
+        // Slot 0 is locked
+        mKeyguardUpdateMonitor.handleSimStateChange(10, 0,
+                TelephonyManager.SIM_STATE_PIN_REQUIRED);
+        // Slot 1 is not ready
+        mKeyguardUpdateMonitor.handleSimStateChange(11, 1,
+                TelephonyManager.SIM_STATE_NOT_READY);
+
+        assertThat(mKeyguardUpdateMonitor.isSimPinSecure()).isTrue();
+    }
+
+    @Test
     public void onAuthEnrollmentChangesCallbacksAreNotified() {
         KeyguardUpdateMonitorCallback callback = mock(KeyguardUpdateMonitorCallback.class);
         ArgumentCaptor<AuthController.Callback> authCallback = ArgumentCaptor.forClass(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
index e3e2491..e313a05 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
@@ -71,7 +71,6 @@
 import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
 import com.android.systemui.statusbar.notification.shared.NotificationContentAlphaOptimization;
 import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainer;
-import com.android.systemui.statusbar.phone.ExpandHeadsUpOnInlineReply;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
 
 import org.junit.Assert;
@@ -863,149 +862,6 @@
     }
 
     @Test
-    @EnableFlags(ExpandHeadsUpOnInlineReply.FLAG_NAME)
-    public void isExpanded_HUNsystemExpandedTrueForPinned_notExpanded() throws Exception {
-        // GIVEN
-        final ExpandableNotificationRow row = mNotificationTestHelper.createRow();
-        row.setOnKeyguard(false);
-        row.setSystemExpanded(true);
-        row.setPinned(true);
-        row.setHeadsUp(true);
-
-        // THEN
-        assertThat(row.isExpanded()).isFalse();
-    }
-
-    @Test
-    @EnableFlags(ExpandHeadsUpOnInlineReply.FLAG_NAME)
-    public void isExpanded_HUNsystemExpandedTrueForNotPinned_expanded() throws Exception {
-        // GIVEN
-        final ExpandableNotificationRow row = mNotificationTestHelper.createRow();
-        row.setOnKeyguard(false);
-        row.setSystemExpanded(true);
-        row.setPinned(false);
-        row.setHeadsUp(true);
-
-        // THEN
-        assertThat(row.isExpanded()).isTrue();
-    }
-
-    @Test
-    @EnableFlags(ExpandHeadsUpOnInlineReply.FLAG_NAME)
-    public void isExpanded_HUNDisappearingsystemExpandedTrueForPinned_notExpanded()
-            throws Exception {
-        // GIVEN
-        final ExpandableNotificationRow row = mNotificationTestHelper.createRow();
-        row.setOnKeyguard(false);
-        row.setSystemExpanded(true);
-        row.setPinned(true);
-        row.setHeadsUpAnimatingAway(true);
-
-        // THEN
-        assertThat(row.isExpanded()).isFalse();
-    }
-
-    @Test
-    @EnableFlags(ExpandHeadsUpOnInlineReply.FLAG_NAME)
-    public void isExpanded_HUNDisappearingsystemExpandedTrueForNotPinned_expanded()
-            throws Exception {
-        // GIVEN
-        final ExpandableNotificationRow row = mNotificationTestHelper.createRow();
-        row.setOnKeyguard(false);
-        row.setSystemExpanded(true);
-        row.setPinned(false);
-        row.setHeadsUpAnimatingAway(true);
-
-        // THEN
-        assertThat(row.isExpanded()).isTrue();
-    }
-
-    @Test
-    @EnableFlags(ExpandHeadsUpOnInlineReply.FLAG_NAME)
-    public void isExpanded_userExpandedTrueForHeadsUp_expanded() throws Exception {
-        // GIVEN
-        final ExpandableNotificationRow row = mNotificationTestHelper.createRow();
-        row.setOnKeyguard(false);
-        row.setSystemExpanded(true);
-        row.setHeadsUpAnimatingAway(true);
-        row.setUserExpanded(true);
-
-        // THEN
-        assertThat(row.isExpanded()).isTrue();
-    }
-    @Test
-    @EnableFlags(ExpandHeadsUpOnInlineReply.FLAG_NAME)
-    public void isExpanded_userExpandedTrueForHeadsUpDisappearRunning_expanded() throws Exception {
-        // GIVEN
-        final ExpandableNotificationRow row = mNotificationTestHelper.createRow();
-        row.setOnKeyguard(false);
-        row.setSystemExpanded(true);
-        row.setHeadsUpAnimatingAway(true);
-        row.setUserExpanded(true);
-
-        // THEN
-        assertThat(row.isExpanded()).isTrue();
-    }
-
-    @Test
-    @EnableFlags(ExpandHeadsUpOnInlineReply.FLAG_NAME)
-    public void isExpanded_userExpandedFalseForHeadsUp_notExpanded() throws Exception {
-        // GIVEN
-        final ExpandableNotificationRow row = mNotificationTestHelper.createRow();
-        row.setOnKeyguard(false);
-        row.setSystemExpanded(true);
-        row.setHeadsUpAnimatingAway(true);
-        row.setUserExpanded(false);
-
-        // THEN
-        assertThat(row.isExpanded()).isFalse();
-    }
-    @Test
-    @EnableFlags(ExpandHeadsUpOnInlineReply.FLAG_NAME)
-    public void isExpanded_userExpandedFalseForHeadsUpDisappearRunning_notExpanded()
-            throws Exception {
-        // GIVEN
-        final ExpandableNotificationRow row = mNotificationTestHelper.createRow();
-        row.setOnKeyguard(false);
-        row.setSystemExpanded(true);
-        row.setHeadsUpAnimatingAway(true);
-        row.setUserExpanded(false);
-
-        // THEN
-        assertThat(row.isExpanded()).isFalse();
-    }
-
-    @Test
-    @EnableFlags(ExpandHeadsUpOnInlineReply.FLAG_NAME)
-    public void isExpanded_HUNexpandedWhenPinningTrue_expanded() throws Exception {
-        // GIVEN
-        final ExpandableNotificationRow row = mNotificationTestHelper.createRow();
-        row.setOnKeyguard(false);
-        row.setSystemExpanded(true);
-        row.setHeadsUp(true);
-        row.setPinned(true);
-
-        // WHEN
-        row.expandNotification();
-
-        // THEN
-        assertThat(row.isExpanded()).isTrue();
-    }
-
-    @Test
-    @EnableFlags(ExpandHeadsUpOnInlineReply.FLAG_NAME)
-    public void isExpanded_HUNexpandedWhenPinningFalse_notExpanded() throws Exception {
-        // GIVEN
-        final ExpandableNotificationRow row = mNotificationTestHelper.createRow();
-        row.setOnKeyguard(false);
-        row.setSystemExpanded(false);
-        row.setHeadsUp(true);
-        row.setPinned(true);
-
-        // THEN
-        assertThat(row.isExpanded()).isFalse();
-    }
-    @Test
     public void onDisappearAnimationFinished_shouldSetFalse_headsUpAnimatingAway()
             throws Exception {
         final ExpandableNotificationRow row = mNotificationTestHelper.createRow();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index c639b3a..33a4b7e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -1978,6 +1978,17 @@
     }
 
     @Test
+    public void primaryBouncerToGoneOnFinishCallsLightBarController() {
+        reset(mLightBarController);
+        mScrimController.mBouncerToGoneTransition.accept(
+                new TransitionStep(KeyguardState.PRIMARY_BOUNCER, KeyguardState.GONE, 0f,
+                        TransitionState.FINISHED, "ScrimControllerTest"));
+
+        verify(mLightBarController).setScrimState(
+                any(ScrimState.class), anyFloat(), any(GradientColors.class));
+    }
+
+    @Test
     public void testDoNotAnimateChangeIfOccludeAnimationPlaying() {
         mScrimController.setOccludeAnimationPlaying(true);
         mScrimController.legacyTransitionTo(ScrimState.UNLOCKED);
diff --git a/services/accessibility/java/com/android/server/accessibility/MouseKeysInterceptor.java b/services/accessibility/java/com/android/server/accessibility/MouseKeysInterceptor.java
index 1df5d1a..1212c75 100644
--- a/services/accessibility/java/com/android/server/accessibility/MouseKeysInterceptor.java
+++ b/services/accessibility/java/com/android/server/accessibility/MouseKeysInterceptor.java
@@ -625,7 +625,9 @@
         });
 
         mHandler.removeCallbacksAndMessages(null);
-        mVirtualDevice.close();
+        if (mVirtualDevice != null) {
+            mVirtualDevice.close();
+        }
     }
 
     @Override
diff --git a/services/companion/java/com/android/server/companion/virtual/SensorController.java b/services/companion/java/com/android/server/companion/virtual/SensorController.java
index 8d075db..88b7910 100644
--- a/services/companion/java/com/android/server/companion/virtual/SensorController.java
+++ b/services/companion/java/com/android/server/companion/virtual/SensorController.java
@@ -256,8 +256,15 @@
                 Slog.e(TAG, "Received invalid ParcelFileDescriptor");
                 return BAD_VALUE;
             }
+
+            SharedMemory sharedMemory;
+            try {
+                sharedMemory = SharedMemory.fromFileDescriptor(fd);
+            } catch (IllegalArgumentException e) {
+                Slog.e(TAG, "Failed to create shared memory: " + e);
+                return BAD_VALUE;
+            }
             final int channelHandle = sNextDirectChannelHandle.getAndIncrement();
-            SharedMemory sharedMemory = SharedMemory.fromFileDescriptor(fd);
             try {
                 mCallback.onDirectChannelCreated(channelHandle, sharedMemory);
             } catch (RemoteException e) {
diff --git a/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java b/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java
deleted file mode 100644
index ea3a3d5..0000000
--- a/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java
+++ /dev/null
@@ -1,193 +0,0 @@
-/*
- * Copyright (C) 2019 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.integrity.parser;
-
-import static com.android.server.integrity.model.ComponentBitSize.ATOMIC_FORMULA_START;
-import static com.android.server.integrity.model.ComponentBitSize.BYTE_BITS;
-import static com.android.server.integrity.model.ComponentBitSize.COMPOUND_FORMULA_END;
-import static com.android.server.integrity.model.ComponentBitSize.COMPOUND_FORMULA_START;
-import static com.android.server.integrity.model.ComponentBitSize.CONNECTOR_BITS;
-import static com.android.server.integrity.model.ComponentBitSize.EFFECT_BITS;
-import static com.android.server.integrity.model.ComponentBitSize.FORMAT_VERSION_BITS;
-import static com.android.server.integrity.model.ComponentBitSize.INSTALLER_ALLOWED_BY_MANIFEST_START;
-import static com.android.server.integrity.model.ComponentBitSize.IS_HASHED_BITS;
-import static com.android.server.integrity.model.ComponentBitSize.KEY_BITS;
-import static com.android.server.integrity.model.ComponentBitSize.OPERATOR_BITS;
-import static com.android.server.integrity.model.ComponentBitSize.SEPARATOR_BITS;
-import static com.android.server.integrity.model.ComponentBitSize.SIGNAL_BIT;
-import static com.android.server.integrity.model.ComponentBitSize.VALUE_SIZE_BITS;
-import static com.android.server.integrity.parser.BinaryFileOperations.getBooleanValue;
-import static com.android.server.integrity.parser.BinaryFileOperations.getIntValue;
-import static com.android.server.integrity.parser.BinaryFileOperations.getStringValue;
-
-import android.content.integrity.AtomicFormula;
-import android.content.integrity.CompoundFormula;
-import android.content.integrity.InstallerAllowedByManifestFormula;
-import android.content.integrity.IntegrityFormula;
-import android.content.integrity.Rule;
-
-import com.android.server.integrity.model.BitInputStream;
-
-import java.io.BufferedInputStream;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-/** A helper class to parse rules into the {@link Rule} model from Binary representation. */
-public class RuleBinaryParser implements RuleParser {
-
-    @Override
-    public List<Rule> parse(byte[] ruleBytes) throws RuleParseException {
-        return parse(RandomAccessObject.ofBytes(ruleBytes), Collections.emptyList());
-    }
-
-    @Override
-    public List<Rule> parse(RandomAccessObject randomAccessObject, List<RuleIndexRange> indexRanges)
-            throws RuleParseException {
-        try (RandomAccessInputStream randomAccessInputStream =
-                new RandomAccessInputStream(randomAccessObject)) {
-            return parseRules(randomAccessInputStream, indexRanges);
-        } catch (Exception e) {
-            throw new RuleParseException(e.getMessage(), e);
-        }
-    }
-
-    private List<Rule> parseRules(
-            RandomAccessInputStream randomAccessInputStream, List<RuleIndexRange> indexRanges)
-            throws IOException {
-
-        // Read the rule binary file format version.
-        randomAccessInputStream.skip(FORMAT_VERSION_BITS / BYTE_BITS);
-
-        return indexRanges.isEmpty()
-                ? parseAllRules(randomAccessInputStream)
-                : parseIndexedRules(randomAccessInputStream, indexRanges);
-    }
-
-    private List<Rule> parseAllRules(RandomAccessInputStream randomAccessInputStream)
-            throws IOException {
-        List<Rule> parsedRules = new ArrayList<>();
-
-        BitInputStream inputStream =
-                new BitInputStream(new BufferedInputStream(randomAccessInputStream));
-        while (inputStream.hasNext()) {
-            if (inputStream.getNext(SIGNAL_BIT) == 1) {
-                parsedRules.add(parseRule(inputStream));
-            }
-        }
-
-        return parsedRules;
-    }
-
-    private List<Rule> parseIndexedRules(
-            RandomAccessInputStream randomAccessInputStream, List<RuleIndexRange> indexRanges)
-            throws IOException {
-        List<Rule> parsedRules = new ArrayList<>();
-
-        for (RuleIndexRange range : indexRanges) {
-            randomAccessInputStream.seek(range.getStartIndex());
-
-            BitInputStream inputStream =
-                    new BitInputStream(
-                            new BufferedInputStream(
-                                    new LimitInputStream(
-                                            randomAccessInputStream,
-                                            range.getEndIndex() - range.getStartIndex())));
-
-            // Read the rules until we reach the end index. available() here is not reliable.
-            while (inputStream.hasNext()) {
-                if (inputStream.getNext(SIGNAL_BIT) == 1) {
-                    parsedRules.add(parseRule(inputStream));
-                }
-            }
-        }
-
-        return parsedRules;
-    }
-
-    private Rule parseRule(BitInputStream bitInputStream) throws IOException {
-        IntegrityFormula formula = parseFormula(bitInputStream);
-        int effect = bitInputStream.getNext(EFFECT_BITS);
-
-        if (bitInputStream.getNext(SIGNAL_BIT) != 1) {
-            throw new IllegalArgumentException("A rule must end with a '1' bit.");
-        }
-
-        return new Rule(formula, effect);
-    }
-
-    private IntegrityFormula parseFormula(BitInputStream bitInputStream) throws IOException {
-        int separator = bitInputStream.getNext(SEPARATOR_BITS);
-        switch (separator) {
-            case ATOMIC_FORMULA_START:
-                return parseAtomicFormula(bitInputStream);
-            case COMPOUND_FORMULA_START:
-                return parseCompoundFormula(bitInputStream);
-            case COMPOUND_FORMULA_END:
-                return null;
-            case INSTALLER_ALLOWED_BY_MANIFEST_START:
-                return new InstallerAllowedByManifestFormula();
-            default:
-                throw new IllegalArgumentException(
-                        String.format("Unknown formula separator: %s", separator));
-        }
-    }
-
-    private CompoundFormula parseCompoundFormula(BitInputStream bitInputStream) throws IOException {
-        int connector = bitInputStream.getNext(CONNECTOR_BITS);
-        List<IntegrityFormula> formulas = new ArrayList<>();
-
-        IntegrityFormula parsedFormula = parseFormula(bitInputStream);
-        while (parsedFormula != null) {
-            formulas.add(parsedFormula);
-            parsedFormula = parseFormula(bitInputStream);
-        }
-
-        return new CompoundFormula(connector, formulas);
-    }
-
-    private AtomicFormula parseAtomicFormula(BitInputStream bitInputStream) throws IOException {
-        int key = bitInputStream.getNext(KEY_BITS);
-        int operator = bitInputStream.getNext(OPERATOR_BITS);
-
-        switch (key) {
-            case AtomicFormula.PACKAGE_NAME:
-            case AtomicFormula.APP_CERTIFICATE:
-            case AtomicFormula.APP_CERTIFICATE_LINEAGE:
-            case AtomicFormula.INSTALLER_NAME:
-            case AtomicFormula.INSTALLER_CERTIFICATE:
-            case AtomicFormula.STAMP_CERTIFICATE_HASH:
-                boolean isHashedValue = bitInputStream.getNext(IS_HASHED_BITS) == 1;
-                int valueSize = bitInputStream.getNext(VALUE_SIZE_BITS);
-                String stringValue = getStringValue(bitInputStream, valueSize, isHashedValue);
-                return new AtomicFormula.StringAtomicFormula(key, stringValue, isHashedValue);
-            case AtomicFormula.VERSION_CODE:
-                // TODO(b/147880712): temporary hack until our input handles long
-                long upper = getIntValue(bitInputStream);
-                long lower = getIntValue(bitInputStream);
-                long longValue = (upper << 32) | lower;
-                return new AtomicFormula.LongAtomicFormula(key, operator, longValue);
-            case AtomicFormula.PRE_INSTALLED:
-            case AtomicFormula.STAMP_TRUSTED:
-                boolean booleanValue = getBooleanValue(bitInputStream);
-                return new AtomicFormula.BooleanAtomicFormula(key, booleanValue);
-            default:
-                throw new IllegalArgumentException(String.format("Unknown key: %d", key));
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/notification/NotificationAttentionHelper.java b/services/core/java/com/android/server/notification/NotificationAttentionHelper.java
index 25741bc..0e390b6 100644
--- a/services/core/java/com/android/server/notification/NotificationAttentionHelper.java
+++ b/services/core/java/com/android/server/notification/NotificationAttentionHelper.java
@@ -1690,6 +1690,9 @@
         @Override
         public void onReceive(Context context, Intent intent) {
             String action = intent.getAction();
+            if (action == null) {
+                return;
+            }
 
             if (action.equals(Intent.ACTION_SCREEN_ON)) {
                 // Keep track of screen on/off state, but do not turn off the notification light
diff --git a/services/core/java/com/android/server/notification/TimeToLiveHelper.java b/services/core/java/com/android/server/notification/TimeToLiveHelper.java
index cabe766..b053dfe 100644
--- a/services/core/java/com/android/server/notification/TimeToLiveHelper.java
+++ b/services/core/java/com/android/server/notification/TimeToLiveHelper.java
@@ -188,7 +188,11 @@
                         timeoutKey = earliest.second;
                     }
                 }
-                mNm.timeoutNotification(timeoutKey);
+                if (timeoutKey != null) {
+                    mNm.timeoutNotification(timeoutKey);
+                } else {
+                    Slog.wtf(TAG, "Alarm triggered but should have been cleaned up already");
+                }
             }
         }
     };
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index d6ded51..aac756f 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -6923,27 +6923,25 @@
                 return;
             }
             if (mFixedRotationLaunchingApp.hasFixedRotationTransform(r)) {
+                if (!r.isVisible()) {
+                    // Let the opening activity update orientation.
+                    return;
+                }
                 if (mFixedRotationLaunchingApp.hasAnimatingFixedRotationTransition()) {
                     // Waiting until all of the associated activities have done animation, or the
                     // orientation would be updated too early and cause flickering.
                     return;
                 }
             } else {
-                // Handle a corner case that the task of {@link #mFixedRotationLaunchingApp} is no
-                // longer animating but the corresponding transition finished event won't notify.
-                // E.g. activity A transferred starting window to B, only A will receive transition
-                // finished event. A doesn't have fixed rotation but B is the rotated launching app.
-                final Task task = r.getTask();
-                if (task != mFixedRotationLaunchingApp.getTask()
+                // Check to skip updating display orientation by a non-top activity.
+                if ((!r.isVisible() || !mFixedRotationLaunchingApp.fillsParent())
                         // When closing a translucent task A (r.fillsParent() is false) to a
                         // visible task B, because only A has visibility change, there is only A's
                         // transition callback. Then it still needs to update orientation for B.
-                        && (!mWmService.mFlags.mRespectNonTopVisibleFixedOrientation
-                                || r.fillsParent())) {
-                    // Different tasks won't be in one activity transition animation.
+                        && r.fillsParent()) {
                     return;
                 }
-                if (task.getActivity(ActivityRecord::isInTransition) != null) {
+                if (r.inTransition()) {
                     return;
                     // Continue to update orientation because the transition of the top rotated
                     // launching activity is done.
diff --git a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
index 42c171b..4e86888 100644
--- a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
+++ b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
@@ -16,6 +16,8 @@
 
 package com.android.server.profcollect;
 
+import android.Manifest;
+import android.annotation.RequiresPermission;
 import android.app.job.JobInfo;
 import android.app.job.JobParameters;
 import android.app.job.JobScheduler;
@@ -88,10 +90,11 @@
                 createAndUploadReport(sSelfService);
             }
             if (UsbManager.ACTION_USB_STATE.equals(intent.getAction())) {
-                boolean connected = intent.getBooleanExtra(UsbManager.USB_CONNECTED, false);
                 boolean isADB = intent.getBooleanExtra(UsbManager.USB_FUNCTION_ADB, false);
                 if (isADB) {
-                    Log.d(LOG_TAG, "Received broadcast that ADB became " + connected);
+                    boolean connected = intent.getBooleanExtra(UsbManager.USB_CONNECTED, false);
+                    Log.d(LOG_TAG, "Received broadcast that ADB became " + connected
+                            + ", was " + mAdbActive);
                     mAdbActive = connected;
                 }
             }
@@ -117,9 +120,6 @@
         mUploadEnabled =
             context.getResources().getBoolean(R.bool.config_profcollectReportUploaderEnabled);
 
-        // TODO: ADB might already be active when our service started.
-        mAdbActive = false;
-
         final IntentFilter filter = new IntentFilter();
         filter.addAction(INTENT_UPLOAD_PROFILES);
         filter.addAction(UsbManager.ACTION_USB_STATE);
@@ -140,7 +140,13 @@
     }
 
     @Override
+    @RequiresPermission(Manifest.permission.MANAGE_USB)
     public void onBootPhase(int phase) {
+        if (phase == PHASE_SYSTEM_SERVICES_READY) {
+            UsbManager usbManager = getContext().getSystemService(UsbManager.class);
+            mAdbActive = ((usbManager.getCurrentFunctions() & UsbManager.FUNCTION_ADB) == 1);
+            Log.d(LOG_TAG, "ADB is " + mAdbActive + " on system startup");
+        }
         if (phase == PHASE_BOOT_COMPLETED) {
             if (mIProfcollect == null) {
                 return;
diff --git a/services/tests/servicestests/src/com/android/server/integrity/parser/RuleBinaryParserTest.java b/services/tests/servicestests/src/com/android/server/integrity/parser/RuleBinaryParserTest.java
deleted file mode 100644
index 03363a1..0000000
--- a/services/tests/servicestests/src/com/android/server/integrity/parser/RuleBinaryParserTest.java
+++ /dev/null
@@ -1,693 +0,0 @@
-/*
- * Copyright (C) 2019 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.integrity.parser;
-
-import static com.android.server.integrity.model.ComponentBitSize.ATOMIC_FORMULA_START;
-import static com.android.server.integrity.model.ComponentBitSize.COMPOUND_FORMULA_END;
-import static com.android.server.integrity.model.ComponentBitSize.COMPOUND_FORMULA_START;
-import static com.android.server.integrity.model.ComponentBitSize.CONNECTOR_BITS;
-import static com.android.server.integrity.model.ComponentBitSize.DEFAULT_FORMAT_VERSION;
-import static com.android.server.integrity.model.ComponentBitSize.EFFECT_BITS;
-import static com.android.server.integrity.model.ComponentBitSize.FORMAT_VERSION_BITS;
-import static com.android.server.integrity.model.ComponentBitSize.KEY_BITS;
-import static com.android.server.integrity.model.ComponentBitSize.OPERATOR_BITS;
-import static com.android.server.integrity.model.ComponentBitSize.SEPARATOR_BITS;
-import static com.android.server.integrity.model.ComponentBitSize.VALUE_SIZE_BITS;
-import static com.android.server.integrity.utils.TestUtils.getBits;
-import static com.android.server.integrity.utils.TestUtils.getBytes;
-import static com.android.server.integrity.utils.TestUtils.getValueBits;
-import static com.android.server.testutils.TestUtils.assertExpectException;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.content.integrity.AtomicFormula;
-import android.content.integrity.CompoundFormula;
-import android.content.integrity.IntegrityUtils;
-import android.content.integrity.Rule;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-import java.nio.ByteBuffer;
-import java.nio.charset.StandardCharsets;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-
-@RunWith(JUnit4.class)
-public class RuleBinaryParserTest {
-
-    private static final String COMPOUND_FORMULA_START_BITS =
-            getBits(COMPOUND_FORMULA_START, SEPARATOR_BITS);
-    private static final String COMPOUND_FORMULA_END_BITS =
-            getBits(COMPOUND_FORMULA_END, SEPARATOR_BITS);
-    private static final String ATOMIC_FORMULA_START_BITS =
-            getBits(ATOMIC_FORMULA_START, SEPARATOR_BITS);
-    private static final int INVALID_FORMULA_SEPARATOR_VALUE = (1 << SEPARATOR_BITS) - 1;
-    private static final String INVALID_FORMULA_SEPARATOR_BITS =
-            getBits(INVALID_FORMULA_SEPARATOR_VALUE, SEPARATOR_BITS);
-
-    private static final String NOT = getBits(CompoundFormula.NOT, CONNECTOR_BITS);
-    private static final String AND = getBits(CompoundFormula.AND, CONNECTOR_BITS);
-    private static final String OR = getBits(CompoundFormula.OR, CONNECTOR_BITS);
-    private static final int INVALID_CONNECTOR_VALUE = 3;
-    private static final String INVALID_CONNECTOR =
-            getBits(INVALID_CONNECTOR_VALUE, CONNECTOR_BITS);
-
-    private static final String PACKAGE_NAME = getBits(AtomicFormula.PACKAGE_NAME, KEY_BITS);
-    private static final String APP_CERTIFICATE = getBits(AtomicFormula.APP_CERTIFICATE, KEY_BITS);
-    private static final String APP_CERTIFICATE_LINEAGE =
-            getBits(AtomicFormula.APP_CERTIFICATE_LINEAGE, KEY_BITS);
-    private static final String VERSION_CODE = getBits(AtomicFormula.VERSION_CODE, KEY_BITS);
-    private static final String PRE_INSTALLED = getBits(AtomicFormula.PRE_INSTALLED, KEY_BITS);
-    private static final int INVALID_KEY_VALUE = 9;
-    private static final String INVALID_KEY = getBits(INVALID_KEY_VALUE, KEY_BITS);
-
-    private static final String EQ = getBits(AtomicFormula.EQ, OPERATOR_BITS);
-    private static final int INVALID_OPERATOR_VALUE = 5;
-    private static final String INVALID_OPERATOR = getBits(INVALID_OPERATOR_VALUE, OPERATOR_BITS);
-
-    private static final String IS_NOT_HASHED = "0";
-    private static final String IS_HASHED = "1";
-
-    private static final String DENY = getBits(Rule.DENY, EFFECT_BITS);
-    private static final int INVALID_EFFECT_VALUE = 5;
-    private static final String INVALID_EFFECT = getBits(INVALID_EFFECT_VALUE, EFFECT_BITS);
-
-    private static final String START_BIT = "1";
-    private static final String END_BIT = "1";
-    private static final String INVALID_MARKER_BIT = "0";
-
-    private static final byte[] DEFAULT_FORMAT_VERSION_BYTES =
-            getBytes(getBits(DEFAULT_FORMAT_VERSION, FORMAT_VERSION_BITS));
-
-    private static final List<RuleIndexRange> NO_INDEXING = Collections.emptyList();
-
-    @Test
-    public void testBinaryStream_validCompoundFormula_noIndexing() throws Exception {
-        String packageName = "com.test.app";
-        String ruleBits =
-                START_BIT
-                        + COMPOUND_FORMULA_START_BITS
-                        + NOT
-                        + ATOMIC_FORMULA_START_BITS
-                        + PACKAGE_NAME
-                        + EQ
-                        + IS_NOT_HASHED
-                        + getBits(packageName.length(), VALUE_SIZE_BITS)
-                        + getValueBits(packageName)
-                        + COMPOUND_FORMULA_END_BITS
-                        + DENY
-                        + END_BIT;
-        byte[] ruleBytes = getBytes(ruleBits);
-        ByteBuffer rule =
-                ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length);
-        rule.put(DEFAULT_FORMAT_VERSION_BYTES);
-        rule.put(ruleBytes);
-        RuleParser binaryParser = new RuleBinaryParser();
-        Rule expectedRule =
-                new Rule(
-                        new CompoundFormula(
-                                CompoundFormula.NOT,
-                                Collections.singletonList(
-                                        new AtomicFormula.StringAtomicFormula(
-                                                AtomicFormula.PACKAGE_NAME,
-                                                packageName,
-                                                /* isHashedValue= */ false))),
-                        Rule.DENY);
-
-        List<Rule> rules =
-                binaryParser.parse(RandomAccessObject.ofBytes(rule.array()), NO_INDEXING);
-
-        assertThat(rules).isEqualTo(Collections.singletonList(expectedRule));
-    }
-
-    @Test
-    public void testBinaryString_validCompoundFormula_notConnector_noIndexing() throws Exception {
-        String packageName = "com.test.app";
-        String ruleBits =
-                START_BIT
-                        + COMPOUND_FORMULA_START_BITS
-                        + NOT
-                        + ATOMIC_FORMULA_START_BITS
-                        + PACKAGE_NAME
-                        + EQ
-                        + IS_NOT_HASHED
-                        + getBits(packageName.length(), VALUE_SIZE_BITS)
-                        + getValueBits(packageName)
-                        + COMPOUND_FORMULA_END_BITS
-                        + DENY
-                        + END_BIT;
-        byte[] ruleBytes = getBytes(ruleBits);
-        ByteBuffer rule =
-                ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length);
-        rule.put(DEFAULT_FORMAT_VERSION_BYTES);
-        rule.put(ruleBytes);
-        RuleParser binaryParser = new RuleBinaryParser();
-        Rule expectedRule =
-                new Rule(
-                        new CompoundFormula(
-                                CompoundFormula.NOT,
-                                Collections.singletonList(
-                                        new AtomicFormula.StringAtomicFormula(
-                                                AtomicFormula.PACKAGE_NAME,
-                                                packageName,
-                                                /* isHashedValue= */ false))),
-                        Rule.DENY);
-
-        List<Rule> rules = binaryParser.parse(rule.array());
-
-        assertThat(rules).isEqualTo(Collections.singletonList(expectedRule));
-    }
-
-    @Test
-    public void testBinaryString_validCompoundFormula_andConnector_noIndexing() throws Exception {
-        String packageName = "com.test.app";
-        String appCertificate = "test_cert";
-        String ruleBits =
-                START_BIT
-                        + COMPOUND_FORMULA_START_BITS
-                        + AND
-                        + ATOMIC_FORMULA_START_BITS
-                        + PACKAGE_NAME
-                        + EQ
-                        + IS_NOT_HASHED
-                        + getBits(packageName.length(), VALUE_SIZE_BITS)
-                        + getValueBits(packageName)
-                        + ATOMIC_FORMULA_START_BITS
-                        + APP_CERTIFICATE
-                        + EQ
-                        + IS_NOT_HASHED
-                        + getBits(appCertificate.length(), VALUE_SIZE_BITS)
-                        + getValueBits(appCertificate)
-                        + COMPOUND_FORMULA_END_BITS
-                        + DENY
-                        + END_BIT;
-        byte[] ruleBytes = getBytes(ruleBits);
-        ByteBuffer rule =
-                ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length);
-        rule.put(DEFAULT_FORMAT_VERSION_BYTES);
-        rule.put(ruleBytes);
-        RuleParser binaryParser = new RuleBinaryParser();
-        Rule expectedRule =
-                new Rule(
-                        new CompoundFormula(
-                                CompoundFormula.AND,
-                                Arrays.asList(
-                                        new AtomicFormula.StringAtomicFormula(
-                                                AtomicFormula.PACKAGE_NAME,
-                                                packageName,
-                                                /* isHashedValue= */ false),
-                                        new AtomicFormula.StringAtomicFormula(
-                                                AtomicFormula.APP_CERTIFICATE,
-                                                appCertificate,
-                                                /* isHashedValue= */ false))),
-                        Rule.DENY);
-        List<Rule> rules = binaryParser.parse(rule.array());
-
-        assertThat(rules).isEqualTo(Collections.singletonList(expectedRule));
-    }
-
-    @Test
-    public void testBinaryString_validCompoundFormula_orConnector_noIndexing() throws Exception {
-        String packageName = "com.test.app";
-        String appCertificate = "test_cert";
-        String ruleBits =
-                START_BIT
-                        + COMPOUND_FORMULA_START_BITS
-                        + OR
-                        + ATOMIC_FORMULA_START_BITS
-                        + PACKAGE_NAME
-                        + EQ
-                        + IS_NOT_HASHED
-                        + getBits(packageName.length(), VALUE_SIZE_BITS)
-                        + getValueBits(packageName)
-                        + ATOMIC_FORMULA_START_BITS
-                        + APP_CERTIFICATE
-                        + EQ
-                        + IS_NOT_HASHED
-                        + getBits(appCertificate.length(), VALUE_SIZE_BITS)
-                        + getValueBits(appCertificate)
-                        + COMPOUND_FORMULA_END_BITS
-                        + DENY
-                        + END_BIT;
-        byte[] ruleBytes = getBytes(ruleBits);
-        ByteBuffer rule =
-                ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length);
-        rule.put(DEFAULT_FORMAT_VERSION_BYTES);
-        rule.put(ruleBytes);
-        RuleParser binaryParser = new RuleBinaryParser();
-        Rule expectedRule =
-                new Rule(
-                        new CompoundFormula(
-                                CompoundFormula.OR,
-                                Arrays.asList(
-                                        new AtomicFormula.StringAtomicFormula(
-                                                AtomicFormula.PACKAGE_NAME,
-                                                packageName,
-                                                /* isHashedValue= */ false),
-                                        new AtomicFormula.StringAtomicFormula(
-                                                AtomicFormula.APP_CERTIFICATE,
-                                                appCertificate,
-                                                /* isHashedValue= */ false))),
-                        Rule.DENY);
-
-        List<Rule> rules = binaryParser.parse(rule.array());
-
-        assertThat(rules).isEqualTo(Collections.singletonList(expectedRule));
-    }
-
-    @Test
-    public void testBinaryString_validAtomicFormula_stringValue_noIndexing() throws Exception {
-        String packageName = "com.test.app";
-        String ruleBits =
-                START_BIT
-                        + ATOMIC_FORMULA_START_BITS
-                        + PACKAGE_NAME
-                        + EQ
-                        + IS_NOT_HASHED
-                        + getBits(packageName.length(), VALUE_SIZE_BITS)
-                        + getValueBits(packageName)
-                        + DENY
-                        + END_BIT;
-        byte[] ruleBytes = getBytes(ruleBits);
-        ByteBuffer rule =
-                ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length);
-        rule.put(DEFAULT_FORMAT_VERSION_BYTES);
-        rule.put(ruleBytes);
-        RuleParser binaryParser = new RuleBinaryParser();
-        Rule expectedRule =
-                new Rule(
-                        new AtomicFormula.StringAtomicFormula(
-                                AtomicFormula.PACKAGE_NAME,
-                                packageName,
-                                /* isHashedValue= */ false),
-                        Rule.DENY);
-
-        List<Rule> rules = binaryParser.parse(rule.array());
-
-        assertThat(rules).isEqualTo(Collections.singletonList(expectedRule));
-    }
-
-    @Test
-    public void testBinaryString_validAtomicFormula_hashedValue_noIndexing() throws Exception {
-        String appCertificate = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
-        String ruleBits =
-                START_BIT
-                        + ATOMIC_FORMULA_START_BITS
-                        + APP_CERTIFICATE
-                        + EQ
-                        + IS_HASHED
-                        + getBits(appCertificate.length(), VALUE_SIZE_BITS)
-                        + getValueBits(appCertificate)
-                        + DENY
-                        + END_BIT;
-        byte[] ruleBytes = getBytes(ruleBits);
-        ByteBuffer rule =
-                ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length);
-        rule.put(DEFAULT_FORMAT_VERSION_BYTES);
-        rule.put(ruleBytes);
-
-        RuleParser binaryParser = new RuleBinaryParser();
-        Rule expectedRule =
-                new Rule(
-                        new AtomicFormula.StringAtomicFormula(
-                                AtomicFormula.APP_CERTIFICATE,
-                                IntegrityUtils.getHexDigest(
-                                        appCertificate.getBytes(StandardCharsets.UTF_8)),
-                                /* isHashedValue= */ true),
-                        Rule.DENY);
-
-        List<Rule> rules = binaryParser.parse(rule.array());
-
-        assertThat(rules).isEqualTo(Collections.singletonList(expectedRule));
-    }
-
-    @Test
-    public void testBinaryString_validAtomicFormulaWithCertificateLineage() throws Exception {
-        String appCertificate = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
-        String ruleBits =
-                START_BIT
-                        + ATOMIC_FORMULA_START_BITS
-                        + APP_CERTIFICATE_LINEAGE
-                        + EQ
-                        + IS_HASHED
-                        + getBits(appCertificate.length(), VALUE_SIZE_BITS)
-                        + getValueBits(appCertificate)
-                        + DENY
-                        + END_BIT;
-        byte[] ruleBytes = getBytes(ruleBits);
-        ByteBuffer rule =
-                ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length);
-        rule.put(DEFAULT_FORMAT_VERSION_BYTES);
-        rule.put(ruleBytes);
-
-        RuleParser binaryParser = new RuleBinaryParser();
-        Rule expectedRule =
-                new Rule(
-                        new AtomicFormula.StringAtomicFormula(
-                                AtomicFormula.APP_CERTIFICATE_LINEAGE,
-                                IntegrityUtils.getHexDigest(
-                                        appCertificate.getBytes(StandardCharsets.UTF_8)),
-                                /* isHashedValue= */ true),
-                        Rule.DENY);
-
-        List<Rule> rules = binaryParser.parse(rule.array());
-
-        assertThat(rules).isEqualTo(Collections.singletonList(expectedRule));
-    }
-
-    @Test
-    public void testBinaryString_validAtomicFormula_integerValue_noIndexing() throws Exception {
-        int versionCode = 1;
-        String ruleBits =
-                START_BIT
-                        + ATOMIC_FORMULA_START_BITS
-                        + VERSION_CODE
-                        + EQ
-                        + getBits(versionCode, /* numOfBits= */ 64)
-                        + DENY
-                        + END_BIT;
-        byte[] ruleBytes = getBytes(ruleBits);
-        ByteBuffer rule =
-                ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length);
-        rule.put(DEFAULT_FORMAT_VERSION_BYTES);
-        rule.put(ruleBytes);
-        RuleParser binaryParser = new RuleBinaryParser();
-        Rule expectedRule =
-                new Rule(
-                        new AtomicFormula.LongAtomicFormula(
-                                AtomicFormula.VERSION_CODE, AtomicFormula.EQ, 1),
-                        Rule.DENY);
-
-        List<Rule> rules = binaryParser.parse(rule.array());
-
-        assertThat(rules).isEqualTo(Collections.singletonList(expectedRule));
-    }
-
-    @Test
-    public void testBinaryString_validAtomicFormula_booleanValue_noIndexing() throws Exception {
-        String isPreInstalled = "1";
-        String ruleBits =
-                START_BIT
-                        + ATOMIC_FORMULA_START_BITS
-                        + PRE_INSTALLED
-                        + EQ
-                        + isPreInstalled
-                        + DENY
-                        + END_BIT;
-        byte[] ruleBytes = getBytes(ruleBits);
-        ByteBuffer rule =
-                ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length);
-        rule.put(DEFAULT_FORMAT_VERSION_BYTES);
-        rule.put(ruleBytes);
-        RuleParser binaryParser = new RuleBinaryParser();
-        Rule expectedRule =
-                new Rule(
-                        new AtomicFormula.BooleanAtomicFormula(
-                                AtomicFormula.PRE_INSTALLED, true),
-                        Rule.DENY);
-
-        List<Rule> rules = binaryParser.parse(rule.array());
-
-        assertThat(rules).isEqualTo(Collections.singletonList(expectedRule));
-    }
-
-    @Test
-    public void testBinaryString_invalidAtomicFormula_noIndexing() {
-        int versionCode = 1;
-        String ruleBits =
-                START_BIT
-                        + ATOMIC_FORMULA_START_BITS
-                        + VERSION_CODE
-                        + EQ
-                        + getBits(versionCode, /* numOfBits= */ 64)
-                        + DENY;
-        byte[] ruleBytes = getBytes(ruleBits);
-        ByteBuffer rule =
-                ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length);
-        rule.put(DEFAULT_FORMAT_VERSION_BYTES);
-        rule.put(ruleBytes);
-        RuleParser binaryParser = new RuleBinaryParser();
-
-        assertExpectException(
-                RuleParseException.class,
-                /* expectedExceptionMessageRegex= */ "A rule must end with a '1' bit.",
-                () -> binaryParser.parse(rule.array()));
-    }
-
-    @Test
-    public void testBinaryString_withNoRuleList_noIndexing() throws RuleParseException {
-        ByteBuffer rule = ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length);
-        rule.put(DEFAULT_FORMAT_VERSION_BYTES);
-        RuleParser binaryParser = new RuleBinaryParser();
-
-        List<Rule> rules = binaryParser.parse(rule.array());
-
-        assertThat(rules).isEmpty();
-    }
-
-    @Test
-    public void testBinaryString_withEmptyRule_noIndexing() {
-        String ruleBits = START_BIT;
-        byte[] ruleBytes = getBytes(ruleBits);
-        ByteBuffer rule =
-                ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length);
-        rule.put(DEFAULT_FORMAT_VERSION_BYTES);
-        rule.put(ruleBytes);
-        RuleParser binaryParser = new RuleBinaryParser();
-
-        assertExpectException(
-                RuleParseException.class,
-                /* expectedExceptionMessageRegex */ "",
-                () -> binaryParser.parse(rule.array()));
-    }
-
-    @Test
-    public void testBinaryString_invalidCompoundFormula_invalidNumberOfFormulas_noIndexing() {
-        String packageName = "com.test.app";
-        String appCertificate = "test_cert";
-        String ruleBits =
-                START_BIT
-                        + COMPOUND_FORMULA_START_BITS
-                        + NOT
-                        + ATOMIC_FORMULA_START_BITS
-                        + PACKAGE_NAME
-                        + EQ
-                        + IS_NOT_HASHED
-                        + getBits(packageName.length(), VALUE_SIZE_BITS)
-                        + getValueBits(packageName)
-                        + ATOMIC_FORMULA_START_BITS
-                        + APP_CERTIFICATE
-                        + EQ
-                        + IS_NOT_HASHED
-                        + getBits(appCertificate.length(), VALUE_SIZE_BITS)
-                        + getValueBits(appCertificate)
-                        + COMPOUND_FORMULA_END_BITS
-                        + DENY
-                        + END_BIT;
-        byte[] ruleBytes = getBytes(ruleBits);
-        ByteBuffer rule =
-                ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length);
-        rule.put(DEFAULT_FORMAT_VERSION_BYTES);
-        rule.put(ruleBytes);
-        RuleParser binaryParser = new RuleBinaryParser();
-
-        assertExpectException(
-                RuleParseException.class,
-                /* expectedExceptionMessageRegex */ "Connector NOT must have 1 formula only",
-                () -> binaryParser.parse(rule.array()));
-    }
-
-    @Test
-    public void testBinaryString_invalidRule_invalidOperator_noIndexing() {
-        int versionCode = 1;
-        String ruleBits =
-                START_BIT
-                        + COMPOUND_FORMULA_START_BITS
-                        + NOT
-                        + ATOMIC_FORMULA_START_BITS
-                        + VERSION_CODE
-                        + INVALID_OPERATOR
-                        + getBits(versionCode, /* numOfBits= */ 64)
-                        + COMPOUND_FORMULA_END_BITS
-                        + DENY
-                        + END_BIT;
-        byte[] ruleBytes = getBytes(ruleBits);
-        ByteBuffer rule =
-                ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length);
-        rule.put(DEFAULT_FORMAT_VERSION_BYTES);
-        rule.put(ruleBytes);
-        RuleParser binaryParser = new RuleBinaryParser();
-
-        assertExpectException(
-                RuleParseException.class,
-                /* expectedExceptionMessageRegex */ String.format(
-                        "Unknown operator: %d", INVALID_OPERATOR_VALUE),
-                () -> binaryParser.parse(rule.array()));
-    }
-
-    @Test
-    public void testBinaryString_invalidRule_invalidEffect_noIndexing() {
-        String packageName = "com.test.app";
-        String ruleBits =
-                START_BIT
-                        + COMPOUND_FORMULA_START_BITS
-                        + NOT
-                        + ATOMIC_FORMULA_START_BITS
-                        + PACKAGE_NAME
-                        + EQ
-                        + IS_NOT_HASHED
-                        + getBits(packageName.length(), VALUE_SIZE_BITS)
-                        + getValueBits(packageName)
-                        + COMPOUND_FORMULA_END_BITS
-                        + INVALID_EFFECT
-                        + END_BIT;
-        byte[] ruleBytes = getBytes(ruleBits);
-        ByteBuffer rule =
-                ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length);
-        rule.put(DEFAULT_FORMAT_VERSION_BYTES);
-        rule.put(ruleBytes);
-        RuleParser binaryParser = new RuleBinaryParser();
-
-        assertExpectException(
-                RuleParseException.class,
-                /* expectedExceptionMessageRegex */ String.format(
-                        "Unknown effect: %d", INVALID_EFFECT_VALUE),
-                () -> binaryParser.parse(rule.array()));
-    }
-
-    @Test
-    public void testBinaryString_invalidRule_invalidConnector_noIndexing() {
-        String packageName = "com.test.app";
-        String ruleBits =
-                START_BIT
-                        + COMPOUND_FORMULA_START_BITS
-                        + INVALID_CONNECTOR
-                        + ATOMIC_FORMULA_START_BITS
-                        + PACKAGE_NAME
-                        + EQ
-                        + IS_NOT_HASHED
-                        + getBits(packageName.length(), VALUE_SIZE_BITS)
-                        + getValueBits(packageName)
-                        + COMPOUND_FORMULA_END_BITS
-                        + DENY
-                        + END_BIT;
-        byte[] ruleBytes = getBytes(ruleBits);
-        ByteBuffer rule =
-                ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length);
-        rule.put(DEFAULT_FORMAT_VERSION_BYTES);
-        rule.put(ruleBytes);
-        RuleParser binaryParser = new RuleBinaryParser();
-
-        assertExpectException(
-                RuleParseException.class,
-                /* expectedExceptionMessageRegex */ String.format(
-                        "Unknown connector: %d", INVALID_CONNECTOR_VALUE),
-                () -> binaryParser.parse(rule.array()));
-    }
-
-    @Test
-    public void testBinaryString_invalidRule_invalidKey_noIndexing() {
-        String packageName = "com.test.app";
-        String ruleBits =
-                START_BIT
-                        + COMPOUND_FORMULA_START_BITS
-                        + NOT
-                        + ATOMIC_FORMULA_START_BITS
-                        + INVALID_KEY
-                        + EQ
-                        + IS_NOT_HASHED
-                        + getBits(packageName.length(), VALUE_SIZE_BITS)
-                        + getValueBits(packageName)
-                        + COMPOUND_FORMULA_END_BITS
-                        + DENY
-                        + END_BIT;
-        byte[] ruleBytes = getBytes(ruleBits);
-        ByteBuffer rule =
-                ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length);
-        rule.put(DEFAULT_FORMAT_VERSION_BYTES);
-        rule.put(ruleBytes);
-        RuleParser binaryParser = new RuleBinaryParser();
-
-        assertExpectException(
-                RuleParseException.class,
-                /* expectedExceptionMessageRegex */ String.format(
-                        "Unknown key: %d", INVALID_KEY_VALUE),
-                () -> binaryParser.parse(rule.array()));
-    }
-
-    @Test
-    public void testBinaryString_invalidRule_invalidSeparator_noIndexing() {
-        String packageName = "com.test.app";
-        String ruleBits =
-                START_BIT
-                        + INVALID_FORMULA_SEPARATOR_BITS
-                        + NOT
-                        + ATOMIC_FORMULA_START_BITS
-                        + PACKAGE_NAME
-                        + EQ
-                        + IS_NOT_HASHED
-                        + getBits(packageName.length(), VALUE_SIZE_BITS)
-                        + getValueBits(packageName)
-                        + COMPOUND_FORMULA_END_BITS
-                        + DENY
-                        + END_BIT;
-        byte[] ruleBytes = getBytes(ruleBits);
-        ByteBuffer rule =
-                ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length);
-        rule.put(DEFAULT_FORMAT_VERSION_BYTES);
-        rule.put(ruleBytes);
-        RuleParser binaryParser = new RuleBinaryParser();
-
-        assertExpectException(
-                RuleParseException.class,
-                /* expectedExceptionMessageRegex */ String.format(
-                        "Unknown formula separator: %d", INVALID_FORMULA_SEPARATOR_VALUE),
-                () -> binaryParser.parse(rule.array()));
-    }
-
-    @Test
-    public void testBinaryString_invalidRule_invalidEndMarker_noIndexing() {
-        String packageName = "com.test.app";
-        String ruleBits =
-                START_BIT
-                        + COMPOUND_FORMULA_START_BITS
-                        + NOT
-                        + ATOMIC_FORMULA_START_BITS
-                        + PACKAGE_NAME
-                        + EQ
-                        + IS_NOT_HASHED
-                        + getBits(packageName.length(), VALUE_SIZE_BITS)
-                        + getValueBits(packageName)
-                        + COMPOUND_FORMULA_END_BITS
-                        + DENY
-                        + INVALID_MARKER_BIT;
-        byte[] ruleBytes = getBytes(ruleBits);
-        ByteBuffer rule =
-                ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length);
-        rule.put(DEFAULT_FORMAT_VERSION_BYTES);
-        rule.put(ruleBytes);
-        RuleParser binaryParser = new RuleBinaryParser();
-
-        assertExpectException(
-                RuleParseException.class,
-                /* expectedExceptionMessageRegex */ "A rule must end with a '1' bit",
-                () -> binaryParser.parse(rule.array()));
-    }
-}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/TimeToLiveHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/TimeToLiveHelperTest.java
index ad6c233..09a6840 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/TimeToLiveHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/TimeToLiveHelperTest.java
@@ -19,6 +19,7 @@
 import static com.google.common.truth.Truth.assertThat;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
@@ -108,6 +109,21 @@
     }
 
     @Test
+    public void testTimeoutExpiresButNotifEntryGone() {
+        NotificationRecord r = getRecord("testTimeoutExpires", 1);
+
+        mHelper.scheduleTimeoutLocked(r, 1);
+        ArgumentCaptor<PendingIntent> captor = ArgumentCaptor.forClass(PendingIntent.class);
+        verify(mAm).setExactAndAllowWhileIdle(anyInt(), eq(2L), captor.capture());
+
+        mHelper.mKeys.clear();
+
+        mHelper.mNotificationTimeoutReceiver.onReceive(mContext, captor.getValue().getIntent());
+
+        verify(mNm, never()).timeoutNotification(anyString());
+    }
+
+    @Test
     public void testTimeoutExpires_twoEntries() {
         NotificationRecord first = getRecord("testTimeoutFirst", 1);
         NotificationRecord later = getRecord("testTimeoutSecond", 2);
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 2bebcc3..9408f90 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -1609,8 +1609,10 @@
 
         final ActivityRecord app = mAppWindow.mActivityRecord;
         app.setVisible(false);
-        mDisplayContent.prepareAppTransition(WindowManager.TRANSIT_OPEN);
-        mDisplayContent.mOpeningApps.add(app);
+        app.setVisibleRequested(false);
+        registerTestTransitionPlayer();
+        mDisplayContent.requestTransitionAndLegacyPrepare(WindowManager.TRANSIT_OPEN, 0);
+        app.setVisibility(true);
         final int newOrientation = getRotatedOrientation(mDisplayContent);
         app.setRequestedOrientation(newOrientation);
 
@@ -1641,14 +1643,6 @@
         assertEquals(state.isSourceOrDefaultVisible(statusBarId, statusBars()),
                 rotatedState.isSourceOrDefaultVisible(statusBarId, statusBars()));
 
-        final Rect outFrame = new Rect();
-        final Rect outInsets = new Rect();
-        final Rect outStableInsets = new Rect();
-        final Rect outSurfaceInsets = new Rect();
-        mAppWindow.getAnimationFrames(outFrame, outInsets, outStableInsets, outSurfaceInsets);
-        // The animation frames should not be rotated because display hasn't rotated.
-        assertEquals(mDisplayContent.getBounds(), outFrame);
-
         // The display should keep current orientation and the rotated configuration should apply
         // to the activity.
         assertEquals(config.orientation, mDisplayContent.getConfiguration().orientation);
@@ -1676,9 +1670,8 @@
         // Launch another activity before the transition is finished.
         final Task task2 = new TaskBuilder(mSupervisor).setDisplay(mDisplayContent).build();
         final ActivityRecord app2 = new ActivityBuilder(mAtm).setTask(task2)
-                .setUseProcess(app.app).build();
-        app2.setVisible(false);
-        mDisplayContent.mOpeningApps.add(app2);
+                .setUseProcess(app.app).setVisible(false).build();
+        app2.setVisibility(true);
         app2.setRequestedOrientation(newOrientation);
 
         // The activity should share the same transform state as the existing one. The activity
@@ -1701,14 +1694,15 @@
         assertTrue(mImeWindow.isAnimating(PARENTS, ANIMATION_TYPE_TOKEN_TRANSFORM));
 
         // The fixed rotation transform can only be finished when all animation finished.
-        doReturn(false).when(app2).isAnimating(anyInt(), anyInt());
-        mDisplayContent.mAppTransition.notifyAppTransitionFinishedLocked(app2.token);
+        doReturn(false).when(app2).inTransition();
+        mDisplayContent.mFixedRotationTransitionListener.onAppTransitionFinishedLocked(app2.token);
         assertTrue(app.hasFixedRotationTransform());
         assertTrue(app2.hasFixedRotationTransform());
 
         // The display should be rotated after the launch is finished.
-        doReturn(false).when(app).isAnimating(anyInt(), anyInt());
-        mDisplayContent.mAppTransition.notifyAppTransitionFinishedLocked(app.token);
+        app.setVisible(true);
+        doReturn(false).when(app).inTransition();
+        mDisplayContent.mFixedRotationTransitionListener.onAppTransitionFinishedLocked(app.token);
         mStatusBarWindow.finishSeamlessRotation(t);
         mNavBarWindow.finishSeamlessRotation(t);
 
@@ -1726,7 +1720,7 @@
         final Task task = app.getTask();
         final ActivityRecord app2 = new ActivityBuilder(mWm.mAtmService).setTask(task).build();
         mDisplayContent.setFixedRotationLaunchingApp(app2, (mDisplayContent.getRotation() + 1) % 4);
-        doReturn(true).when(app).inTransitionSelfOrParent();
+        doReturn(true).when(app).inTransition();
         // If the task contains a transition, this should be no-op.
         mDisplayContent.mFixedRotationTransitionListener.onAppTransitionFinishedLocked(app.token);
 
@@ -1736,7 +1730,7 @@
         // The display should be unlikely to be in transition, but if it happens, the fixed
         // rotation should proceed to finish because the activity/task level transition is finished.
         doReturn(true).when(mDisplayContent).inTransition();
-        doReturn(false).when(app).inTransitionSelfOrParent();
+        doReturn(false).when(app).inTransition();
         // Although this notifies app instead of app2 that uses the fixed rotation, app2 should
         // still finish the transform because there is no more transition event.
         mDisplayContent.mFixedRotationTransitionListener.onAppTransitionFinishedLocked(app.token);