Merge "PointerIconLoadingTest: Save test output to Downloads directory" into main
diff --git a/core/api/current.txt b/core/api/current.txt
index 56b6136..f2c59dac 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -1058,7 +1058,7 @@
     field public static final int label = 16842753; // 0x1010001
     field public static final int labelFor = 16843718; // 0x10103c6
     field @Deprecated public static final int labelTextSize = 16843317; // 0x1010235
-    field @FlaggedApi("android.view.inputmethod.ime_switcher_revamp") public static final int languageSettingsActivity;
+    field @FlaggedApi("android.view.inputmethod.ime_switcher_revamp_api") public static final int languageSettingsActivity;
     field public static final int languageTag = 16844040; // 0x1010508
     field public static final int largeHeap = 16843610; // 0x101035a
     field public static final int largeScreens = 16843398; // 0x1010286
@@ -56333,7 +56333,7 @@
   public final class InputMethodInfo implements android.os.Parcelable {
     ctor public InputMethodInfo(android.content.Context, android.content.pm.ResolveInfo) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
     ctor public InputMethodInfo(String, String, CharSequence, String);
-    method @FlaggedApi("android.view.inputmethod.ime_switcher_revamp") @Nullable public android.content.Intent createImeLanguageSettingsActivityIntent();
+    method @FlaggedApi("android.view.inputmethod.ime_switcher_revamp_api") @Nullable public android.content.Intent createImeLanguageSettingsActivityIntent();
     method @Nullable public android.content.Intent createStylusHandwritingSettingsActivityIntent();
     method public int describeContents();
     method public void dump(android.util.Printer, String);
@@ -56354,7 +56354,7 @@
     method public boolean supportsStylusHandwriting();
     method public boolean suppressesSpellChecker();
     method public void writeToParcel(android.os.Parcel, int);
-    field @FlaggedApi("android.view.inputmethod.ime_switcher_revamp") public static final String ACTION_IME_LANGUAGE_SETTINGS = "android.view.inputmethod.action.IME_LANGUAGE_SETTINGS";
+    field @FlaggedApi("android.view.inputmethod.ime_switcher_revamp_api") public static final String ACTION_IME_LANGUAGE_SETTINGS = "android.view.inputmethod.action.IME_LANGUAGE_SETTINGS";
     field public static final String ACTION_STYLUS_HANDWRITING_SETTINGS = "android.view.inputmethod.action.STYLUS_HANDWRITING_SETTINGS";
     field @NonNull public static final android.os.Parcelable.Creator<android.view.inputmethod.InputMethodInfo> CREATOR;
   }
diff --git a/core/java/android/view/inputmethod/InputMethodInfo.java b/core/java/android/view/inputmethod/InputMethodInfo.java
index 11ee286..098f655 100644
--- a/core/java/android/view/inputmethod/InputMethodInfo.java
+++ b/core/java/android/view/inputmethod/InputMethodInfo.java
@@ -92,7 +92,7 @@
      *
      * @see #createImeLanguageSettingsActivityIntent()
      */
-    @FlaggedApi(android.view.inputmethod.Flags.FLAG_IME_SWITCHER_REVAMP)
+    @FlaggedApi(android.view.inputmethod.Flags.FLAG_IME_SWITCHER_REVAMP_API)
     public static final String ACTION_IME_LANGUAGE_SETTINGS =
             "android.view.inputmethod.action.IME_LANGUAGE_SETTINGS";
 
@@ -298,7 +298,7 @@
                     com.android.internal.R.styleable.InputMethod);
             settingsActivityComponent = sa.getString(
                     com.android.internal.R.styleable.InputMethod_settingsActivity);
-            if (Flags.imeSwitcherRevamp()) {
+            if (Flags.imeSwitcherRevampApi()) {
                 languageSettingsActivityComponent = sa.getString(
                         com.android.internal.R.styleable.InputMethod_languageSettingsActivity);
             }
@@ -888,7 +888,7 @@
      *
      * @attr ref R.styleable#InputMethod_languageSettingsActivity
      */
-    @FlaggedApi(android.view.inputmethod.Flags.FLAG_IME_SWITCHER_REVAMP)
+    @FlaggedApi(android.view.inputmethod.Flags.FLAG_IME_SWITCHER_REVAMP_API)
     @Nullable
     public Intent createImeLanguageSettingsActivityIntent() {
         if (TextUtils.isEmpty(mLanguageSettingsActivityName)) {
diff --git a/core/java/android/view/inputmethod/flags.aconfig b/core/java/android/view/inputmethod/flags.aconfig
index 56e5bcf..e294ee2 100644
--- a/core/java/android/view/inputmethod/flags.aconfig
+++ b/core/java/android/view/inputmethod/flags.aconfig
@@ -82,6 +82,15 @@
 }
 
 flag {
+    name: "ime_switcher_revamp_api"
+    is_exported: true
+    namespace: "input_method"
+    description: "Feature flag for APIs for revamping the Input Method Switcher menu"
+    bug: "311791923"
+    is_fixed_read_only: true
+}
+
+flag {
     name: "initiation_without_input_connection"
     namespace: "input_method"
     description: "Feature flag for initiating handwriting without InputConnection"
diff --git a/core/java/android/window/BackProgressAnimator.java b/core/java/android/window/BackProgressAnimator.java
index d28500c..12d4ab8 100644
--- a/core/java/android/window/BackProgressAnimator.java
+++ b/core/java/android/window/BackProgressAnimator.java
@@ -16,12 +16,15 @@
 
 package android.window;
 
+import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.util.FloatProperty;
 import android.util.TimeUtils;
 import android.view.Choreographer;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.dynamicanimation.animation.DynamicAnimation;
 import com.android.internal.dynamicanimation.animation.FlingAnimation;
 import com.android.internal.dynamicanimation.animation.FloatValueHolder;
@@ -210,7 +213,8 @@
     }
 
     /** Returns true if the back animation is in progress. */
-    boolean isBackAnimationInProgress() {
+    @VisibleForTesting(visibility = PACKAGE)
+    public boolean isBackAnimationInProgress() {
         return mBackAnimationInProgress;
     }
 
diff --git a/core/java/android/window/ITaskFragmentOrganizerController.aidl b/core/java/android/window/ITaskFragmentOrganizerController.aidl
index 4706dfd..2c64b8e 100644
--- a/core/java/android/window/ITaskFragmentOrganizerController.aidl
+++ b/core/java/android/window/ITaskFragmentOrganizerController.aidl
@@ -39,19 +39,6 @@
     void unregisterOrganizer(in ITaskFragmentOrganizer organizer);
 
     /**
-     * Registers remote animations per transition type for the organizer. It will override the
-     * animations if the transition only contains windows that belong to the organized
-     * TaskFragments in the given Task.
-     */
-    void registerRemoteAnimations(in ITaskFragmentOrganizer organizer,
-        in RemoteAnimationDefinition definition);
-
-    /**
-     * Unregisters remote animations per transition type for the organizer.
-     */
-    void unregisterRemoteAnimations(in ITaskFragmentOrganizer organizer);
-
-    /**
      * Checks if an activity organized by a {@link android.window.TaskFragmentOrganizer} and
      * only occupies a portion of Task bounds.
      */
diff --git a/core/java/android/window/TaskFragmentOrganizer.java b/core/java/android/window/TaskFragmentOrganizer.java
index 461eab6..d4c3fbe 100644
--- a/core/java/android/window/TaskFragmentOrganizer.java
+++ b/core/java/android/window/TaskFragmentOrganizer.java
@@ -32,7 +32,6 @@
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.RemoteException;
-import android.view.RemoteAnimationDefinition;
 import android.view.WindowManager;
 
 import com.android.window.flags.Flags;
@@ -205,34 +204,6 @@
     }
 
     /**
-     * Registers remote animations per transition type for the organizer. It will override the
-     * animations if the transition only contains windows that belong to the organized
-     * TaskFragments, and at least one of the transition window is embedded (not filling the Task).
-     * @hide
-     */
-    @CallSuper
-    public void registerRemoteAnimations(@NonNull RemoteAnimationDefinition definition) {
-        try {
-            getController().registerRemoteAnimations(mInterface, definition);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /**
-     * Unregisters remote animations per transition type for the organizer.
-     * @hide
-     */
-    @CallSuper
-    public void unregisterRemoteAnimations() {
-        try {
-            getController().unregisterRemoteAnimations(mInterface);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /**
      * Notifies the server that the organizer has finished handling the given transaction. The
      * server should apply the given {@link WindowContainerTransaction} for the necessary changes.
      *
diff --git a/core/java/android/window/WindowOnBackInvokedDispatcher.java b/core/java/android/window/WindowOnBackInvokedDispatcher.java
index 9b87e23..7bbc3db 100644
--- a/core/java/android/window/WindowOnBackInvokedDispatcher.java
+++ b/core/java/android/window/WindowOnBackInvokedDispatcher.java
@@ -244,6 +244,7 @@
                 // We should call onBackCancelled() when an active callback is removed from
                 // dispatcher.
                 sendCancelledIfInProgress(callback);
+                mHandler.post(mProgressAnimator::reset);
                 setTopOnBackInvokedCallback(getTopCallback());
             }
         }
diff --git a/core/java/android/window/flags/windowing_frontend.aconfig b/core/java/android/window/flags/windowing_frontend.aconfig
index 92db37e..0322e4e 100644
--- a/core/java/android/window/flags/windowing_frontend.aconfig
+++ b/core/java/android/window/flags/windowing_frontend.aconfig
@@ -206,3 +206,13 @@
   }
 }
 
+flag {
+  name: "enforce_shell_thread_model"
+  namespace: "windowing_frentend"
+  description: "Crash the shell process if someone calls in from the wrong thread"
+  bug: "351189446"
+  is_fixed_read_only: true
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 4892f59..0975eda 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -4304,7 +4304,7 @@
         <attr name="settingsActivity" format="string" />
         <!-- Component name of an activity that allows the user to modify
              on-screen keyboards variants (e.g. different language or layout) for this service. -->
-        <!-- @FlaggedApi("android.view.inputmethod.ime_switcher_revamp") -->
+        <!-- @FlaggedApi("android.view.inputmethod.ime_switcher_revamp_api") -->
         <attr name="languageSettingsActivity" format="string"/>
         <!-- Set to true in all of the configurations for which this input
              method should be considered an option as the default. -->
diff --git a/core/res/res/values/public-staging.xml b/core/res/res/values/public-staging.xml
index b64334f..b74b41c 100644
--- a/core/res/res/values/public-staging.xml
+++ b/core/res/res/values/public-staging.xml
@@ -114,7 +114,7 @@
     <public name="optional"/>
     <!-- @FlaggedApi("android.media.tv.flags.enable_ad_service_fw") -->
     <public name="adServiceTypes" />
-    <!-- @FlaggedApi("android.view.inputmethod.ime_switcher_revamp") -->
+    <!-- @FlaggedApi("android.view.inputmethod.ime_switcher_revamp_api") -->
     <public name="languageSettingsActivity"/>
     <!-- @FlaggedApi("android.service.controls.flags.Flags.FLAG_HOME_PANEL_DREAM") -->
     <public name="dreamCategory"/>
diff --git a/core/tests/InputMethodCoreTests/src/android/view/inputmethod/InputMethodInfoTest.java b/core/tests/InputMethodCoreTests/src/android/view/inputmethod/InputMethodInfoTest.java
index 36ab0d4..ce85a76 100644
--- a/core/tests/InputMethodCoreTests/src/android/view/inputmethod/InputMethodInfoTest.java
+++ b/core/tests/InputMethodCoreTests/src/android/view/inputmethod/InputMethodInfoTest.java
@@ -70,7 +70,7 @@
         assertThat(imi.supportsInlineSuggestionsWithTouchExploration(), is(false));
         assertThat(imi.supportsStylusHandwriting(), is(false));
         assertThat(imi.createStylusHandwritingSettingsActivityIntent(), equalTo(null));
-        if (Flags.imeSwitcherRevamp()) {
+        if (Flags.imeSwitcherRevampApi()) {
             assertThat(imi.createImeLanguageSettingsActivityIntent(), equalTo(null));
         }
     }
diff --git a/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java b/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java
index d4482f2..9ae96a0 100644
--- a/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java
+++ b/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java
@@ -348,12 +348,16 @@
 
         waitForIdle();
         verify(mCallback1).onBackStarted(any(BackEvent.class));
+        assertTrue(mDispatcher.mProgressAnimator.isBackAnimationInProgress());
 
         mDispatcher.unregisterOnBackInvokedCallback(mCallback1);
 
         waitForIdle();
         verify(mCallback1).onBackCancelled();
         verify(mWindowSession).setOnBackInvokedCallbackInfo(Mockito.eq(mWindow), isNull());
+        // Verify that ProgressAnimator is reset (and thus does not cause further callback event
+        // dispatching)
+        assertFalse(mDispatcher.mProgressAnimator.isBackAnimationInProgress());
     }
 
     @Test
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java
index 9ea2943..1eb95c1 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java
@@ -70,10 +70,6 @@
     @NonNull
     private final TaskFragmentCallback mCallback;
 
-    @VisibleForTesting
-    @Nullable
-    TaskFragmentAnimationController mAnimationController;
-
     /**
      * Callback that notifies the controller about changes to task fragments.
      */
@@ -91,25 +87,6 @@
         mCallback = callback;
     }
 
-    @Override
-    public void unregisterOrganizer() {
-        if (mAnimationController != null) {
-            mAnimationController.unregisterRemoteAnimations();
-            mAnimationController = null;
-        }
-        super.unregisterOrganizer();
-    }
-
-    /**
-     * Overrides the animation for transitions of embedded activities organized by this organizer.
-     */
-    void overrideSplitAnimation() {
-        if (mAnimationController == null) {
-            mAnimationController = new TaskFragmentAnimationController(this);
-        }
-        mAnimationController.registerRemoteAnimations();
-    }
-
     /**
      * Starts a new Activity and puts it into split with an existing Activity side-by-side.
      * @param launchingFragmentToken    token for the launching TaskFragment. If it exists, it will
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
index 7ddda1f..3261a37 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -114,7 +114,6 @@
 public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmentCallback,
         ActivityEmbeddingComponent, DividerPresenter.DragEventCallback {
     static final String TAG = "SplitController";
-    static final boolean ENABLE_SHELL_TRANSITIONS = true;
 
     // TODO(b/243518738): Move to WM Extensions if we have requirement of overlay without
     //  association. It's not set in WM Extensions nor Wm Jetpack library currently.
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
index eb1fc23..ea60b15 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
@@ -166,11 +166,6 @@
         mWindowLayoutComponent = windowLayoutComponent;
         mController = controller;
         registerOrganizer();
-        if (!SplitController.ENABLE_SHELL_TRANSITIONS) {
-            // TODO(b/207070762): cleanup with legacy app transition
-            // Animation will be handled by WM Shell when Shell transition is enabled.
-            overrideSplitAnimation();
-        }
     }
 
     /**
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationAdapter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationAdapter.java
deleted file mode 100644
index 33220c4..0000000
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationAdapter.java
+++ /dev/null
@@ -1,224 +0,0 @@
-/*
- * Copyright (C) 2021 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 androidx.window.extensions.embedding;
-
-import static android.graphics.Matrix.MTRANS_X;
-import static android.graphics.Matrix.MTRANS_Y;
-import static android.view.RemoteAnimationTarget.MODE_CLOSING;
-
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.view.Choreographer;
-import android.view.RemoteAnimationTarget;
-import android.view.SurfaceControl;
-import android.view.animation.Animation;
-import android.view.animation.Transformation;
-
-import androidx.annotation.NonNull;
-
-/**
- * Wrapper to handle the TaskFragment animation update in one {@link SurfaceControl.Transaction}.
- *
- * The base adapter can be used for {@link RemoteAnimationTarget} that is simple open/close.
- */
-class TaskFragmentAnimationAdapter {
-
-    /**
-     * If {@link #mOverrideLayer} is set to this value, we don't want to override the surface layer.
-     */
-    private static final int LAYER_NO_OVERRIDE = -1;
-
-    @NonNull
-    final Animation mAnimation;
-    @NonNull
-    final RemoteAnimationTarget mTarget;
-    @NonNull
-    final SurfaceControl mLeash;
-    /** Area in absolute coordinate that the animation surface shouldn't go beyond. */
-    @NonNull
-    private final Rect mWholeAnimationBounds = new Rect();
-    /**
-     * Area in absolute coordinate that should represent all the content to show for this window.
-     * This should be the end bounds for opening window, and start bounds for closing window in case
-     * the window is resizing during the open/close transition.
-     */
-    @NonNull
-    private final Rect mContentBounds = new Rect();
-    /** Offset relative to the window parent surface for {@link #mContentBounds}. */
-    @NonNull
-    private final Point mContentRelOffset = new Point();
-
-    @NonNull
-    final Transformation mTransformation = new Transformation();
-    @NonNull
-    final float[] mMatrix = new float[9];
-    @NonNull
-    final float[] mVecs = new float[4];
-    @NonNull
-    final Rect mRect = new Rect();
-    private boolean mIsFirstFrame = true;
-    private int mOverrideLayer = LAYER_NO_OVERRIDE;
-
-    TaskFragmentAnimationAdapter(@NonNull Animation animation,
-            @NonNull RemoteAnimationTarget target) {
-        this(animation, target, target.leash, target.screenSpaceBounds);
-    }
-
-    /**
-     * @param leash the surface to animate.
-     * @param wholeAnimationBounds  area in absolute coordinate that the animation surface shouldn't
-     *                              go beyond.
-     */
-    TaskFragmentAnimationAdapter(@NonNull Animation animation,
-            @NonNull RemoteAnimationTarget target, @NonNull SurfaceControl leash,
-            @NonNull Rect wholeAnimationBounds) {
-        mAnimation = animation;
-        mTarget = target;
-        mLeash = leash;
-        mWholeAnimationBounds.set(wholeAnimationBounds);
-        if (target.mode == MODE_CLOSING) {
-            // When it is closing, we want to show the content at the start position in case the
-            // window is resizing as well. For example, when the activities is changing from split
-            // to stack, the bottom TaskFragment will be resized to fullscreen when hiding.
-            final Rect startBounds = target.startBounds;
-            final Rect endBounds = target.screenSpaceBounds;
-            mContentBounds.set(startBounds);
-            mContentRelOffset.set(target.localBounds.left, target.localBounds.top);
-            mContentRelOffset.offset(
-                    startBounds.left - endBounds.left,
-                    startBounds.top - endBounds.top);
-        } else {
-            mContentBounds.set(target.screenSpaceBounds);
-            mContentRelOffset.set(target.localBounds.left, target.localBounds.top);
-        }
-    }
-
-    /**
-     * Surface layer to be set at the first frame of the animation. We will not set the layer if it
-     * is set to {@link #LAYER_NO_OVERRIDE}.
-     */
-    final void overrideLayer(int layer) {
-        mOverrideLayer = layer;
-    }
-
-    /** Called on frame update. */
-    final void onAnimationUpdate(@NonNull SurfaceControl.Transaction t, long currentPlayTime) {
-        if (mIsFirstFrame) {
-            t.show(mLeash);
-            if (mOverrideLayer != LAYER_NO_OVERRIDE) {
-                t.setLayer(mLeash, mOverrideLayer);
-            }
-            mIsFirstFrame = false;
-        }
-
-        // Extract the transformation to the current time.
-        mAnimation.getTransformation(Math.min(currentPlayTime, mAnimation.getDuration()),
-                mTransformation);
-        t.setFrameTimelineVsync(Choreographer.getInstance().getVsyncId());
-        onAnimationUpdateInner(t);
-    }
-
-    /** To be overridden by subclasses to adjust the animation surface change. */
-    void onAnimationUpdateInner(@NonNull SurfaceControl.Transaction t) {
-        // Update the surface position and alpha.
-        mTransformation.getMatrix().postTranslate(mContentRelOffset.x, mContentRelOffset.y);
-        t.setMatrix(mLeash, mTransformation.getMatrix(), mMatrix);
-        t.setAlpha(mLeash, mTransformation.getAlpha());
-
-        // Get current surface bounds in absolute coordinate.
-        // positionX/Y are in local coordinate, so minus the local offset to get the slide amount.
-        final int positionX = Math.round(mMatrix[MTRANS_X]);
-        final int positionY = Math.round(mMatrix[MTRANS_Y]);
-        final Rect cropRect = new Rect(mContentBounds);
-        cropRect.offset(positionX - mContentRelOffset.x, positionY - mContentRelOffset.y);
-
-        // Store the current offset of the surface top left from (0,0) in absolute coordinate.
-        final int offsetX = cropRect.left;
-        final int offsetY = cropRect.top;
-
-        // Intersect to make sure the animation happens within the whole animation bounds.
-        if (!cropRect.intersect(mWholeAnimationBounds)) {
-            // Hide the surface when it is outside of the animation area.
-            t.setAlpha(mLeash, 0);
-        }
-
-        // cropRect is in absolute coordinate, so we need to translate it to surface top left.
-        cropRect.offset(-offsetX, -offsetY);
-        t.setCrop(mLeash, cropRect);
-    }
-
-    /** Called after animation finished. */
-    final void onAnimationEnd(@NonNull SurfaceControl.Transaction t) {
-        onAnimationUpdate(t, mAnimation.getDuration());
-    }
-
-    final long getDurationHint() {
-        return mAnimation.computeDurationHint();
-    }
-
-    /**
-     * Should be used for the animation of the snapshot of a {@link RemoteAnimationTarget} that has
-     * size change.
-     */
-    static class SnapshotAdapter extends TaskFragmentAnimationAdapter {
-
-        SnapshotAdapter(@NonNull Animation animation, @NonNull RemoteAnimationTarget target) {
-            // Start leash is the snapshot of the starting surface.
-            super(animation, target, target.startLeash, target.screenSpaceBounds);
-        }
-
-        @Override
-        void onAnimationUpdateInner(@NonNull SurfaceControl.Transaction t) {
-            // Snapshot should always be placed at the top left of the animation leash.
-            mTransformation.getMatrix().postTranslate(0, 0);
-            t.setMatrix(mLeash, mTransformation.getMatrix(), mMatrix);
-            t.setAlpha(mLeash, mTransformation.getAlpha());
-        }
-    }
-
-    /**
-     * Should be used for the animation of the {@link RemoteAnimationTarget} that has size change.
-     */
-    static class BoundsChangeAdapter extends TaskFragmentAnimationAdapter {
-
-        BoundsChangeAdapter(@NonNull Animation animation, @NonNull RemoteAnimationTarget target) {
-            super(animation, target);
-        }
-
-        @Override
-        void onAnimationUpdateInner(@NonNull SurfaceControl.Transaction t) {
-            mTransformation.getMatrix().postTranslate(
-                    mTarget.localBounds.left, mTarget.localBounds.top);
-            t.setMatrix(mLeash, mTransformation.getMatrix(), mMatrix);
-            t.setAlpha(mLeash, mTransformation.getAlpha());
-
-            // The following applies an inverse scale to the clip-rect so that it crops "after" the
-            // scale instead of before.
-            mVecs[1] = mVecs[2] = 0;
-            mVecs[0] = mVecs[3] = 1;
-            mTransformation.getMatrix().mapVectors(mVecs);
-            mVecs[0] = 1.f / mVecs[0];
-            mVecs[3] = 1.f / mVecs[3];
-            final Rect clipRect = mTransformation.getClipRect();
-            mRect.left = (int) (clipRect.left * mVecs[0] + 0.5f);
-            mRect.right = (int) (clipRect.right * mVecs[0] + 0.5f);
-            mRect.top = (int) (clipRect.top * mVecs[3] + 0.5f);
-            mRect.bottom = (int) (clipRect.bottom * mVecs[3] + 0.5f);
-            t.setWindowCrop(mLeash, mRect);
-        }
-    }
-}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationController.java
deleted file mode 100644
index d7eb9a0..0000000
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationController.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright (C) 2021 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 androidx.window.extensions.embedding;
-
-import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_CLOSE;
-import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN;
-import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_CHANGE;
-import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_CLOSE;
-import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_OPEN;
-
-import android.util.Log;
-import android.view.RemoteAnimationAdapter;
-import android.view.RemoteAnimationDefinition;
-import android.window.TaskFragmentOrganizer;
-
-import androidx.annotation.NonNull;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-/** Controls the TaskFragment remote animations. */
-class TaskFragmentAnimationController {
-
-    private static final String TAG = "TaskFragAnimationCtrl";
-    static final boolean DEBUG = false;
-
-    private final TaskFragmentOrganizer mOrganizer;
-    private final TaskFragmentAnimationRunner mRemoteRunner = new TaskFragmentAnimationRunner();
-    @VisibleForTesting
-    final RemoteAnimationDefinition mDefinition;
-    private boolean mIsRegistered;
-
-    TaskFragmentAnimationController(@NonNull TaskFragmentOrganizer organizer) {
-        mOrganizer = organizer;
-        mDefinition = new RemoteAnimationDefinition();
-        final RemoteAnimationAdapter animationAdapter =
-                new RemoteAnimationAdapter(mRemoteRunner, 0, 0, true /* changeNeedsSnapshot */);
-        mDefinition.addRemoteAnimation(TRANSIT_OLD_ACTIVITY_OPEN, animationAdapter);
-        mDefinition.addRemoteAnimation(TRANSIT_OLD_TASK_FRAGMENT_OPEN, animationAdapter);
-        mDefinition.addRemoteAnimation(TRANSIT_OLD_ACTIVITY_CLOSE, animationAdapter);
-        mDefinition.addRemoteAnimation(TRANSIT_OLD_TASK_FRAGMENT_CLOSE, animationAdapter);
-        mDefinition.addRemoteAnimation(TRANSIT_OLD_TASK_FRAGMENT_CHANGE, animationAdapter);
-    }
-
-    void registerRemoteAnimations() {
-        if (DEBUG) {
-            Log.v(TAG, "registerRemoteAnimations");
-        }
-        if (mIsRegistered) {
-            return;
-        }
-        mOrganizer.registerRemoteAnimations(mDefinition);
-        mIsRegistered = true;
-    }
-
-    void unregisterRemoteAnimations() {
-        if (DEBUG) {
-            Log.v(TAG, "unregisterRemoteAnimations");
-        }
-        if (!mIsRegistered) {
-            return;
-        }
-        mOrganizer.unregisterRemoteAnimations();
-        mIsRegistered = false;
-    }
-}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationRunner.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationRunner.java
deleted file mode 100644
index d9b73a8..0000000
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationRunner.java
+++ /dev/null
@@ -1,309 +0,0 @@
-/*
- * Copyright (C) 2021 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 androidx.window.extensions.embedding;
-
-import static android.os.Process.THREAD_PRIORITY_DISPLAY;
-import static android.view.RemoteAnimationTarget.MODE_CHANGING;
-import static android.view.RemoteAnimationTarget.MODE_CLOSING;
-import static android.view.RemoteAnimationTarget.MODE_OPENING;
-import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_CLOSE;
-import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN;
-import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_CHANGE;
-import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_CLOSE;
-import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_OPEN;
-import static android.view.WindowManagerPolicyConstants.TYPE_LAYER_OFFSET;
-
-import android.animation.Animator;
-import android.animation.ValueAnimator;
-import android.graphics.Rect;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.RemoteException;
-import android.util.Log;
-import android.view.IRemoteAnimationFinishedCallback;
-import android.view.IRemoteAnimationRunner;
-import android.view.RemoteAnimationTarget;
-import android.view.SurfaceControl;
-import android.view.WindowManager;
-import android.view.animation.Animation;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.function.BiFunction;
-
-/** To run the TaskFragment animations. */
-class TaskFragmentAnimationRunner extends IRemoteAnimationRunner.Stub {
-
-    private static final String TAG = "TaskFragAnimationRunner";
-    private final Handler mHandler;
-    private final TaskFragmentAnimationSpec mAnimationSpec;
-
-    TaskFragmentAnimationRunner() {
-        HandlerThread animationThread = new HandlerThread(
-                "androidx.window.extensions.embedding", THREAD_PRIORITY_DISPLAY);
-        animationThread.start();
-        mHandler = animationThread.getThreadHandler();
-        mAnimationSpec = new TaskFragmentAnimationSpec(mHandler);
-    }
-
-    @Nullable
-    private Animator mAnimator;
-
-    @Override
-    public void onAnimationStart(@WindowManager.TransitionOldType int transit,
-            @NonNull RemoteAnimationTarget[] apps,
-            @NonNull RemoteAnimationTarget[] wallpapers,
-            @NonNull RemoteAnimationTarget[] nonApps,
-            @NonNull IRemoteAnimationFinishedCallback finishedCallback) {
-        if (wallpapers.length != 0 || nonApps.length != 0) {
-            throw new IllegalArgumentException("TaskFragment shouldn't handle animation with"
-                    + "wallpaper or non-app windows.");
-        }
-        if (TaskFragmentAnimationController.DEBUG) {
-            Log.v(TAG, "onAnimationStart transit=" + transit);
-        }
-        mHandler.post(() -> startAnimation(transit, apps, finishedCallback));
-    }
-
-    @Override
-    public void onAnimationCancelled() {
-        mHandler.post(this::cancelAnimation);
-    }
-
-    /** Creates and starts animation. */
-    private void startAnimation(@WindowManager.TransitionOldType int transit,
-            @NonNull RemoteAnimationTarget[] targets,
-            @NonNull IRemoteAnimationFinishedCallback finishedCallback) {
-        if (mAnimator != null) {
-            Log.w(TAG, "start new animation when the previous one is not finished yet.");
-            mAnimator.cancel();
-        }
-        mAnimator = createAnimator(transit, targets, finishedCallback);
-        mAnimator.start();
-    }
-
-    /** Cancels animation. */
-    private void cancelAnimation() {
-        if (mAnimator == null) {
-            return;
-        }
-        mAnimator.cancel();
-        mAnimator = null;
-    }
-
-    /** Creates the animator given the transition type and windows. */
-    @NonNull
-    private Animator createAnimator(@WindowManager.TransitionOldType int transit,
-            @NonNull RemoteAnimationTarget[] targets,
-            @NonNull IRemoteAnimationFinishedCallback finishedCallback) {
-        final List<TaskFragmentAnimationAdapter> adapters =
-                createAnimationAdapters(transit, targets);
-        long duration = 0;
-        for (TaskFragmentAnimationAdapter adapter : adapters) {
-            duration = Math.max(duration, adapter.getDurationHint());
-        }
-        final ValueAnimator animator = ValueAnimator.ofFloat(0, 1);
-        animator.setDuration(duration);
-        animator.addUpdateListener((anim) -> {
-            // Update all adapters in the same transaction.
-            final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
-            for (TaskFragmentAnimationAdapter adapter : adapters) {
-                adapter.onAnimationUpdate(t, animator.getCurrentPlayTime());
-            }
-            t.apply();
-        });
-        animator.addListener(new Animator.AnimatorListener() {
-            @Override
-            public void onAnimationStart(Animator animation) {}
-
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
-                for (TaskFragmentAnimationAdapter adapter : adapters) {
-                    adapter.onAnimationEnd(t);
-                }
-                t.apply();
-
-                try {
-                    finishedCallback.onAnimationFinished();
-                } catch (RemoteException e) {
-                    e.rethrowFromSystemServer();
-                }
-                mAnimator = null;
-            }
-
-            @Override
-            public void onAnimationCancel(Animator animation) {}
-
-            @Override
-            public void onAnimationRepeat(Animator animation) {}
-        });
-        return animator;
-    }
-
-    /** List of {@link TaskFragmentAnimationAdapter} to handle animations on all window targets. */
-    @NonNull
-    private List<TaskFragmentAnimationAdapter> createAnimationAdapters(
-            @WindowManager.TransitionOldType int transit,
-            @NonNull RemoteAnimationTarget[] targets) {
-        switch (transit) {
-            case TRANSIT_OLD_ACTIVITY_OPEN:
-            case TRANSIT_OLD_TASK_FRAGMENT_OPEN:
-                return createOpenAnimationAdapters(targets);
-            case TRANSIT_OLD_ACTIVITY_CLOSE:
-            case TRANSIT_OLD_TASK_FRAGMENT_CLOSE:
-                return createCloseAnimationAdapters(targets);
-            case TRANSIT_OLD_TASK_FRAGMENT_CHANGE:
-                return createChangeAnimationAdapters(targets);
-            default:
-                throw new IllegalArgumentException("Unhandled transit type=" + transit);
-        }
-    }
-
-    @NonNull
-    private List<TaskFragmentAnimationAdapter> createOpenAnimationAdapters(
-            @NonNull RemoteAnimationTarget[] targets) {
-        return createOpenCloseAnimationAdapters(targets, true /* isOpening */,
-                mAnimationSpec::loadOpenAnimation);
-    }
-
-    @NonNull
-    private List<TaskFragmentAnimationAdapter> createCloseAnimationAdapters(
-            @NonNull RemoteAnimationTarget[] targets) {
-        return createOpenCloseAnimationAdapters(targets, false /* isOpening */,
-                mAnimationSpec::loadCloseAnimation);
-    }
-
-    /**
-     * Creates {@link TaskFragmentAnimationAdapter} for OPEN and CLOSE types of transition.
-     * @param isOpening {@code true} for OPEN type, {@code false} for CLOSE type.
-     */
-    @NonNull
-    private List<TaskFragmentAnimationAdapter> createOpenCloseAnimationAdapters(
-            @NonNull RemoteAnimationTarget[] targets, boolean isOpening,
-            @NonNull BiFunction<RemoteAnimationTarget, Rect, Animation> animationProvider) {
-        // We need to know if the target window is only a partial of the whole animation screen.
-        // If so, we will need to adjust it to make the whole animation screen looks like one.
-        final List<RemoteAnimationTarget> openingTargets = new ArrayList<>();
-        final List<RemoteAnimationTarget> closingTargets = new ArrayList<>();
-        final Rect openingWholeScreenBounds = new Rect();
-        final Rect closingWholeScreenBounds = new Rect();
-        for (RemoteAnimationTarget target : targets) {
-            if (target.mode != MODE_CLOSING) {
-                openingTargets.add(target);
-                openingWholeScreenBounds.union(target.screenSpaceBounds);
-            } else {
-                closingTargets.add(target);
-                closingWholeScreenBounds.union(target.screenSpaceBounds);
-                // Union the start bounds since this may be the ClosingChanging animation.
-                closingWholeScreenBounds.union(target.startBounds);
-            }
-        }
-
-        // For OPEN transition, open windows should be above close windows.
-        // For CLOSE transition, open windows should be below close windows.
-        int offsetLayer = TYPE_LAYER_OFFSET;
-        final List<TaskFragmentAnimationAdapter> adapters = new ArrayList<>();
-        for (RemoteAnimationTarget target : openingTargets) {
-            final TaskFragmentAnimationAdapter adapter = createOpenCloseAnimationAdapter(target,
-                    animationProvider, openingWholeScreenBounds);
-            if (isOpening) {
-                adapter.overrideLayer(offsetLayer++);
-            }
-            adapters.add(adapter);
-        }
-        for (RemoteAnimationTarget target : closingTargets) {
-            final TaskFragmentAnimationAdapter adapter = createOpenCloseAnimationAdapter(target,
-                    animationProvider, closingWholeScreenBounds);
-            if (!isOpening) {
-                adapter.overrideLayer(offsetLayer++);
-            }
-            adapters.add(adapter);
-        }
-        return adapters;
-    }
-
-    @NonNull
-    private TaskFragmentAnimationAdapter createOpenCloseAnimationAdapter(
-            @NonNull RemoteAnimationTarget target,
-            @NonNull BiFunction<RemoteAnimationTarget, Rect, Animation> animationProvider,
-            @NonNull Rect wholeAnimationBounds) {
-        final Animation animation = animationProvider.apply(target, wholeAnimationBounds);
-        return new TaskFragmentAnimationAdapter(animation, target, target.leash,
-                wholeAnimationBounds);
-    }
-
-    @NonNull
-    private List<TaskFragmentAnimationAdapter> createChangeAnimationAdapters(
-            @NonNull RemoteAnimationTarget[] targets) {
-        if (shouldUseJumpCutForChangeAnimation(targets)) {
-            return new ArrayList<>();
-        }
-
-        final List<TaskFragmentAnimationAdapter> adapters = new ArrayList<>();
-        for (RemoteAnimationTarget target : targets) {
-            if (target.mode == MODE_CHANGING) {
-                // This is the target with bounds change.
-                final Animation[] animations =
-                        mAnimationSpec.createChangeBoundsChangeAnimations(target);
-                // Adapter for the starting snapshot leash.
-                adapters.add(new TaskFragmentAnimationAdapter.SnapshotAdapter(
-                        animations[0], target));
-                // Adapter for the ending bounds changed leash.
-                adapters.add(new TaskFragmentAnimationAdapter.BoundsChangeAdapter(
-                        animations[1], target));
-                continue;
-            }
-
-            // These are the other targets that don't have bounds change in the same transition.
-            final Animation animation;
-            if (target.hasAnimatingParent) {
-                // No-op if it will be covered by the changing parent window.
-                animation = TaskFragmentAnimationSpec.createNoopAnimation(target);
-            } else if (target.mode == MODE_CLOSING) {
-                animation = mAnimationSpec.createChangeBoundsCloseAnimation(target);
-            } else {
-                animation = mAnimationSpec.createChangeBoundsOpenAnimation(target);
-            }
-            adapters.add(new TaskFragmentAnimationAdapter(animation, target));
-        }
-        return adapters;
-    }
-
-    /**
-     * Whether we should use jump cut for the change transition.
-     * This normally happens when opening a new secondary with the existing primary using a
-     * different split layout. This can be complicated, like from horizontal to vertical split with
-     * new split pairs.
-     * Uses a jump cut animation to simplify.
-     */
-    private boolean shouldUseJumpCutForChangeAnimation(@NonNull RemoteAnimationTarget[] targets) {
-        boolean hasOpeningWindow = false;
-        boolean hasClosingWindow = false;
-        for (RemoteAnimationTarget target : targets) {
-            if (target.hasAnimatingParent) {
-                continue;
-            }
-            hasOpeningWindow |= target.mode == MODE_OPENING;
-            hasClosingWindow |= target.mode == MODE_CLOSING;
-        }
-        return hasOpeningWindow && hasClosingWindow;
-    }
-}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationSpec.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationSpec.java
deleted file mode 100644
index 1f866c3..0000000
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationSpec.java
+++ /dev/null
@@ -1,267 +0,0 @@
-/*
- * Copyright (C) 2021 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 androidx.window.extensions.embedding;
-
-import static android.view.RemoteAnimationTarget.MODE_CLOSING;
-
-import android.app.ActivityThread;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.database.ContentObserver;
-import android.graphics.Rect;
-import android.os.Handler;
-import android.provider.Settings;
-import android.view.RemoteAnimationTarget;
-import android.view.WindowManager;
-import android.view.animation.AlphaAnimation;
-import android.view.animation.Animation;
-import android.view.animation.AnimationSet;
-import android.view.animation.AnimationUtils;
-import android.view.animation.ClipRectAnimation;
-import android.view.animation.Interpolator;
-import android.view.animation.LinearInterpolator;
-import android.view.animation.ScaleAnimation;
-import android.view.animation.TranslateAnimation;
-
-import androidx.annotation.NonNull;
-
-import com.android.internal.R;
-import com.android.internal.policy.AttributeCache;
-import com.android.internal.policy.TransitionAnimation;
-
-/** Animation spec for TaskFragment transition. */
-// TODO(b/206557124): provide an easier way to customize animation
-class TaskFragmentAnimationSpec {
-
-    private static final String TAG = "TaskFragAnimationSpec";
-    private static final int CHANGE_ANIMATION_DURATION = 517;
-    private static final int CHANGE_ANIMATION_FADE_DURATION = 80;
-    private static final int CHANGE_ANIMATION_FADE_OFFSET = 30;
-
-    private final Context mContext;
-    private final TransitionAnimation mTransitionAnimation;
-    private final Interpolator mFastOutExtraSlowInInterpolator;
-    private final LinearInterpolator mLinearInterpolator;
-    private float mTransitionAnimationScaleSetting;
-
-    TaskFragmentAnimationSpec(@NonNull Handler handler) {
-        mContext = ActivityThread.currentActivityThread().getApplication();
-        mTransitionAnimation = new TransitionAnimation(mContext, false /* debug */, TAG);
-        // Initialize the AttributeCache for the TransitionAnimation.
-        AttributeCache.init(mContext);
-        mFastOutExtraSlowInInterpolator = AnimationUtils.loadInterpolator(
-                mContext, android.R.interpolator.fast_out_extra_slow_in);
-        mLinearInterpolator = new LinearInterpolator();
-
-        // The transition animation should be adjusted based on the developer option.
-        final ContentResolver resolver = mContext.getContentResolver();
-        mTransitionAnimationScaleSetting = getTransitionAnimationScaleSetting();
-        resolver.registerContentObserver(
-                Settings.Global.getUriFor(Settings.Global.TRANSITION_ANIMATION_SCALE), false,
-                new SettingsObserver(handler));
-    }
-
-    /** For target that doesn't need to be animated. */
-    @NonNull
-    static Animation createNoopAnimation(@NonNull RemoteAnimationTarget target) {
-        // Noop but just keep the target showing/hiding.
-        final float alpha = target.mode == MODE_CLOSING ? 0f : 1f;
-        return new AlphaAnimation(alpha, alpha);
-    }
-
-    /** Animation for target that is opening in a change transition. */
-    @NonNull
-    Animation createChangeBoundsOpenAnimation(@NonNull RemoteAnimationTarget target) {
-        final Rect parentBounds = target.taskInfo.configuration.windowConfiguration.getBounds();
-        final Rect bounds = target.screenSpaceBounds;
-        final int startLeft;
-        final int startTop;
-        if (parentBounds.top == bounds.top && parentBounds.bottom == bounds.bottom) {
-            // The window will be animated in from left or right depending on its position.
-            startTop = 0;
-            startLeft = parentBounds.left == bounds.left ? -bounds.width() : bounds.width();
-        } else {
-            // The window will be animated in from top or bottom depending on its position.
-            startTop = parentBounds.top == bounds.top ? -bounds.height() : bounds.height();
-            startLeft = 0;
-        }
-
-        // The position should be 0-based as we will post translate in
-        // TaskFragmentAnimationAdapter#onAnimationUpdate
-        final Animation animation = new TranslateAnimation(startLeft, 0, startTop, 0);
-        animation.setInterpolator(mFastOutExtraSlowInInterpolator);
-        animation.setDuration(CHANGE_ANIMATION_DURATION);
-        animation.initialize(bounds.width(), bounds.height(), bounds.width(), bounds.height());
-        animation.scaleCurrentDuration(mTransitionAnimationScaleSetting);
-        return animation;
-    }
-
-    /** Animation for target that is closing in a change transition. */
-    @NonNull
-    Animation createChangeBoundsCloseAnimation(@NonNull RemoteAnimationTarget target) {
-        final Rect parentBounds = target.taskInfo.configuration.windowConfiguration.getBounds();
-        // Use startBounds if the window is closing in case it may also resize.
-        final Rect bounds = target.startBounds;
-        final int endTop;
-        final int endLeft;
-        if (parentBounds.top == bounds.top && parentBounds.bottom == bounds.bottom) {
-            // The window will be animated out to left or right depending on its position.
-            endTop = 0;
-            endLeft = parentBounds.left == bounds.left ? -bounds.width() : bounds.width();
-        } else {
-            // The window will be animated out to top or bottom depending on its position.
-            endTop = parentBounds.top == bounds.top ? -bounds.height() : bounds.height();
-            endLeft = 0;
-        }
-
-        // The position should be 0-based as we will post translate in
-        // TaskFragmentAnimationAdapter#onAnimationUpdate
-        final Animation animation = new TranslateAnimation(0, endLeft, 0, endTop);
-        animation.setInterpolator(mFastOutExtraSlowInInterpolator);
-        animation.setDuration(CHANGE_ANIMATION_DURATION);
-        animation.initialize(bounds.width(), bounds.height(), bounds.width(), bounds.height());
-        animation.scaleCurrentDuration(mTransitionAnimationScaleSetting);
-        return animation;
-    }
-
-    /**
-     * Animation for target that is changing (bounds change) in a change transition.
-     * @return the return array always has two elements. The first one is for the start leash, and
-     *         the second one is for the end leash.
-     */
-    @NonNull
-    Animation[] createChangeBoundsChangeAnimations(@NonNull RemoteAnimationTarget target) {
-        // Both start bounds and end bounds are in screen coordinates. We will post translate
-        // to the local coordinates in TaskFragmentAnimationAdapter#onAnimationUpdate
-        final Rect startBounds = target.startBounds;
-        final Rect parentBounds = target.taskInfo.configuration.windowConfiguration.getBounds();
-        final Rect endBounds = target.screenSpaceBounds;
-        float scaleX = ((float) startBounds.width()) / endBounds.width();
-        float scaleY = ((float) startBounds.height()) / endBounds.height();
-        // Start leash is a child of the end leash. Reverse the scale so that the start leash won't
-        // be scaled up with its parent.
-        float startScaleX = 1.f / scaleX;
-        float startScaleY = 1.f / scaleY;
-
-        // The start leash will be fade out.
-        final AnimationSet startSet = new AnimationSet(false /* shareInterpolator */);
-        final Animation startAlpha = new AlphaAnimation(1f, 0f);
-        startAlpha.setInterpolator(mLinearInterpolator);
-        startAlpha.setDuration(CHANGE_ANIMATION_FADE_DURATION);
-        startAlpha.setStartOffset(CHANGE_ANIMATION_FADE_OFFSET);
-        startSet.addAnimation(startAlpha);
-        final Animation startScale = new ScaleAnimation(startScaleX, startScaleX, startScaleY,
-                startScaleY);
-        startScale.setInterpolator(mFastOutExtraSlowInInterpolator);
-        startScale.setDuration(CHANGE_ANIMATION_DURATION);
-        startSet.addAnimation(startScale);
-        startSet.initialize(startBounds.width(), startBounds.height(), endBounds.width(),
-                endBounds.height());
-        startSet.scaleCurrentDuration(mTransitionAnimationScaleSetting);
-
-        // The end leash will be moved into the end position while scaling.
-        final AnimationSet endSet = new AnimationSet(true /* shareInterpolator */);
-        endSet.setInterpolator(mFastOutExtraSlowInInterpolator);
-        final Animation endScale = new ScaleAnimation(scaleX, 1, scaleY, 1);
-        endScale.setDuration(CHANGE_ANIMATION_DURATION);
-        endSet.addAnimation(endScale);
-        // The position should be 0-based as we will post translate in
-        // TaskFragmentAnimationAdapter#onAnimationUpdate
-        final Animation endTranslate = new TranslateAnimation(startBounds.left - endBounds.left, 0,
-                startBounds.top - endBounds.top, 0);
-        endTranslate.setDuration(CHANGE_ANIMATION_DURATION);
-        endSet.addAnimation(endTranslate);
-        // The end leash is resizing, we should update the window crop based on the clip rect.
-        final Rect startClip = new Rect(startBounds);
-        final Rect endClip = new Rect(endBounds);
-        startClip.offsetTo(0, 0);
-        endClip.offsetTo(0, 0);
-        final Animation clipAnim = new ClipRectAnimation(startClip, endClip);
-        clipAnim.setDuration(CHANGE_ANIMATION_DURATION);
-        endSet.addAnimation(clipAnim);
-        endSet.initialize(startBounds.width(), startBounds.height(), parentBounds.width(),
-                parentBounds.height());
-        endSet.scaleCurrentDuration(mTransitionAnimationScaleSetting);
-
-        return new Animation[]{startSet, endSet};
-    }
-
-    @NonNull
-    Animation loadOpenAnimation(@NonNull RemoteAnimationTarget target,
-            @NonNull Rect wholeAnimationBounds) {
-        final boolean isEnter = target.mode != MODE_CLOSING;
-        final Animation animation;
-        // Background color on TaskDisplayArea has already been set earlier in
-        // WindowContainer#getAnimationAdapter.
-        if (target.showBackdrop) {
-            animation = mTransitionAnimation.loadDefaultAnimationRes(isEnter
-                    ? com.android.internal.R.anim.task_fragment_clear_top_open_enter
-                    : com.android.internal.R.anim.task_fragment_clear_top_open_exit);
-        } else {
-            animation = mTransitionAnimation.loadDefaultAnimationRes(isEnter
-                    ? com.android.internal.R.anim.task_fragment_open_enter
-                    : com.android.internal.R.anim.task_fragment_open_exit);
-        }
-        // Use the whole animation bounds instead of the change bounds, so that when multiple change
-        // targets are opening at the same time, the animation applied to each will be the same.
-        // Otherwise, we may see gap between the activities that are launching together.
-        animation.initialize(wholeAnimationBounds.width(), wholeAnimationBounds.height(),
-                wholeAnimationBounds.width(), wholeAnimationBounds.height());
-        animation.scaleCurrentDuration(mTransitionAnimationScaleSetting);
-        return animation;
-    }
-
-    @NonNull
-    Animation loadCloseAnimation(@NonNull RemoteAnimationTarget target,
-            @NonNull Rect wholeAnimationBounds) {
-        final boolean isEnter = target.mode != MODE_CLOSING;
-        final Animation animation;
-        if (target.showBackdrop) {
-            animation = mTransitionAnimation.loadDefaultAnimationRes(isEnter
-                    ? com.android.internal.R.anim.task_fragment_clear_top_close_enter
-                    : com.android.internal.R.anim.task_fragment_clear_top_close_exit);
-        } else {
-            animation = mTransitionAnimation.loadDefaultAnimationRes(isEnter
-                    ? com.android.internal.R.anim.task_fragment_close_enter
-                    : com.android.internal.R.anim.task_fragment_close_exit);
-        }
-        // Use the whole animation bounds instead of the change bounds, so that when multiple change
-        // targets are closing at the same time, the animation applied to each will be the same.
-        // Otherwise, we may see gap between the activities that are finishing together.
-        animation.initialize(wholeAnimationBounds.width(), wholeAnimationBounds.height(),
-                wholeAnimationBounds.width(), wholeAnimationBounds.height());
-        animation.scaleCurrentDuration(mTransitionAnimationScaleSetting);
-        return animation;
-    }
-
-    private float getTransitionAnimationScaleSetting() {
-        return WindowManager.fixScale(Settings.Global.getFloat(mContext.getContentResolver(),
-                Settings.Global.TRANSITION_ANIMATION_SCALE, mContext.getResources().getFloat(
-                                R.dimen.config_appTransitionAnimationDurationScaleDefault)));
-    }
-
-    private class SettingsObserver extends ContentObserver {
-        SettingsObserver(@NonNull Handler handler) {
-            super(handler);
-        }
-
-        @Override
-        public void onChange(boolean selfChange) {
-            mTransitionAnimationScaleSetting = getTransitionAnimationScaleSetting();
-        }
-    }
-}
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizerTest.java
index 7b473b0..ad41b18 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizerTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizerTest.java
@@ -23,8 +23,6 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
 
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
@@ -86,24 +84,6 @@
     }
 
     @Test
-    public void testUnregisterOrganizer() {
-        mOrganizer.overrideSplitAnimation();
-        mOrganizer.unregisterOrganizer();
-
-        verify(mOrganizer).unregisterRemoteAnimations();
-    }
-
-    @Test
-    public void testOverrideSplitAnimation() {
-        assertNull(mOrganizer.mAnimationController);
-
-        mOrganizer.overrideSplitAnimation();
-
-        assertNotNull(mOrganizer.mAnimationController);
-        verify(mOrganizer).registerRemoteAnimations(mOrganizer.mAnimationController.mDefinition);
-    }
-
-    @Test
     public void testExpandTaskFragment() {
         final TaskContainer taskContainer = createTestTaskContainer();
         doReturn(taskContainer).when(mSplitController).getTaskContainer(anyInt());
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentAnimationControllerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentAnimationControllerTest.java
deleted file mode 100644
index a1e9f08..0000000
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentAnimationControllerTest.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.window.extensions.embedding;
-
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
-
-import static org.mockito.Mockito.never;
-
-import android.platform.test.annotations.Presubmit;
-import android.window.TaskFragmentOrganizer;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.junit.MockitoJUnit;
-import org.mockito.junit.MockitoRule;
-
-/**
- * Test class for {@link TaskFragmentAnimationController}.
- *
- * Build/Install/Run:
- *  atest WMJetpackUnitTests:TaskFragmentAnimationControllerTest
- */
-@Presubmit
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class TaskFragmentAnimationControllerTest {
-    @Rule
-    public MockitoRule rule = MockitoJUnit.rule();
-
-    @Mock
-    private TaskFragmentOrganizer mOrganizer;
-    private TaskFragmentAnimationController mAnimationController;
-
-    @Before
-    public void setup() {
-        mAnimationController = new TaskFragmentAnimationController(mOrganizer);
-    }
-
-    @Test
-    public void testRegisterRemoteAnimations() {
-        mAnimationController.registerRemoteAnimations();
-
-        verify(mOrganizer).registerRemoteAnimations(mAnimationController.mDefinition);
-
-        mAnimationController.registerRemoteAnimations();
-
-        // No extra call if it has been registered.
-        verify(mOrganizer).registerRemoteAnimations(mAnimationController.mDefinition);
-    }
-
-    @Test
-    public void testUnregisterRemoteAnimations() {
-        mAnimationController.unregisterRemoteAnimations();
-
-        // No call if it is not registered.
-        verify(mOrganizer, never()).unregisterRemoteAnimations();
-
-        mAnimationController.registerRemoteAnimations();
-        mAnimationController.unregisterRemoteAnimations();
-
-        verify(mOrganizer).unregisterRemoteAnimations();
-
-        mAnimationController.unregisterRemoteAnimations();
-
-        // No extra call if it has been unregistered.
-        verify(mOrganizer).unregisterRemoteAnimations();
-    }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/HandlerExecutor.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/HandlerExecutor.java
index bfee820..736d954 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/HandlerExecutor.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/HandlerExecutor.java
@@ -54,4 +54,11 @@
     public boolean hasCallback(Runnable r) {
         return mHandler.hasCallbacks(r);
     }
+
+    @Override
+    public void assertCurrentThread() {
+        if (!mHandler.getLooper().isCurrentThread()) {
+            throw new IllegalStateException("must be called on " + mHandler);
+        }
+    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/ShellExecutor.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ShellExecutor.java
index f729164..2c2961f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/ShellExecutor.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ShellExecutor.java
@@ -96,4 +96,11 @@
      * See {@link android.os.Handler#hasCallbacks(Runnable)}.
      */
     boolean hasCallback(Runnable runnable);
+
+    /**
+     * May throw if the caller is not on the same thread as the executor.
+     */
+    default void assertCurrentThread() {
+       return;
+    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index 5b4208a..8d53beb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -38,6 +38,7 @@
 import static android.window.TransitionInfo.FLAG_NO_ANIMATION;
 import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT;
 
+import static com.android.window.flags.Flags.enforceShellThreadModel;
 import static com.android.window.flags.Flags.ensureWallpaperInTransitions;
 import static com.android.systemui.shared.Flags.returnAnimationFrameworkLibrary;
 import static com.android.wm.shell.shared.TransitionUtil.isClosingType;
@@ -924,9 +925,12 @@
         }
         // An existing animation is playing, so see if we can merge.
         final ActiveTransition playing = track.mActiveTransition;
+        final IBinder playingToken = playing.mToken;
+        final IBinder readyToken = ready.mToken;
+
         if (ready.mAborted) {
             // record as merged since it is no-op. Calls back into processReadyQueue
-            onMerged(playing, ready);
+            onMerged(playingToken, readyToken);
             return;
         }
         ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition %s ready while"
@@ -934,14 +938,31 @@
                 + " in case they can be merged", ready, playing);
         mTransitionTracer.logMergeRequested(ready.mInfo.getDebugId(), playing.mInfo.getDebugId());
         playing.mHandler.mergeAnimation(ready.mToken, ready.mInfo, ready.mStartT,
-                playing.mToken, (wct) -> onMerged(playing, ready));
+                playing.mToken, (wct) -> onMerged(playingToken, readyToken));
     }
 
-    private void onMerged(@NonNull ActiveTransition playing, @NonNull ActiveTransition merged) {
+    private void onMerged(@NonNull IBinder playingToken, @NonNull IBinder mergedToken) {
+        if (enforceShellThreadModel()) {
+            mMainExecutor.assertCurrentThread();
+        }
+
+        ActiveTransition playing = mKnownTransitions.get(playingToken);
+        if (playing == null) {
+            Log.e(TAG, "Merging into a non-existent transition: " + playingToken);
+            return;
+        }
+
+        ActiveTransition merged = mKnownTransitions.get(mergedToken);
+        if (merged == null) {
+            Log.e(TAG, "Merging a non-existent transition: " + mergedToken);
+            return;
+        }
+
         if (playing.getTrack() != merged.getTrack()) {
             throw new IllegalStateException("Can't merge across tracks: " + merged + " into "
                     + playing);
         }
+
         final Track track = mTracks.get(playing.getTrack());
         ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition was merged: %s into %s",
                 merged, playing);
@@ -1071,13 +1092,17 @@
         info.releaseAnimSurfaces();
     }
 
-    private void onFinish(IBinder token,
-            @Nullable WindowContainerTransaction wct) {
+    private void onFinish(IBinder token, @Nullable WindowContainerTransaction wct) {
+        if (enforceShellThreadModel()) {
+            mMainExecutor.assertCurrentThread();
+        }
+
         final ActiveTransition active = mKnownTransitions.get(token);
         if (active == null) {
             Log.e(TAG, "Trying to finish a non-existent transition: " + token);
             return;
         }
+
         final Track track = mTracks.get(active.getTrack());
         if (track == null || track.mActiveTransition != active) {
             Log.e(TAG, "Trying to finish a non-running transition. Either remote crashed or "
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
index 5ffd883..5d662b2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
@@ -852,18 +852,19 @@
      */
     void createHandleMenu(SplitScreenController splitScreenController) {
         loadAppInfoIfNeeded();
-        mHandleMenu = new HandleMenu.Builder(this)
-                .setAppIcon(mAppIconBitmap)
-                .setAppName(mAppName)
-                .setOnClickListener(mOnCaptionButtonClickListener)
-                .setOnTouchListener(mOnCaptionTouchListener)
-                .setLayoutId(mRelayoutParams.mLayoutResId)
-                .setWindowingButtonsVisible(DesktopModeStatus.canEnterDesktopMode(mContext))
-                .setCaptionHeight(mResult.mCaptionHeight)
-                .setDisplayController(mDisplayController)
-                .setSplitScreenController(splitScreenController)
-                .setBrowserLinkAvailable(browserLinkAvailable())
-                .build();
+        mHandleMenu = new HandleMenu(
+                this,
+                mRelayoutParams.mLayoutResId,
+                mOnCaptionButtonClickListener,
+                mOnCaptionTouchListener,
+                mAppIconBitmap,
+                mAppName,
+                mDisplayController,
+                splitScreenController,
+                DesktopModeStatus.canEnterDesktopMode(mContext),
+                browserLinkAvailable(),
+                mResult.mCaptionHeight
+        );
         mWindowDecorViewHolder.onHandleMenuOpened();
         mHandleMenu.show();
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java
deleted file mode 100644
index 7e44f32..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java
+++ /dev/null
@@ -1,539 +0,0 @@
-/*
- * Copyright (C) 2023 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.windowdecor;
-
-import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
-import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.view.MotionEvent.ACTION_DOWN;
-import static android.view.MotionEvent.ACTION_UP;
-
-import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
-import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.ActivityManager.RunningTaskInfo;
-import android.content.Context;
-import android.content.res.ColorStateList;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.graphics.Bitmap;
-import android.graphics.Color;
-import android.graphics.Point;
-import android.graphics.PointF;
-import android.graphics.Rect;
-import android.view.MotionEvent;
-import android.view.SurfaceControl;
-import android.view.View;
-import android.widget.Button;
-import android.widget.ImageButton;
-import android.widget.ImageView;
-import android.widget.TextView;
-import android.window.SurfaceSyncGroup;
-
-import androidx.annotation.VisibleForTesting;
-
-import com.android.window.flags.Flags;
-import com.android.wm.shell.R;
-import com.android.wm.shell.common.DisplayController;
-import com.android.wm.shell.common.DisplayLayout;
-import com.android.wm.shell.splitscreen.SplitScreenController;
-import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalSystemViewContainer;
-import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalViewContainer;
-
-/**
- * Handle menu opened when the appropriate button is clicked on.
- *
- * Displays up to 3 pills that show the following:
- * App Info: App name, app icon, and collapse button to close the menu.
- * Windowing Options(Proto 2 only): Buttons to change windowing modes.
- * Additional Options: Miscellaneous functions including screenshot and closing task.
- */
-class HandleMenu {
-    private static final String TAG = "HandleMenu";
-    private static final boolean SHOULD_SHOW_MORE_ACTIONS_PILL = false;
-    private final Context mContext;
-    private final DesktopModeWindowDecoration mParentDecor;
-    @VisibleForTesting
-    AdditionalViewContainer mHandleMenuViewContainer;
-    // Position of the handle menu used for laying out the handle view.
-    @VisibleForTesting
-    final PointF mHandleMenuPosition = new PointF();
-    // With the introduction of {@link AdditionalSystemViewContainer}, {@link mHandleMenuPosition}
-    // may be in a different coordinate space than the input coordinates. Therefore, we still care
-    // about the menu's coordinates relative to the display as a whole, so we need to maintain
-    // those as well.
-    final Point mGlobalMenuPosition = new Point();
-    private final boolean mShouldShowWindowingPill;
-    private final boolean mShouldShowBrowserPill;
-    private final Bitmap mAppIconBitmap;
-    private final CharSequence mAppName;
-    private final View.OnClickListener mOnClickListener;
-    private final View.OnTouchListener mOnTouchListener;
-    private final RunningTaskInfo mTaskInfo;
-    private final DisplayController mDisplayController;
-    private final SplitScreenController mSplitScreenController;
-    private final int mLayoutResId;
-    private int mMarginMenuTop;
-    private int mMarginMenuStart;
-    private int mMenuHeight;
-    private int mMenuWidth;
-    private final int mCaptionHeight;
-    private HandleMenuAnimator mHandleMenuAnimator;
-
-
-    HandleMenu(DesktopModeWindowDecoration parentDecor, int layoutResId,
-            View.OnClickListener onClickListener, View.OnTouchListener onTouchListener,
-            Bitmap appIcon, CharSequence appName, DisplayController displayController,
-            SplitScreenController splitScreenController, boolean shouldShowWindowingPill,
-            boolean shouldShowBrowserPill, int captionHeight) {
-        mParentDecor = parentDecor;
-        mContext = mParentDecor.mDecorWindowContext;
-        mTaskInfo = mParentDecor.mTaskInfo;
-        mDisplayController = displayController;
-        mSplitScreenController = splitScreenController;
-        mLayoutResId = layoutResId;
-        mOnClickListener = onClickListener;
-        mOnTouchListener = onTouchListener;
-        mAppIconBitmap = appIcon;
-        mAppName = appName;
-        mShouldShowWindowingPill = shouldShowWindowingPill;
-        mShouldShowBrowserPill = shouldShowBrowserPill;
-        mCaptionHeight = captionHeight;
-        loadHandleMenuDimensions();
-        updateHandleMenuPillPositions();
-    }
-
-    void show() {
-        final SurfaceSyncGroup ssg = new SurfaceSyncGroup(TAG);
-        SurfaceControl.Transaction t = new SurfaceControl.Transaction();
-
-        createHandleMenuViewContainer(t, ssg);
-        ssg.addTransaction(t);
-        ssg.markSyncReady();
-        setupHandleMenu();
-        animateHandleMenu();
-    }
-
-    private void createHandleMenuViewContainer(SurfaceControl.Transaction t,
-            SurfaceSyncGroup ssg) {
-        final int x = (int) mHandleMenuPosition.x;
-        final int y = (int) mHandleMenuPosition.y;
-        if (!mTaskInfo.isFreeform() && Flags.enableAdditionalWindowsAboveStatusBar()) {
-            mHandleMenuViewContainer = new AdditionalSystemViewContainer(mContext,
-                    R.layout.desktop_mode_window_decor_handle_menu, mTaskInfo.taskId,
-                    x, y, mMenuWidth, mMenuHeight);
-        } else {
-            mHandleMenuViewContainer = mParentDecor.addWindow(
-                    R.layout.desktop_mode_window_decor_handle_menu, "Handle Menu",
-                    t, ssg, x, y, mMenuWidth, mMenuHeight);
-        }
-        final View handleMenuView = mHandleMenuViewContainer.getView();
-        mHandleMenuAnimator = new HandleMenuAnimator(handleMenuView, mMenuWidth, mCaptionHeight);
-    }
-
-    /**
-     * Animates the appearance of the handle menu and its three pills.
-     */
-    private void animateHandleMenu() {
-        if (mTaskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN
-                || mTaskInfo.getWindowingMode() == WINDOWING_MODE_MULTI_WINDOW) {
-            mHandleMenuAnimator.animateCaptionHandleExpandToOpen();
-        } else {
-            mHandleMenuAnimator.animateOpen();
-        }
-    }
-
-    /**
-     * Set up all three pills of the handle menu: app info pill, windowing pill, & more actions
-     * pill.
-     */
-    private void setupHandleMenu() {
-        final View handleMenu = mHandleMenuViewContainer.getView();
-        handleMenu.setOnTouchListener(mOnTouchListener);
-        setupAppInfoPill(handleMenu);
-        if (mShouldShowWindowingPill) {
-            setupWindowingPill(handleMenu);
-        }
-        setupMoreActionsPill(handleMenu);
-        setupOpenInBrowserPill(handleMenu);
-    }
-
-    /**
-     * Set up interactive elements of handle menu's app info pill.
-     */
-    private void setupAppInfoPill(View handleMenu) {
-        final HandleMenuImageButton collapseBtn =
-                handleMenu.findViewById(R.id.collapse_menu_button);
-        final ImageView appIcon = handleMenu.findViewById(R.id.application_icon);
-        final TextView appName = handleMenu.findViewById(R.id.application_name);
-        collapseBtn.setOnClickListener(mOnClickListener);
-        collapseBtn.setTaskInfo(mTaskInfo);
-        appIcon.setImageBitmap(mAppIconBitmap);
-        appName.setText(mAppName);
-    }
-
-    /**
-     * Set up interactive elements and color of handle menu's windowing pill.
-     */
-    private void setupWindowingPill(View handleMenu) {
-        final ImageButton fullscreenBtn = handleMenu.findViewById(
-                R.id.fullscreen_button);
-        final ImageButton splitscreenBtn = handleMenu.findViewById(
-                R.id.split_screen_button);
-        final ImageButton floatingBtn = handleMenu.findViewById(R.id.floating_button);
-        // TODO: Remove once implemented.
-        floatingBtn.setVisibility(View.GONE);
-
-        final ImageButton desktopBtn = handleMenu.findViewById(R.id.desktop_button);
-        fullscreenBtn.setOnClickListener(mOnClickListener);
-        splitscreenBtn.setOnClickListener(mOnClickListener);
-        floatingBtn.setOnClickListener(mOnClickListener);
-        desktopBtn.setOnClickListener(mOnClickListener);
-        // The button corresponding to the windowing mode that the task is currently in uses a
-        // different color than the others.
-        final ColorStateList[] iconColors = getWindowingIconColor();
-        final ColorStateList inActiveColorStateList = iconColors[0];
-        final ColorStateList activeColorStateList = iconColors[1];
-        final int windowingMode = mTaskInfo.getWindowingMode();
-        fullscreenBtn.setImageTintList(windowingMode == WINDOWING_MODE_FULLSCREEN
-                ? activeColorStateList : inActiveColorStateList);
-        splitscreenBtn.setImageTintList(windowingMode == WINDOWING_MODE_MULTI_WINDOW
-                ? activeColorStateList : inActiveColorStateList);
-        floatingBtn.setImageTintList(windowingMode == WINDOWING_MODE_PINNED
-                ? activeColorStateList : inActiveColorStateList);
-        desktopBtn.setImageTintList(windowingMode == WINDOWING_MODE_FREEFORM
-                ? activeColorStateList : inActiveColorStateList);
-    }
-
-    /**
-     * Set up interactive elements & height of handle menu's more actions pill
-     */
-    private void setupMoreActionsPill(View handleMenu) {
-        if (!SHOULD_SHOW_MORE_ACTIONS_PILL) {
-            handleMenu.findViewById(R.id.more_actions_pill).setVisibility(View.GONE);
-        }
-    }
-
-    private void setupOpenInBrowserPill(View handleMenu) {
-        if (!mShouldShowBrowserPill) {
-            handleMenu.findViewById(R.id.open_in_browser_pill).setVisibility(View.GONE);
-            return;
-        }
-        final Button browserButton = handleMenu.findViewById(R.id.open_in_browser_button);
-        browserButton.setOnClickListener(mOnClickListener);
-    }
-
-    /**
-     * Returns array of windowing icon color based on current UI theme. First element of the
-     * array is for inactive icons and the second is for active icons.
-     */
-    private ColorStateList[] getWindowingIconColor() {
-        final int mode = mContext.getResources().getConfiguration().uiMode
-                & Configuration.UI_MODE_NIGHT_MASK;
-        final boolean isNightMode = (mode == Configuration.UI_MODE_NIGHT_YES);
-        final TypedArray typedArray = mContext.obtainStyledAttributes(new int[]{
-                com.android.internal.R.attr.materialColorOnSurface,
-                com.android.internal.R.attr.materialColorPrimary});
-        final int inActiveColor = typedArray.getColor(0, isNightMode ? Color.WHITE : Color.BLACK);
-        final int activeColor = typedArray.getColor(1, isNightMode ? Color.WHITE : Color.BLACK);
-        typedArray.recycle();
-        return new ColorStateList[]{ColorStateList.valueOf(inActiveColor),
-                ColorStateList.valueOf(activeColor)};
-    }
-
-    /**
-     * Updates handle menu's position variables to reflect its next position.
-     */
-    private void updateHandleMenuPillPositions() {
-        int menuX;
-        final int menuY;
-        final Rect taskBounds = mTaskInfo.getConfiguration().windowConfiguration.getBounds();
-        updateGlobalMenuPosition(taskBounds);
-        if (mLayoutResId == R.layout.desktop_mode_app_header) {
-            // Align the handle menu to the left side of the caption.
-            menuX = mMarginMenuStart;
-            menuY = mMarginMenuTop;
-        } else {
-            if (Flags.enableAdditionalWindowsAboveStatusBar()) {
-                // In a focused decor, we use global coordinates for handle menu. Therefore we
-                // need to account for other factors like split stage and menu/handle width to
-                // center the menu.
-                final DisplayLayout layout = mDisplayController
-                        .getDisplayLayout(mTaskInfo.displayId);
-                menuX = mGlobalMenuPosition.x + ((mMenuWidth - layout.width()) / 2);
-                menuY = mGlobalMenuPosition.y + ((mMenuHeight - layout.height()) / 2);
-            } else {
-                menuX = (taskBounds.width() / 2) - (mMenuWidth / 2);
-                menuY = mMarginMenuTop;
-            }
-        }
-        // Handle Menu position setup.
-        mHandleMenuPosition.set(menuX, menuY);
-    }
-
-    private void updateGlobalMenuPosition(Rect taskBounds) {
-        if (mTaskInfo.isFreeform()) {
-            mGlobalMenuPosition.set(taskBounds.left + mMarginMenuStart,
-                    taskBounds.top + mMarginMenuTop);
-        } else if (mTaskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) {
-            mGlobalMenuPosition.set(
-                    (taskBounds.width() / 2) - (mMenuWidth / 2) + mMarginMenuStart,
-                    mMarginMenuTop
-            );
-        } else if (mTaskInfo.getWindowingMode() == WINDOWING_MODE_MULTI_WINDOW) {
-            final int splitPosition = mSplitScreenController.getSplitPosition(mTaskInfo.taskId);
-            final Rect leftOrTopStageBounds = new Rect();
-            final Rect rightOrBottomStageBounds = new Rect();
-            mSplitScreenController.getStageBounds(leftOrTopStageBounds,
-                    rightOrBottomStageBounds);
-            // TODO(b/343561161): This needs to be calculated differently if the task is in
-            //  top/bottom split.
-            if (splitPosition == SPLIT_POSITION_BOTTOM_OR_RIGHT) {
-                mGlobalMenuPosition.set(leftOrTopStageBounds.width()
-                                + (rightOrBottomStageBounds.width() / 2)
-                                - (mMenuWidth / 2) + mMarginMenuStart,
-                        mMarginMenuTop);
-            } else if (splitPosition == SPLIT_POSITION_TOP_OR_LEFT) {
-                mGlobalMenuPosition.set((leftOrTopStageBounds.width() / 2)
-                                - (mMenuWidth / 2) + mMarginMenuStart,
-                        mMarginMenuTop);
-            }
-        }
-    }
-
-    /**
-     * Update pill layout, in case task changes have caused positioning to change.
-     */
-    void relayout(SurfaceControl.Transaction t) {
-        if (mHandleMenuViewContainer != null) {
-            updateHandleMenuPillPositions();
-            mHandleMenuViewContainer.setPosition(t, mHandleMenuPosition.x, mHandleMenuPosition.y);
-        }
-    }
-
-    /**
-     * Check a passed MotionEvent if a click or hover has occurred on any button on this caption
-     * Note this should only be called when a regular onClick/onHover is not possible
-     * (i.e. the button was clicked through status bar layer)
-     *
-     * @param ev the MotionEvent to compare against.
-     */
-    void checkMotionEvent(MotionEvent ev) {
-        // If the menu view is above status bar, we can let the views handle input directly.
-        if (isViewAboveStatusBar()) return;
-        final View handleMenu = mHandleMenuViewContainer.getView();
-        final HandleMenuImageButton collapse = handleMenu.findViewById(R.id.collapse_menu_button);
-        final PointF inputPoint = translateInputToLocalSpace(ev);
-        final boolean inputInCollapseButton = pointInView(collapse, inputPoint.x, inputPoint.y);
-        final int action = ev.getActionMasked();
-        collapse.setHovered(inputInCollapseButton && action != ACTION_UP);
-        collapse.setPressed(inputInCollapseButton && action == ACTION_DOWN);
-        if (action == ACTION_UP && inputInCollapseButton) {
-            collapse.performClick();
-        }
-    }
-
-    private boolean isViewAboveStatusBar() {
-        return Flags.enableAdditionalWindowsAboveStatusBar()
-                && !mTaskInfo.isFreeform();
-    }
-
-    // Translate the input point from display coordinates to the same space as the handle menu.
-    private PointF translateInputToLocalSpace(MotionEvent ev) {
-        return new PointF(ev.getX() - mHandleMenuPosition.x,
-                ev.getY() - mHandleMenuPosition.y);
-    }
-
-    /**
-     * A valid menu input is one of the following:
-     * An input that happens in the menu views.
-     * Any input before the views have been laid out.
-     *
-     * @param inputPoint the input to compare against.
-     */
-    boolean isValidMenuInput(PointF inputPoint) {
-        if (!viewsLaidOut()) return true;
-        if (!isViewAboveStatusBar()) {
-            return pointInView(
-                    mHandleMenuViewContainer.getView(),
-                    inputPoint.x - mHandleMenuPosition.x,
-                    inputPoint.y - mHandleMenuPosition.y);
-        } else {
-            // Handle menu exists in a different coordinate space when added to WindowManager.
-            // Therefore we must compare the provided input coordinates to global menu coordinates.
-            // This includes factoring for split stage as input coordinates are relative to split
-            // stage position, not relative to the display as a whole.
-            PointF inputRelativeToMenu = new PointF(
-                    inputPoint.x - mGlobalMenuPosition.x,
-                    inputPoint.y - mGlobalMenuPosition.y
-            );
-            if (mSplitScreenController.getSplitPosition(mTaskInfo.taskId)
-                    == SPLIT_POSITION_BOTTOM_OR_RIGHT) {
-                // TODO(b/343561161): This also needs to be calculated differently if
-                //  the task is in top/bottom split.
-                Rect leftStageBounds = new Rect();
-                mSplitScreenController.getStageBounds(leftStageBounds, new Rect());
-                inputRelativeToMenu.x += leftStageBounds.width();
-            }
-            return pointInView(
-                    mHandleMenuViewContainer.getView(),
-                    inputRelativeToMenu.x,
-                    inputRelativeToMenu.y);
-        }
-    }
-
-    private boolean pointInView(View v, float x, float y) {
-        return v != null && v.getLeft() <= x && v.getRight() >= x
-                && v.getTop() <= y && v.getBottom() >= y;
-    }
-
-    /**
-     * Check if the views for handle menu can be seen.
-     */
-    private boolean viewsLaidOut() {
-        return mHandleMenuViewContainer.getView().isLaidOut();
-    }
-
-    private void loadHandleMenuDimensions() {
-        final Resources resources = mContext.getResources();
-        mMenuWidth = loadDimensionPixelSize(resources,
-                R.dimen.desktop_mode_handle_menu_width);
-        mMenuHeight = getHandleMenuHeight(resources);
-        mMarginMenuTop = loadDimensionPixelSize(resources,
-                R.dimen.desktop_mode_handle_menu_margin_top);
-        mMarginMenuStart = loadDimensionPixelSize(resources,
-                R.dimen.desktop_mode_handle_menu_margin_start);
-    }
-
-    /**
-     * Determines handle menu height based on if windowing pill should be shown.
-     */
-    private int getHandleMenuHeight(Resources resources) {
-        int menuHeight = loadDimensionPixelSize(resources, R.dimen.desktop_mode_handle_menu_height);
-        if (!mShouldShowWindowingPill) {
-            menuHeight -= loadDimensionPixelSize(resources,
-                    R.dimen.desktop_mode_handle_menu_windowing_pill_height);
-        }
-        if (!SHOULD_SHOW_MORE_ACTIONS_PILL) {
-            menuHeight -= loadDimensionPixelSize(resources,
-                    R.dimen.desktop_mode_handle_menu_more_actions_pill_height);
-        }
-        if (!mShouldShowBrowserPill) {
-            menuHeight -= loadDimensionPixelSize(resources,
-                    R.dimen.desktop_mode_handle_menu_open_in_browser_pill_height);
-        }
-        return menuHeight;
-    }
-
-    private int loadDimensionPixelSize(Resources resources, int resourceId) {
-        if (resourceId == Resources.ID_NULL) {
-            return 0;
-        }
-        return resources.getDimensionPixelSize(resourceId);
-    }
-
-    void close() {
-        final Runnable after = () -> {
-            mHandleMenuViewContainer.releaseView();
-            mHandleMenuViewContainer = null;
-        };
-        if (mTaskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN
-                || mTaskInfo.getWindowingMode() == WINDOWING_MODE_MULTI_WINDOW) {
-            mHandleMenuAnimator.animateCollapseIntoHandleClose(after);
-        } else {
-            mHandleMenuAnimator.animateClose(after);
-        }
-    }
-
-    static final class Builder {
-        private final DesktopModeWindowDecoration mParent;
-        private CharSequence mName;
-        private Bitmap mAppIcon;
-        private View.OnClickListener mOnClickListener;
-        private View.OnTouchListener mOnTouchListener;
-        private int mLayoutId;
-        private boolean mShowWindowingPill;
-        private int mCaptionHeight;
-        private DisplayController mDisplayController;
-        private SplitScreenController mSplitScreenController;
-        private boolean mShowBrowserPill;
-
-        Builder(@NonNull DesktopModeWindowDecoration parent) {
-            mParent = parent;
-        }
-
-        Builder setAppName(@Nullable CharSequence name) {
-            mName = name;
-            return this;
-        }
-
-        Builder setAppIcon(@Nullable Bitmap appIcon) {
-            mAppIcon = appIcon;
-            return this;
-        }
-
-        Builder setOnClickListener(@Nullable View.OnClickListener onClickListener) {
-            mOnClickListener = onClickListener;
-            return this;
-        }
-
-        Builder setOnTouchListener(@Nullable View.OnTouchListener onTouchListener) {
-            mOnTouchListener = onTouchListener;
-            return this;
-        }
-
-        Builder setLayoutId(int layoutId) {
-            mLayoutId = layoutId;
-            return this;
-        }
-
-        Builder setWindowingButtonsVisible(boolean windowingButtonsVisible) {
-            mShowWindowingPill = windowingButtonsVisible;
-            return this;
-        }
-
-        Builder setCaptionHeight(int captionHeight) {
-            mCaptionHeight = captionHeight;
-            return this;
-        }
-
-        Builder setDisplayController(DisplayController displayController) {
-            mDisplayController = displayController;
-            return this;
-        }
-
-        Builder setSplitScreenController(SplitScreenController splitScreenController) {
-            mSplitScreenController = splitScreenController;
-            return this;
-        }
-
-        Builder setBrowserLinkAvailable(Boolean showBrowserPill) {
-            mShowBrowserPill = showBrowserPill;
-            return this;
-        }
-
-        HandleMenu build() {
-            return new HandleMenu(mParent, mLayoutId, mOnClickListener,
-                    mOnTouchListener, mAppIcon, mName, mDisplayController, mSplitScreenController,
-                    mShowWindowingPill, mShowBrowserPill, mCaptionHeight);
-        }
-    }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt
new file mode 100644
index 0000000..39595cf
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt
@@ -0,0 +1,491 @@
+/*
+ * Copyright (C) 2023 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.windowdecor
+
+import android.app.ActivityManager
+import android.app.WindowConfiguration
+import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
+import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN
+import android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW
+import android.app.WindowConfiguration.WINDOWING_MODE_PINNED
+import android.content.Context
+import android.content.res.ColorStateList
+import android.content.res.Configuration
+import android.content.res.Resources
+import android.graphics.Bitmap
+import android.graphics.Color
+import android.graphics.Point
+import android.graphics.PointF
+import android.graphics.Rect
+import android.view.MotionEvent
+import android.view.SurfaceControl
+import android.view.View
+import android.widget.Button
+import android.widget.ImageButton
+import android.widget.ImageView
+import android.widget.TextView
+import android.window.SurfaceSyncGroup
+import androidx.annotation.VisibleForTesting
+import com.android.window.flags.Flags
+import com.android.wm.shell.R
+import com.android.wm.shell.common.DisplayController
+import com.android.wm.shell.common.split.SplitScreenConstants
+import com.android.wm.shell.splitscreen.SplitScreenController
+import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalSystemViewContainer
+import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalViewContainer
+import com.android.wm.shell.windowdecor.extension.isFullscreen
+
+/**
+ * Handle menu opened when the appropriate button is clicked on.
+ *
+ * Displays up to 3 pills that show the following:
+ * App Info: App name, app icon, and collapse button to close the menu.
+ * Windowing Options(Proto 2 only): Buttons to change windowing modes.
+ * Additional Options: Miscellaneous functions including screenshot and closing task.
+ */
+class HandleMenu(
+    private val parentDecor: DesktopModeWindowDecoration,
+    private val layoutResId: Int,
+    private val onClickListener: View.OnClickListener?,
+    private val onTouchListener: View.OnTouchListener?,
+    private val appIconBitmap: Bitmap?,
+    private val appName: CharSequence?,
+    private val displayController: DisplayController,
+    private val splitScreenController: SplitScreenController,
+    private val shouldShowWindowingPill: Boolean,
+    private val shouldShowBrowserPill: Boolean,
+    private val captionHeight: Int
+) {
+    private val context: Context = parentDecor.mDecorWindowContext
+    private val taskInfo: ActivityManager.RunningTaskInfo = parentDecor.mTaskInfo
+
+    private val isViewAboveStatusBar: Boolean
+        get() = (Flags.enableAdditionalWindowsAboveStatusBar() && !taskInfo.isFreeform)
+
+    private var marginMenuTop = 0
+    private var marginMenuStart = 0
+    private var menuHeight = 0
+    private var menuWidth = 0
+    private var handleMenuAnimator: HandleMenuAnimator? = null
+
+    @VisibleForTesting
+    var handleMenuViewContainer: AdditionalViewContainer? = null
+
+    // Position of the handle menu used for laying out the handle view.
+    @VisibleForTesting
+    val handleMenuPosition: PointF = PointF()
+
+    // With the introduction of {@link AdditionalSystemViewContainer}, {@link mHandleMenuPosition}
+    // may be in a different coordinate space than the input coordinates. Therefore, we still care
+    // about the menu's coordinates relative to the display as a whole, so we need to maintain
+    // those as well.
+    private val globalMenuPosition: Point = Point()
+
+    /**
+     * An a array of windowing icon color based on current UI theme. First element of the
+     * array is for inactive icons and the second is for active icons.
+     */
+    private val windowingIconColor: Array<ColorStateList>
+        get() {
+            val mode = (context.resources.configuration.uiMode
+                    and Configuration.UI_MODE_NIGHT_MASK)
+            val isNightMode = (mode == Configuration.UI_MODE_NIGHT_YES)
+            val typedArray = context.obtainStyledAttributes(
+                intArrayOf(
+                    com.android.internal.R.attr.materialColorOnSurface,
+                    com.android.internal.R.attr.materialColorPrimary
+                )
+            )
+            val inActiveColor =
+                typedArray.getColor(0, if (isNightMode) Color.WHITE else Color.BLACK)
+            val activeColor = typedArray.getColor(1, if (isNightMode) Color.WHITE else Color.BLACK)
+            typedArray.recycle()
+            return arrayOf(
+                ColorStateList.valueOf(inActiveColor),
+                ColorStateList.valueOf(activeColor)
+            )
+        }
+
+    init {
+        loadHandleMenuDimensions()
+        updateHandleMenuPillPositions()
+    }
+
+    fun show() {
+        val ssg = SurfaceSyncGroup(TAG)
+        val t = SurfaceControl.Transaction()
+
+        createHandleMenuViewContainer(t, ssg)
+        ssg.addTransaction(t)
+        ssg.markSyncReady()
+        setupHandleMenu()
+        animateHandleMenu()
+    }
+
+    private fun createHandleMenuViewContainer(
+        t: SurfaceControl.Transaction,
+        ssg: SurfaceSyncGroup
+    ) {
+        val x = handleMenuPosition.x.toInt()
+        val y = handleMenuPosition.y.toInt()
+        handleMenuViewContainer =
+            if (!taskInfo.isFreeform && Flags.enableAdditionalWindowsAboveStatusBar()) {
+                AdditionalSystemViewContainer(
+                    context = context,
+                    layoutId = R.layout.desktop_mode_window_decor_handle_menu,
+                    taskId = taskInfo.taskId,
+                    x = x,
+                    y = y,
+                    width = menuWidth,
+                    height = menuHeight
+                )
+            } else {
+                parentDecor.addWindow(
+                    R.layout.desktop_mode_window_decor_handle_menu, "Handle Menu",
+                    t, ssg, x, y, menuWidth, menuHeight
+                )
+            }
+        handleMenuViewContainer?.view?.let { view ->
+            handleMenuAnimator =
+                HandleMenuAnimator(view, menuWidth, captionHeight.toFloat())
+        }
+    }
+
+    /**
+     * Animates the appearance of the handle menu and its three pills.
+     */
+    private fun animateHandleMenu() {
+        when (taskInfo.windowingMode) {
+            WindowConfiguration.WINDOWING_MODE_FULLSCREEN,
+            WINDOWING_MODE_MULTI_WINDOW -> {
+                handleMenuAnimator?.animateCaptionHandleExpandToOpen()
+            }
+            else -> {
+                handleMenuAnimator?.animateOpen()
+            }
+        }
+    }
+
+    /**
+     * Set up all three pills of the handle menu: app info pill, windowing pill, & more actions
+     * pill.
+     */
+    private fun setupHandleMenu() {
+        val handleMenu = handleMenuViewContainer?.view ?: return
+        handleMenu.setOnTouchListener(onTouchListener)
+        setupAppInfoPill(handleMenu)
+        if (shouldShowWindowingPill) {
+            setupWindowingPill(handleMenu)
+        }
+        setupMoreActionsPill(handleMenu)
+        setupOpenInBrowserPill(handleMenu)
+    }
+
+    /**
+     * Set up interactive elements of handle menu's app info pill.
+     */
+    private fun setupAppInfoPill(handleMenu: View) {
+        val collapseBtn = handleMenu.findViewById<HandleMenuImageButton>(R.id.collapse_menu_button)
+        val appIcon = handleMenu.findViewById<ImageView>(R.id.application_icon)
+        val appName = handleMenu.findViewById<TextView>(R.id.application_name)
+        collapseBtn.setOnClickListener(onClickListener)
+        collapseBtn.taskInfo = taskInfo
+        appIcon.setImageBitmap(appIconBitmap)
+        appName.text = this.appName
+    }
+
+    /**
+     * Set up interactive elements and color of handle menu's windowing pill.
+     */
+    private fun setupWindowingPill(handleMenu: View) {
+        val fullscreenBtn = handleMenu.findViewById<ImageButton>(R.id.fullscreen_button)
+        val splitscreenBtn = handleMenu.findViewById<ImageButton>(R.id.split_screen_button)
+        val floatingBtn = handleMenu.findViewById<ImageButton>(R.id.floating_button)
+        // TODO: Remove once implemented.
+        floatingBtn.visibility = View.GONE
+
+        val desktopBtn = handleMenu.findViewById<ImageButton>(R.id.desktop_button)
+        fullscreenBtn.setOnClickListener(onClickListener)
+        splitscreenBtn.setOnClickListener(onClickListener)
+        floatingBtn.setOnClickListener(onClickListener)
+        desktopBtn.setOnClickListener(onClickListener)
+        // The button corresponding to the windowing mode that the task is currently in uses a
+        // different color than the others.
+        val iconColors = windowingIconColor
+        val inActiveColorStateList = iconColors[0]
+        val activeColorStateList = iconColors[1]
+        fullscreenBtn.imageTintList = if (taskInfo.isFullscreen) {
+            activeColorStateList
+        } else {
+            inActiveColorStateList
+        }
+        splitscreenBtn.imageTintList = if (taskInfo.windowingMode == WINDOWING_MODE_MULTI_WINDOW) {
+            activeColorStateList
+        } else {
+            inActiveColorStateList
+        }
+        floatingBtn.imageTintList = if (taskInfo.windowingMode == WINDOWING_MODE_PINNED) {
+            activeColorStateList
+        } else {
+            inActiveColorStateList
+        }
+        desktopBtn.imageTintList = if (taskInfo.isFreeform) {
+            activeColorStateList
+        } else {
+            inActiveColorStateList
+        }
+    }
+
+    /**
+     * Set up interactive elements & height of handle menu's more actions pill
+     */
+    private fun setupMoreActionsPill(handleMenu: View) {
+        if (!SHOULD_SHOW_MORE_ACTIONS_PILL) {
+            handleMenu.findViewById<View>(R.id.more_actions_pill).visibility = View.GONE
+        }
+    }
+
+    private fun setupOpenInBrowserPill(handleMenu: View) {
+        if (!shouldShowBrowserPill) {
+            handleMenu.findViewById<View>(R.id.open_in_browser_pill).visibility = View.GONE
+            return
+        }
+        val browserButton = handleMenu.findViewById<Button>(R.id.open_in_browser_button)
+        browserButton.setOnClickListener(onClickListener)
+    }
+
+    /**
+     * Updates handle menu's position variables to reflect its next position.
+     */
+    private fun updateHandleMenuPillPositions() {
+        val menuX: Int
+        val menuY: Int
+        val taskBounds = taskInfo.getConfiguration().windowConfiguration.bounds
+        updateGlobalMenuPosition(taskBounds)
+        if (layoutResId == R.layout.desktop_mode_app_header) {
+            // Align the handle menu to the left side of the caption.
+            menuX = marginMenuStart
+            menuY = marginMenuTop
+        } else {
+            if (Flags.enableAdditionalWindowsAboveStatusBar()) {
+                // In a focused decor, we use global coordinates for handle menu. Therefore we
+                // need to account for other factors like split stage and menu/handle width to
+                // center the menu.
+                menuX = globalMenuPosition.x
+                menuY = globalMenuPosition.y
+            } else {
+                menuX = (taskBounds.width() / 2) - (menuWidth / 2)
+                menuY = marginMenuTop
+            }
+        }
+        // Handle Menu position setup.
+        handleMenuPosition.set(menuX.toFloat(), menuY.toFloat())
+    }
+
+    private fun updateGlobalMenuPosition(taskBounds: Rect) {
+        when (taskInfo.windowingMode) {
+            WINDOWING_MODE_FREEFORM -> {
+                globalMenuPosition.set(
+                    /* x = */ taskBounds.left + marginMenuStart,
+                    /* y = */ taskBounds.top + marginMenuTop
+                )
+            }
+            WINDOWING_MODE_FULLSCREEN -> {
+                globalMenuPosition.set(
+                    /* x = */ taskBounds.width() / 2 - (menuWidth / 2),
+                    /* y = */ marginMenuTop
+                )
+            }
+            WINDOWING_MODE_MULTI_WINDOW -> {
+                val splitPosition = splitScreenController.getSplitPosition(taskInfo.taskId)
+                val leftOrTopStageBounds = Rect()
+                val rightOrBottomStageBounds = Rect()
+                splitScreenController.getStageBounds(leftOrTopStageBounds, rightOrBottomStageBounds)
+                // TODO(b/343561161): This needs to be calculated differently if the task is in
+                //  top/bottom split.
+                when (splitPosition) {
+                    SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT -> {
+                        globalMenuPosition.set(
+                            /* x = */ leftOrTopStageBounds.width()
+                                    + (rightOrBottomStageBounds.width() / 2)
+                                    - (menuWidth / 2),
+                            /* y = */ marginMenuTop
+                        )
+                    }
+                    SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT -> {
+                        globalMenuPosition.set(
+                            /* x = */ (leftOrTopStageBounds.width() / 2)
+                                    - (menuWidth / 2),
+                            /* y = */ marginMenuTop
+                        )
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Update pill layout, in case task changes have caused positioning to change.
+     */
+    fun relayout(t: SurfaceControl.Transaction) {
+        handleMenuViewContainer?.let { container ->
+            updateHandleMenuPillPositions()
+            container.setPosition(t, handleMenuPosition.x, handleMenuPosition.y)
+        }
+    }
+
+    /**
+     * Check a passed MotionEvent if a click or hover has occurred on any button on this caption
+     * Note this should only be called when a regular onClick/onHover is not possible
+     * (i.e. the button was clicked through status bar layer)
+     *
+     * @param ev the MotionEvent to compare against.
+     */
+    fun checkMotionEvent(ev: MotionEvent) {
+        // If the menu view is above status bar, we can let the views handle input directly.
+        if (isViewAboveStatusBar) return
+        val handleMenu = handleMenuViewContainer?.view ?: return
+        val collapse = handleMenu.findViewById<HandleMenuImageButton>(R.id.collapse_menu_button)
+        val inputPoint = translateInputToLocalSpace(ev)
+        val inputInCollapseButton = pointInView(collapse, inputPoint.x, inputPoint.y)
+        val action = ev.actionMasked
+        collapse.isHovered = inputInCollapseButton && action != MotionEvent.ACTION_UP
+        collapse.isPressed = inputInCollapseButton && action == MotionEvent.ACTION_DOWN
+        if (action == MotionEvent.ACTION_UP && inputInCollapseButton) {
+            collapse.performClick()
+        }
+    }
+
+    // Translate the input point from display coordinates to the same space as the handle menu.
+    private fun translateInputToLocalSpace(ev: MotionEvent): PointF {
+        return PointF(
+            ev.x - handleMenuPosition.x,
+            ev.y - handleMenuPosition.y
+        )
+    }
+
+    /**
+     * A valid menu input is one of the following:
+     * An input that happens in the menu views.
+     * Any input before the views have been laid out.
+     *
+     * @param inputPoint the input to compare against.
+     */
+    fun isValidMenuInput(inputPoint: PointF): Boolean {
+        if (!viewsLaidOut()) return true
+        if (!isViewAboveStatusBar) {
+            return pointInView(
+                handleMenuViewContainer?.view,
+                inputPoint.x - handleMenuPosition.x,
+                inputPoint.y - handleMenuPosition.y
+            )
+        } else {
+            // Handle menu exists in a different coordinate space when added to WindowManager.
+            // Therefore we must compare the provided input coordinates to global menu coordinates.
+            // This includes factoring for split stage as input coordinates are relative to split
+            // stage position, not relative to the display as a whole.
+            val inputRelativeToMenu = PointF(
+                inputPoint.x - globalMenuPosition.x,
+                inputPoint.y - globalMenuPosition.y
+            )
+            if (splitScreenController.getSplitPosition(taskInfo.taskId)
+                == SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT) {
+                // TODO(b/343561161): This also needs to be calculated differently if
+                //  the task is in top/bottom split.
+                val leftStageBounds = Rect()
+                splitScreenController.getStageBounds(leftStageBounds, Rect())
+                inputRelativeToMenu.x += leftStageBounds.width().toFloat()
+            }
+            return pointInView(
+                handleMenuViewContainer?.view,
+                inputRelativeToMenu.x,
+                inputRelativeToMenu.y
+            )
+        }
+    }
+
+    private fun pointInView(v: View?, x: Float, y: Float): Boolean {
+        return v != null && v.left <= x && v.right >= x && v.top <= y && v.bottom >= y
+    }
+
+    /**
+     * Check if the views for handle menu can be seen.
+     */
+    private fun viewsLaidOut(): Boolean = handleMenuViewContainer?.view?.isLaidOut ?: false
+
+    private fun loadHandleMenuDimensions() {
+        val resources = context.resources
+        menuWidth = loadDimensionPixelSize(resources, R.dimen.desktop_mode_handle_menu_width)
+        menuHeight = getHandleMenuHeight(resources)
+        marginMenuTop = loadDimensionPixelSize(
+            resources,
+            R.dimen.desktop_mode_handle_menu_margin_top
+        )
+        marginMenuStart = loadDimensionPixelSize(
+            resources,
+            R.dimen.desktop_mode_handle_menu_margin_start
+        )
+    }
+
+    /**
+     * Determines handle menu height based on if windowing pill should be shown.
+     */
+    private fun getHandleMenuHeight(resources: Resources): Int {
+        var menuHeight = loadDimensionPixelSize(resources, R.dimen.desktop_mode_handle_menu_height)
+        if (!shouldShowWindowingPill) {
+            menuHeight -= loadDimensionPixelSize(
+                resources,
+                R.dimen.desktop_mode_handle_menu_windowing_pill_height
+            )
+        }
+        if (!SHOULD_SHOW_MORE_ACTIONS_PILL) {
+            menuHeight -= loadDimensionPixelSize(
+                resources,
+                R.dimen.desktop_mode_handle_menu_more_actions_pill_height
+            )
+        }
+        if (!shouldShowBrowserPill) {
+            menuHeight -= loadDimensionPixelSize(resources,
+                R.dimen.desktop_mode_handle_menu_open_in_browser_pill_height)
+        }
+        return menuHeight
+    }
+
+    private fun loadDimensionPixelSize(resources: Resources, resourceId: Int): Int {
+        if (resourceId == Resources.ID_NULL) {
+            return 0
+        }
+        return resources.getDimensionPixelSize(resourceId)
+    }
+
+    fun close() {
+        val after = {
+            handleMenuViewContainer?.releaseView()
+            handleMenuViewContainer = null
+        }
+        if (taskInfo.windowingMode == WINDOWING_MODE_FULLSCREEN ||
+            taskInfo.windowingMode == WINDOWING_MODE_MULTI_WINDOW) {
+            handleMenuAnimator?.animateCollapseIntoHandleClose(after)
+        } else {
+            handleMenuAnimator?.animateClose(after)
+        }
+    }
+
+    companion object {
+        private const val TAG = "HandleMenu"
+        private const val SHOULD_SHOW_MORE_ACTIONS_PILL = false
+    }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenuAnimator.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenuAnimator.kt
index 25a829b..e3d2234 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenuAnimator.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenuAnimator.kt
@@ -108,7 +108,7 @@
      *
      * @param after runs after the animation finishes.
      */
-    fun animateCollapseIntoHandleClose(after: Runnable) {
+    fun animateCollapseIntoHandleClose(after: () -> Unit) {
         appInfoCollapseToHandle()
         animateAppInfoPillFadeOut()
         windowingPillClose()
@@ -125,7 +125,7 @@
      * @param after runs after animation finishes.
      *
      */
-    fun animateClose(after: Runnable) {
+    fun animateClose(after: () -> Unit) {
         appInfoPillCollapse()
         animateAppInfoPillFadeOut()
         windowingPillClose()
@@ -463,9 +463,9 @@
      *
      * @param after runs after animation finishes.
      */
-    private fun runAnimations(after: Runnable? = null) {
+    private fun runAnimations(after: (() -> Unit)? = null) {
         runningAnimation?.apply {
-            // Remove all listeners, so that after runnable isn't triggered upon cancel.
+            // Remove all listeners, so that the after function isn't triggered upon cancel.
             removeAllListeners()
             // If an animation runs while running animation is triggered, gracefully cancel.
             cancel()
@@ -475,7 +475,7 @@
             playTogether(animators)
             animators.clear()
             doOnEnd {
-                after?.run()
+                after?.invoke()
                 runningAnimation = null
             }
             start()
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
index e86f6a1..d212f21 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
@@ -107,7 +107,7 @@
      * System-wide context. Only used to create context with overridden configurations.
      */
     final Context mContext;
-    final DisplayController mDisplayController;
+    final @NonNull DisplayController mDisplayController;
     final ShellTaskOrganizer mTaskOrganizer;
     final Supplier<SurfaceControl.Builder> mSurfaceControlBuilderSupplier;
     final Supplier<SurfaceControl.Transaction> mSurfaceControlTransactionSupplier;
@@ -160,7 +160,7 @@
 
     WindowDecoration(
             Context context,
-            DisplayController displayController,
+            @NonNull DisplayController displayController,
             ShellTaskOrganizer taskOrganizer,
             RunningTaskInfo taskInfo,
             @NonNull SurfaceControl taskSurface,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/additionalviewcontainer/AdditionalSystemViewContainer.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/additionalviewcontainer/AdditionalSystemViewContainer.kt
index 6c2c8fd..4897f76 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/additionalviewcontainer/AdditionalSystemViewContainer.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/additionalviewcontainer/AdditionalSystemViewContainer.kt
@@ -18,6 +18,7 @@
 
 import android.content.Context
 import android.graphics.PixelFormat
+import android.view.Gravity
 import android.view.LayoutInflater
 import android.view.SurfaceControl
 import android.view.View
@@ -45,9 +46,11 @@
             WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL,
             WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
             PixelFormat.TRANSPARENT
-        )
-        lp.title = "Additional view container of Task=$taskId"
-        lp.setTrustedOverlay()
+        ).apply {
+            title = "Additional view container of Task=$taskId"
+            gravity = Gravity.LEFT or Gravity.TOP
+            setTrustedOverlay()
+        }
         val wm: WindowManager? = context.getSystemService(WindowManager::class.java)
         wm?.addView(view, lp)
     }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt
index 0c50ab6..e0e603ff 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt
@@ -22,10 +22,10 @@
 import android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW
 import android.graphics.Bitmap
 import android.graphics.Color
+import android.graphics.Point
 import android.graphics.Rect
-import android.platform.test.annotations.RequiresFlagsEnabled
-import android.platform.test.flag.junit.CheckFlagsRule
-import android.platform.test.flag.junit.DeviceFlagsValueProvider
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.SetFlagsRule
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import android.view.Display
@@ -33,6 +33,7 @@
 import android.view.SurfaceControl
 import android.view.SurfaceControlViewHost
 import android.view.View
+import androidx.core.graphics.toPointF
 import androidx.test.filters.SmallTest
 import com.android.window.flags.Flags
 import com.android.wm.shell.R
@@ -46,6 +47,7 @@
 import com.android.wm.shell.splitscreen.SplitScreenController
 import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalSystemViewContainer
 import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalViewHostViewContainer
+import org.junit.Assert.assertEquals
 import org.junit.Assert.assertTrue
 import org.junit.Before
 import org.junit.Rule
@@ -69,7 +71,7 @@
 class HandleMenuTest : ShellTestCase() {
     @JvmField
     @Rule
-    val mCheckFlagsRule: CheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()
+    val setFlagsRule: SetFlagsRule = SetFlagsRule()
 
     @Mock
     private lateinit var mockDesktopWindowDecoration: DesktopModeWindowDecoration
@@ -100,7 +102,7 @@
         ) {
             SurfaceControl.Transaction()
         }
-        val menuView = LayoutInflater.from(context).inflate(
+        val menuView = LayoutInflater.from(mContext).inflate(
             R.layout.desktop_mode_window_decor_handle_menu, null)
         whenever(mockDesktopWindowDecoration.addWindow(
             anyInt(), any(), any(), any(), anyInt(), anyInt(), anyInt(), anyInt())
@@ -110,50 +112,60 @@
         whenever(displayLayout.width()).thenReturn(DISPLAY_BOUNDS.width())
         whenever(displayLayout.height()).thenReturn(DISPLAY_BOUNDS.height())
         whenever(displayLayout.isLandscape).thenReturn(true)
-        mockDesktopWindowDecoration.mDecorWindowContext = context
+        mContext.orCreateTestableResources.apply {
+            addOverride(R.dimen.desktop_mode_handle_menu_width, MENU_WIDTH)
+            addOverride(R.dimen.desktop_mode_handle_menu_height, MENU_HEIGHT)
+            addOverride(R.dimen.desktop_mode_handle_menu_margin_top, MENU_TOP_MARGIN)
+            addOverride(R.dimen.desktop_mode_handle_menu_margin_start, MENU_START_MARGIN)
+        }
+        mockDesktopWindowDecoration.mDecorWindowContext = mContext
     }
 
     @Test
-    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_ADDITIONAL_WINDOWS_ABOVE_STATUS_BAR)
+    @EnableFlags(Flags.FLAG_ENABLE_ADDITIONAL_WINDOWS_ABOVE_STATUS_BAR)
     fun testFullscreenMenuUsesSystemViewContainer() {
         createTaskInfo(WINDOWING_MODE_FULLSCREEN, SPLIT_POSITION_UNDEFINED)
         val handleMenu = createAndShowHandleMenu()
-        assertTrue(handleMenu.mHandleMenuViewContainer is AdditionalSystemViewContainer)
+        assertTrue(handleMenu.handleMenuViewContainer is AdditionalSystemViewContainer)
         // Verify menu is created at coordinates that, when added to WindowManager,
         // show at the top-center of display.
-        assertTrue(handleMenu.mHandleMenuPosition.equals(16f, -512f))
+        val expected = Point(DISPLAY_BOUNDS.centerX() - MENU_WIDTH / 2, MENU_TOP_MARGIN)
+        assertEquals(expected.toPointF(), handleMenu.handleMenuPosition)
     }
 
     @Test
-    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_ADDITIONAL_WINDOWS_ABOVE_STATUS_BAR)
+    @EnableFlags(Flags.FLAG_ENABLE_ADDITIONAL_WINDOWS_ABOVE_STATUS_BAR)
     fun testFreeformMenu_usesViewHostViewContainer() {
         createTaskInfo(WINDOWING_MODE_FREEFORM, SPLIT_POSITION_UNDEFINED)
         handleMenu = createAndShowHandleMenu()
-        assertTrue(handleMenu.mHandleMenuViewContainer is AdditionalViewHostViewContainer)
+        assertTrue(handleMenu.handleMenuViewContainer is AdditionalViewHostViewContainer)
         // Verify menu is created near top-left of task.
-        assertTrue(handleMenu.mHandleMenuPosition.equals(12f, 8f))
+        val expected = Point(MENU_START_MARGIN, MENU_TOP_MARGIN)
+        assertEquals(expected.toPointF(), handleMenu.handleMenuPosition)
     }
 
     @Test
-    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_ADDITIONAL_WINDOWS_ABOVE_STATUS_BAR)
+    @EnableFlags(Flags.FLAG_ENABLE_ADDITIONAL_WINDOWS_ABOVE_STATUS_BAR)
     fun testSplitLeftMenu_usesSystemViewContainer() {
         createTaskInfo(WINDOWING_MODE_MULTI_WINDOW, SPLIT_POSITION_TOP_OR_LEFT)
         handleMenu = createAndShowHandleMenu()
-        assertTrue(handleMenu.mHandleMenuViewContainer is AdditionalSystemViewContainer)
+        assertTrue(handleMenu.handleMenuViewContainer is AdditionalSystemViewContainer)
         // Verify menu is created at coordinates that, when added to WindowManager,
-        // show at the top of split left task.
-        assertTrue(handleMenu.mHandleMenuPosition.equals(-624f, -512f))
+        // show at the top-center of split left task.
+        val expected = Point(SPLIT_LEFT_BOUNDS.centerX() - MENU_WIDTH / 2, MENU_TOP_MARGIN)
+        assertEquals(expected.toPointF(), handleMenu.handleMenuPosition)
     }
 
     @Test
-    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_ADDITIONAL_WINDOWS_ABOVE_STATUS_BAR)
+    @EnableFlags(Flags.FLAG_ENABLE_ADDITIONAL_WINDOWS_ABOVE_STATUS_BAR)
     fun testSplitRightMenu_usesSystemViewContainer() {
         createTaskInfo(WINDOWING_MODE_MULTI_WINDOW, SPLIT_POSITION_BOTTOM_OR_RIGHT)
         handleMenu = createAndShowHandleMenu()
-        assertTrue(handleMenu.mHandleMenuViewContainer is AdditionalSystemViewContainer)
+        assertTrue(handleMenu.handleMenuViewContainer is AdditionalSystemViewContainer)
         // Verify menu is created at coordinates that, when added to WindowManager,
-        // show at the top of split right task.
-        assertTrue(handleMenu.mHandleMenuPosition.equals(656f, -512f))
+        // show at the top-center of split right task.
+        val expected = Point(SPLIT_RIGHT_BOUNDS.centerX() - MENU_WIDTH / 2, MENU_TOP_MARGIN)
+        assertEquals(expected.toPointF(), handleMenu.handleMenuPosition)
     }
 
     private fun createTaskInfo(windowingMode: Int, splitPosition: Int) {
@@ -178,14 +190,10 @@
             .setBounds(bounds)
             .setVisible(true)
             .build()
-        // Calculate captionX similar to how WindowDecoration calculates it.
-        whenever(mockDesktopWindowDecoration.captionX).thenReturn(
-            (mockDesktopWindowDecoration.mTaskInfo.configuration.windowConfiguration
-                .bounds.width() - context.resources.getDimensionPixelSize(
-                R.dimen.desktop_mode_fullscreen_decor_caption_width)) / 2)
         whenever(splitScreenController.getSplitPosition(any())).thenReturn(splitPosition)
         whenever(splitScreenController.getStageBounds(any(), any())).thenAnswer {
             (it.arguments.first() as Rect).set(SPLIT_LEFT_BOUNDS)
+            (it.arguments[1] as Rect).set(SPLIT_RIGHT_BOUNDS)
         }
     }
 
@@ -193,7 +201,7 @@
         val layoutId = if (mockDesktopWindowDecoration.mTaskInfo.isFreeform) {
             R.layout.desktop_mode_app_header
         } else {
-            R.layout.desktop_mode_app_header
+            R.layout.desktop_mode_app_handle
         }
         val handleMenu = HandleMenu(mockDesktopWindowDecoration, layoutId,
                 onClickListener, onTouchListener, appIcon, appName, displayController,
@@ -208,5 +216,9 @@
         private val FREEFORM_BOUNDS = Rect(500, 500, 2000, 1200)
         private val SPLIT_LEFT_BOUNDS = Rect(0, 0, 1280, 1600)
         private val SPLIT_RIGHT_BOUNDS = Rect(1280, 0, 2560, 1600)
+        private const val MENU_WIDTH = 200
+        private const val MENU_HEIGHT = 400
+        private const val MENU_TOP_MARGIN = 10
+        private const val MENU_START_MARGIN = 20
     }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/qs/QSLongPressEffectTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/qs/QSLongPressEffectTest.kt
index 74eee9b..693fcda 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/qs/QSLongPressEffectTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/qs/QSLongPressEffectTest.kt
@@ -17,10 +17,12 @@
 package com.android.systemui.haptics.qs
 
 import android.os.VibrationEffect
+import android.service.quicksettings.Tile
 import android.testing.TestableLooper.RunWithLooper
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.classifier.falsingManager
 import com.android.systemui.haptics.vibratorHelper
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.qs.qsTileFactory
@@ -69,6 +71,7 @@
             QSLongPressEffect(
                 vibratorHelper,
                 kosmos.keyguardStateController,
+                kosmos.falsingManager,
             )
         longPressEffect.callback = callback
         longPressEffect.qsTile = qsTile
@@ -175,17 +178,17 @@
         }
 
     @Test
-    fun onAnimationComplete_keyguardDismissible_effectCompletes() =
+    fun onAnimationComplete_keyguardDismissible_effectEndsInLongClicked() =
         testWhileInState(QSLongPressEffect.State.RUNNING_FORWARD) {
             // GIVEN that the animation completes
             longPressEffect.handleAnimationComplete()
 
-            // THEN the long-press effect completes
-            assertEffectCompleted()
+            // THEN the long-press effect completes with a long-click state
+            assertEffectCompleted(QSLongPressEffect.State.LONG_CLICKED)
         }
 
     @Test
-    fun onAnimationComplete_keyguardNotDismissible_effectEndsWithReset() =
+    fun onAnimationComplete_keyguardNotDismissible_effectEndsInIdleWithReset() =
         testWhileInState(QSLongPressEffect.State.RUNNING_FORWARD) {
             // GIVEN that the keyguard is not dismissible
             whenever(kosmos.keyguardStateController.isUnlocked).thenReturn(false)
@@ -193,19 +196,20 @@
             // GIVEN that the animation completes
             longPressEffect.handleAnimationComplete()
 
-            // THEN the long-press effect completes and the properties are called to reset
-            assertEffectCompleted()
+            // THEN the long-press effect ends in the idle state and the properties are reset
+            assertEffectCompleted(QSLongPressEffect.State.IDLE)
             verify(callback, times(1)).onResetProperties()
         }
 
     @Test
-    fun onAnimationComplete_whenRunningBackwardsFromUp_endsWithFinishedReversing() =
+    fun onAnimationComplete_whenRunningBackwardsFromUp_endsWithFinishedReversingAndClick() =
         testWhileInState(QSLongPressEffect.State.RUNNING_BACKWARDS_FROM_UP) {
             // GIVEN that the animation completes
             longPressEffect.handleAnimationComplete()
 
-            // THEN the callback for finished reversing is used.
+            // THEN the callback for finished reversing is used and the effect ends with a click.
             verify(callback, times(1)).onEffectFinishedReversing()
+            assertThat(longPressEffect.state).isEqualTo(QSLongPressEffect.State.CLICKED)
         }
 
     @Test
@@ -262,18 +266,88 @@
         }
 
     @Test
-    fun onTileClick_whileWaiting_withoutQSTile_cannotClick() =
-        testWhileInState(QSLongPressEffect.State.TIMEOUT_WAIT) {
-            // GIVEN that no QSTile has been set
-            longPressEffect.qsTile = null
-
+    fun onTileClick_whileIdle_withQSTile_clicks() =
+        testWhileInState(QSLongPressEffect.State.IDLE) {
             // GIVEN that a click was detected
             val couldClick = longPressEffect.onTileClick()
 
+            // THEN the click is successful
+            assertThat(couldClick).isTrue()
+        }
+
+    @Test
+    fun onTileClick_whenBouncerIsShowing_ignoresClick() =
+        testWhileInState(QSLongPressEffect.State.IDLE) {
+            // GIVEN that the bouncer is showing
+            whenever(kosmos.keyguardStateController.isPrimaryBouncerShowing).thenReturn(true)
+
+            // WHEN a click is detected by the tile view
+            val couldClick = longPressEffect.onTileClick()
+
             // THEN the click is not successful
             assertThat(couldClick).isFalse()
         }
 
+    @Test
+    fun getStateForClick_withUnavailableTile_returnsIdle() {
+        // GIVEN an unavailable tile
+        qsTile.state?.state = Tile.STATE_UNAVAILABLE
+
+        // WHEN determining the state of a click action
+        val clickState = longPressEffect.getStateForClick()
+
+        // THEN the state is IDLE
+        assertThat(clickState).isEqualTo(QSLongPressEffect.State.IDLE)
+    }
+
+    @Test
+    fun getStateForClick_withFalseTapWhenLocked_returnsIdle() {
+        // GIVEN an active tile
+        qsTile.state?.state = Tile.STATE_ACTIVE
+
+        // GIVEN that the device is locked and a false tap is detected
+        whenever(kosmos.keyguardStateController.isUnlocked).thenReturn(false)
+        kosmos.falsingManager.setFalseTap(true)
+
+        // WHEN determining the state of a click action
+        val clickState = longPressEffect.getStateForClick()
+
+        // THEN the state is IDLE
+        assertThat(clickState).isEqualTo(QSLongPressEffect.State.IDLE)
+    }
+
+    @Test
+    fun getStateForClick_withValidTapAndTile_returnsClicked() {
+        // GIVEN an active tile
+        qsTile.state?.state = Tile.STATE_ACTIVE
+
+        // GIVEN that the device is locked and a false tap is not detected
+        whenever(kosmos.keyguardStateController.isUnlocked).thenReturn(false)
+        kosmos.falsingManager.setFalseTap(false)
+
+        // WHEN determining the state of a click action
+        val clickState = longPressEffect.getStateForClick()
+
+        // THEN the state is CLICKED
+        assertThat(clickState).isEqualTo(QSLongPressEffect.State.CLICKED)
+    }
+
+    @Test
+    fun getStateForClick_withNullTile_returnsIdle() {
+        // GIVEN that the tile is null
+        longPressEffect.qsTile = null
+
+        // GIVEN that the device is locked and a false tap is not detected
+        whenever(kosmos.keyguardStateController.isUnlocked).thenReturn(false)
+        kosmos.falsingManager.setFalseTap(false)
+
+        // WHEN determining the state of a click action
+        val clickState = longPressEffect.getStateForClick()
+
+        // THEN the state is IDLE
+        assertThat(clickState).isEqualTo(QSLongPressEffect.State.IDLE)
+    }
+
     private fun testWithScope(initialize: Boolean = true, test: suspend TestScope.() -> Unit) =
         with(kosmos) {
             testScope.runTest {
@@ -339,14 +413,14 @@
     /**
      * Asserts that the effect completes by checking that:
      * 1. The final snap haptics are played
-     * 2. The internal state goes back to [QSLongPressEffect.State.IDLE]
+     * 2. The internal state goes back to specified end state.
      */
-    private fun assertEffectCompleted() {
+    private fun assertEffectCompleted(endState: QSLongPressEffect.State) {
         val snapEffect = LongPressHapticBuilder.createSnapEffect()
 
         assertThat(snapEffect).isNotNull()
         assertThat(vibratorHelper.hasVibratedWithEffects(snapEffect!!)).isTrue()
-        assertThat(longPressEffect.state).isEqualTo(QSLongPressEffect.State.IDLE)
+        assertThat(longPressEffect.state).isEqualTo(endState)
     }
 
     /**
diff --git a/packages/SystemUI/res/layout/app_clips_screenshot.xml b/packages/SystemUI/res/layout/app_clips_screenshot.xml
index b09d35d..5191895 100644
--- a/packages/SystemUI/res/layout/app_clips_screenshot.xml
+++ b/packages/SystemUI/res/layout/app_clips_screenshot.xml
@@ -60,6 +60,7 @@
         android:layout_marginStart="16dp"
         android:checked="true"
         android:text="@string/backlinks_include_link"
+        android:textColor="?android:textColorSecondary"
         android:visibility="gone"
         app:layout_constraintBottom_toTopOf="@id/preview"
         app:layout_constraintStart_toEndOf="@id/cancel"
@@ -74,6 +75,7 @@
         android:drawablePadding="4dp"
         android:gravity="center"
         android:paddingHorizontal="8dp"
+        android:textColor="?android:textColorSecondary"
         android:visibility="gone"
         app:layout_constraintBottom_toTopOf="@id/preview"
         app:layout_constraintStart_toEndOf="@id/backlinks_include_data"
diff --git a/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffect.kt b/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffect.kt
index 7b5139a..c44eb47 100644
--- a/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffect.kt
+++ b/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffect.kt
@@ -17,8 +17,10 @@
 package com.android.systemui.haptics.qs
 
 import android.os.VibrationEffect
+import android.service.quicksettings.Tile
 import androidx.annotation.VisibleForTesting
 import com.android.systemui.animation.Expandable
+import com.android.systemui.plugins.FalsingManager
 import com.android.systemui.plugins.qs.QSTile
 import com.android.systemui.statusbar.VibratorHelper
 import com.android.systemui.statusbar.policy.KeyguardStateController
@@ -40,6 +42,7 @@
 constructor(
     private val vibratorHelper: VibratorHelper?,
     private val keyguardStateController: KeyguardStateController,
+    private val falsingManager: FalsingManager,
 ) {
 
     var effectDuration = 0
@@ -130,18 +133,18 @@
     fun handleAnimationComplete() {
         when (state) {
             State.RUNNING_FORWARD -> {
-                setState(State.IDLE)
                 vibrate(snapEffect)
                 if (keyguardStateController.isUnlocked) {
-                    qsTile?.longClick(expandable)
+                    setState(State.LONG_CLICKED)
                 } else {
                     callback?.onResetProperties()
-                    qsTile?.longClick(expandable)
+                    setState(State.IDLE)
                 }
+                qsTile?.longClick(expandable)
             }
             State.RUNNING_BACKWARDS_FROM_UP -> {
-                setState(State.IDLE)
                 callback?.onEffectFinishedReversing()
+                setState(getStateForClick())
                 qsTile?.click(expandable)
             }
             State.RUNNING_BACKWARDS_FROM_CANCEL -> setState(State.IDLE)
@@ -160,14 +163,37 @@
     }
 
     fun onTileClick(): Boolean {
-        if (state == State.TIMEOUT_WAIT) {
-            setState(State.IDLE)
-            qsTile?.let {
-                it.click(expandable)
-                return true
-            }
+        val isStateClickable = state == State.TIMEOUT_WAIT || state == State.IDLE
+
+        // Ignore View-generated clicks on invalid states or if the bouncer is showing
+        if (keyguardStateController.isPrimaryBouncerShowing || !isStateClickable) return false
+
+        setState(getStateForClick())
+        qsTile?.click(expandable)
+        return true
+    }
+
+    /**
+     * Get the appropriate state for a click action.
+     *
+     * In some occasions, the click action will not result in a subsequent action that resets the
+     * state upon completion (e.g., a launch transition animation). In these cases, the state needs
+     * to be reset before the click is dispatched.
+     */
+    @VisibleForTesting
+    fun getStateForClick(): State {
+        val isTileUnavailable = qsTile?.state?.state == Tile.STATE_UNAVAILABLE
+        val isFalseTapWhileLocked =
+            !keyguardStateController.isUnlocked &&
+                falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)
+        val handlesLongClick = qsTile?.state?.handlesLongClick == true
+        return if (isTileUnavailable || isFalseTapWhileLocked || !handlesLongClick) {
+            // The click event will not perform an action that resets the state. Therefore, this is
+            // the last opportunity to reset the state back to IDLE.
+            State.IDLE
+        } else {
+            State.CLICKED
         }
-        return false
     }
 
     /**
@@ -194,6 +220,8 @@
         return true
     }
 
+    fun resetState() = setState(State.IDLE)
+
     enum class State {
         IDLE, /* The effect is idle waiting for touch input */
         TIMEOUT_WAIT, /* The effect is waiting for a tap timeout period */
@@ -202,6 +230,8 @@
         RUNNING_BACKWARDS_FROM_UP,
         /* The effect was interrupted by an ACTION_CANCEL and is now running backwards */
         RUNNING_BACKWARDS_FROM_CANCEL,
+        CLICKED, /* The effect has ended with a click */
+        LONG_CLICKED, /* The effect has ended with a long-click */
     }
 
     /** Callbacks to notify view and animator actions */
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
index 44c846b..787fd1a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
@@ -386,6 +386,7 @@
             // The launch animation of a long-press effect did not reset the long-press effect so
             // we must do it here
             resetLongPressEffectProperties()
+            longPressEffect.resetState()
         }
         val actualHeight =
             if (heightOverride != HeightOverrideable.NO_OVERRIDE) {
@@ -771,11 +772,14 @@
         lastIconTint = icon.getColor(state)
 
         // Long-press effects
+        longPressEffect?.qsTile?.state?.handlesLongClick = state.handlesLongClick
         if (
             state.handlesLongClick &&
                 longPressEffect?.initializeEffect(longPressEffectDuration) == true
         ) {
             showRippleEffect = false
+            longPressEffect.qsTile?.state?.state = lastState // Store the tile's state
+            longPressEffect.resetState()
             initializeLongPressProperties(measuredHeight, measuredWidth)
         } else {
             // Long-press effects might have been enabled before but the new state does not
@@ -906,6 +910,7 @@
     }
 
     override fun onActivityLaunchAnimationEnd() {
+        longPressEffect?.resetState()
         if (longPressEffect != null && !haveLongPressPropertiesBeenReset) {
             resetLongPressEffectProperties()
         }
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsViewModel.java b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsViewModel.java
index 8c833ec..bd9e295 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsViewModel.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsViewModel.java
@@ -33,6 +33,7 @@
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
 import android.graphics.Bitmap;
 import android.graphics.HardwareRenderer;
 import android.graphics.RecordingCanvas;
@@ -267,6 +268,8 @@
     }
 
     private boolean canAppStartThroughLauncher(String packageName) {
+        // Use Intent.resolveActivity API to check if the intent resolves as that is what Android
+        // uses internally when apps use Context.startActivity.
         return getMainLauncherIntentForPackage(packageName).resolveActivity(mPackageManager)
                 != null;
     }
@@ -366,10 +369,19 @@
         return taskInfo.topActivityInfo.loadLabel(mPackageManager).toString();
     }
 
-    private Intent getMainLauncherIntentForPackage(String packageName) {
-        return new Intent(ACTION_MAIN)
-                .addCategory(CATEGORY_LAUNCHER)
-                .setPackage(packageName);
+    private Intent getMainLauncherIntentForPackage(String pkgName) {
+        Intent intent = new Intent(ACTION_MAIN).addCategory(CATEGORY_LAUNCHER).setPackage(pkgName);
+
+        // Not all apps use DEFAULT_CATEGORY for their main launcher activity so the exact component
+        // needs to be queried and set on the Intent in order for note-taking apps to be able to
+        // start this intent. When starting an activity with an implicit intent, Android adds the
+        // DEFAULT_CATEGORY flag otherwise it fails to resolve the intent.
+        ResolveInfo resolvedActivity = mPackageManager.resolveActivity(intent, /* flags= */ 0);
+        if (resolvedActivity != null) {
+            intent.setComponent(resolvedActivity.getComponentInfo().getComponentName());
+        }
+
+        return intent;
     }
 
     /** Helper factory to help with injecting {@link AppClipsViewModel}. */
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsViewModelTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsViewModelTest.java
index baf1357..193d29c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsViewModelTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsViewModelTest.java
@@ -334,13 +334,17 @@
     }
 
     private void resetPackageManagerMockingForUsingFallbackBacklinks() {
+        ResolveInfo backlinksTaskResolveInfo = createBacklinksTaskResolveInfo();
         reset(mPackageManager);
         when(mPackageManager.loadItemIcon(any(), any())).thenReturn(FAKE_DRAWABLE);
         when(mPackageManager.resolveActivity(any(Intent.class), anyInt()))
-                // First the logic queries whether a package has a launcher activity, this should
+                // Firstly, the logic queries whether a package has a launcher activity, this should
                 // resolve otherwise the logic filters out the task.
-                .thenReturn(createBacklinksTaskResolveInfo())
-                // Then logic queries with the backlinks intent, this should not resolve for the
+                .thenReturn(backlinksTaskResolveInfo)
+                // Secondly, the logic builds a fallback main launcher intent, this should also
+                // resolve for the fallback intent to build correctly.
+                .thenReturn(backlinksTaskResolveInfo)
+                // Lastly, logic queries with the backlinks intent, this should not resolve for the
                 // logic to use the fallback intent.
                 .thenReturn(null);
     }
@@ -360,6 +364,8 @@
         assertThat(actualBacklinksIntent.getPackage()).isEqualTo(BACKLINKS_TASK_PACKAGE_NAME);
         assertThat(actualBacklinksIntent.getAction()).isEqualTo(ACTION_MAIN);
         assertThat(actualBacklinksIntent.getCategories()).containsExactly(CATEGORY_LAUNCHER);
+        assertThat(actualBacklinksIntent.getComponent()).isEqualTo(
+                new ComponentName(BACKLINKS_TASK_PACKAGE_NAME, BACKLINKS_TASK_APP_NAME));
     }
 
     private static ResolveInfo createBacklinksTaskResolveInfo() {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/haptics/qs/QSLongPressEffectKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/haptics/qs/QSLongPressEffectKosmos.kt
index eff99e04..28355e1 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/haptics/qs/QSLongPressEffectKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/haptics/qs/QSLongPressEffectKosmos.kt
@@ -16,9 +16,10 @@
 
 package com.android.systemui.haptics.qs
 
+import com.android.systemui.classifier.falsingManager
 import com.android.systemui.haptics.vibratorHelper
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.statusbar.policy.keyguardStateController
 
 val Kosmos.qsLongPressEffect by
-    Kosmos.Fixture { QSLongPressEffect(vibratorHelper, keyguardStateController) }
+    Kosmos.Fixture { QSLongPressEffect(vibratorHelper, keyguardStateController, falsingManager) }
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java b/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java
index 88da6fb..550f68f 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java
@@ -660,7 +660,11 @@
                     .setPortId(physicalAddressToPortId(physicalAddress))
                     .setDeviceType(type)
                     .build();
-            updateCecDevice(updatedDeviceInfo);
+            if (deviceInfo.getPhysicalAddress() != physicalAddress) {
+                addCecDevice(updatedDeviceInfo);
+            } else {
+                updateCecDevice(updatedDeviceInfo);
+            }
         }
     }
 
diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index f85b8cc..aa5f5a24 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -206,7 +206,6 @@
         mPolicy = policy;
         mFaceDownDetector = faceDownDetector;
         mScreenUndimDetector = screenUndimDetector;
-        mWakefulnessSessionObserver = new WakefulnessSessionObserver(mContext, null);
         mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
         mInputManagerInternal = LocalServices.getService(InputManagerInternal.class);
         mInputMethodManagerInternal = LocalServices.getService(InputMethodManagerInternal.class);
@@ -214,6 +213,7 @@
         mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
         mTrustManager = mContext.getSystemService(TrustManager.class);
         mVibrator = mContext.getSystemService(Vibrator.class);
+        mWakefulnessSessionObserver = new WakefulnessSessionObserver(mContext, null);
 
         mHandler = new NotifierHandler(looper);
         mBackgroundExecutor = backgroundExecutor;
@@ -813,6 +813,8 @@
         if (DEBUG) {
             Slog.d(TAG, "onScreenPolicyUpdate: newPolicy=" + newPolicy);
         }
+        mWakefulnessSessionObserver.onScreenPolicyUpdate(
+                SystemClock.uptimeMillis(), displayGroupId, newPolicy);
 
         synchronized (mLock) {
             Message msg = mHandler.obtainMessage(MSG_SCREEN_POLICY);
diff --git a/services/core/java/com/android/server/power/WakefulnessSessionObserver.java b/services/core/java/com/android/server/power/WakefulnessSessionObserver.java
index d57cd5d..3546565 100644
--- a/services/core/java/com/android/server/power/WakefulnessSessionObserver.java
+++ b/services/core/java/com/android/server/power/WakefulnessSessionObserver.java
@@ -16,8 +16,11 @@
 
 package com.android.server.power;
 
+import static android.hardware.display.DisplayManagerInternal.DisplayPowerRequest.POLICY_BRIGHT;
+import static android.hardware.display.DisplayManagerInternal.DisplayPowerRequest.POLICY_DIM;
 import static android.os.PowerManager.USER_ACTIVITY_EVENT_OTHER;
 import static android.os.PowerManagerInternal.isInteractive;
+import static android.view.Display.DEFAULT_DISPLAY;
 
 import static com.android.server.power.PowerManagerService.DEFAULT_SCREEN_OFF_TIMEOUT;
 import static com.android.server.power.ScreenTimeoutOverridePolicy.RELEASE_REASON_NON_INTERACTIVE;
@@ -34,6 +37,9 @@
 import android.app.SynchronousUserSwitchObserver;
 import android.content.Context;
 import android.database.ContentObserver;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.DisplayManagerInternal;
+import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
 import android.os.Handler;
 import android.os.PowerManager;
 import android.os.PowerManagerInternal;
@@ -44,9 +50,13 @@
 import android.util.IndentingPrintWriter;
 import android.util.SparseArray;
 import android.view.Display;
+import android.view.DisplayAddress;
+import android.view.DisplayInfo;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.BackgroundThread;
 import com.android.internal.util.FrameworkStatsLog;
+import com.android.server.LocalServices;
 
 import java.io.PrintWriter;
 import java.lang.annotation.Retention;
@@ -117,9 +127,42 @@
     @Retention(RetentionPolicy.SOURCE)
     private @interface OverrideOutcome {}
 
-    private static final int DEFAULT_USER_ACTIVITY = USER_ACTIVITY_EVENT_OTHER;
-    private static final long TIMEOUT_USER_INITIATED_REVERT_THRESHOLD_MILLIS = 5000L;
+    private static final int POLICY_REASON_UNKNOWN = FrameworkStatsLog
+            .SCREEN_DIM_REPORTED__POLICY_REASON__UNKNOWN;
+    @VisibleForTesting
+    protected static final int POLICY_REASON_OFF_TIMEOUT = FrameworkStatsLog
+            .SCREEN_DIM_REPORTED__POLICY_REASON__OFF_TIMEOUT;
+    @VisibleForTesting
+    protected static final int POLICY_REASON_OFF_POWER_BUTTON = FrameworkStatsLog
+            .SCREEN_DIM_REPORTED__POLICY_REASON__OFF_POWER_BUTTON;
+    @VisibleForTesting
+    protected static final int POLICY_REASON_BRIGHT_UNDIM = FrameworkStatsLog
+            .SCREEN_DIM_REPORTED__POLICY_REASON__BRIGHT_UNDIM;
+    @VisibleForTesting
+    protected static final int POLICY_REASON_BRIGHT_INITIATED_REVERT = FrameworkStatsLog
+            .SCREEN_DIM_REPORTED__POLICY_REASON__BRIGHT_INITIATED_REVERT;
+
+    /**
+     * Policy Reason
+     * {@link android.os.statsd.power.ScreenDimReported.PolicyReason}.
+     */
+    @IntDef(prefix = {"POLICY_REASON_"}, value = {
+            POLICY_REASON_UNKNOWN,
+            POLICY_REASON_OFF_TIMEOUT,
+            POLICY_REASON_OFF_POWER_BUTTON,
+            POLICY_REASON_BRIGHT_UNDIM,
+            POLICY_REASON_BRIGHT_INITIATED_REVERT
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    private @interface PolicyReason {}
+
+    @VisibleForTesting protected static final int DEFAULT_USER_ACTIVITY = USER_ACTIVITY_EVENT_OTHER;
+    private static final long USER_INITIATED_REVERT_THRESHOLD_MILLIS = 5000L;
     private static final long SEND_OVERRIDE_TIMEOUT_LOG_THRESHOLD_MILLIS = 1000L;
+    @VisibleForTesting
+    protected static final long SCREEN_POLICY_DIM_POWER_OFF_BRIGHT_THRESHOLD_MILLIS = 500L;
+
+    @VisibleForTesting protected static final Object HANDLER_TOKEN = new Object();
 
     private Context mContext;
     private int mScreenOffTimeoutMs;
@@ -130,17 +173,24 @@
     protected WakefulnessSessionFrameworkStatsLogger mWakefulnessSessionFrameworkStatsLogger;
     private final Clock mClock;
     private final Object mLock = new Object();
+    private final Handler mHandler;
 
-    public WakefulnessSessionObserver(Context context, Injector injector) {
+    private DisplayManagerInternal mDisplayManagerInternal;
+    private int mPhysicalDisplayPortIdForDefaultDisplay;
+
+    public WakefulnessSessionObserver(
+            Context context, Injector injector) {
         if (injector == null) {
             injector = new Injector();
         }
 
         mContext = context;
+        mDisplayManagerInternal = injector.getDisplayManagerInternal();
         mWakefulnessSessionFrameworkStatsLogger = injector
                 .getWakefulnessSessionFrameworkStatsLogger();
         mClock = injector.getClock();
-        updateSettingScreenOffTimeout(context);
+        mHandler = injector.getHandler();
+        updateSettingScreenOffTimeout(mContext);
 
         try {
             final UserSwitchObserver observer = new UserSwitchObserver();
@@ -164,6 +214,31 @@
                         },
                         UserHandle.USER_ALL);
 
+        mPhysicalDisplayPortIdForDefaultDisplay = getPhysicalDisplayPortId(DEFAULT_DISPLAY);
+        DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
+        if (displayManager != null) {
+            displayManager.registerDisplayListener(
+                    new DisplayManager.DisplayListener() {
+                        @Override
+                        public void onDisplayChanged(int displayId) {
+                            if (displayId == DEFAULT_DISPLAY) {
+                                mPhysicalDisplayPortIdForDefaultDisplay = getPhysicalDisplayPortId(
+                                        DEFAULT_DISPLAY);
+                            }
+                        }
+
+                        @Override
+                        public void onDisplayAdded(int i) {
+                        }
+
+                        @Override
+                        public void onDisplayRemoved(int i) {
+                        }
+                    },
+                    mHandler,
+                    DisplayManager.EVENT_FLAG_DISPLAY_CHANGED);
+        }
+
         mPowerGroups.append(
                 Display.DEFAULT_DISPLAY_GROUP,
                 new WakefulnessSessionPowerGroup(Display.DEFAULT_DISPLAY_GROUP));
@@ -186,6 +261,20 @@
     }
 
     /**
+     * Track the screen policy
+     *
+     * @param eventTime policy changing time, in uptime millis.
+     * @param powerGroupId Power Group Id for this screen policy
+     * @param newPolicy Screen Policy defined in {@link DisplayPowerRequest}
+     */
+    public void onScreenPolicyUpdate(long eventTime, int powerGroupId, int newPolicy) {
+        if (!mPowerGroups.contains(powerGroupId)) {
+            mPowerGroups.append(powerGroupId, new WakefulnessSessionPowerGroup(powerGroupId));
+        }
+        mPowerGroups.get(powerGroupId).onScreenPolicyUpdate(eventTime, newPolicy);
+    }
+
+    /**
      * Track the system wakefulness
      *
      * @param powerGroupId Power Group Id for this wakefulness changes
@@ -267,6 +356,14 @@
         }
     }
 
+    private int getPhysicalDisplayPortId(int displayId) {
+        if (mDisplayManagerInternal == null) {
+            return -1;
+        }
+        DisplayInfo display = mDisplayManagerInternal.getDisplayInfo(displayId);
+        return ((DisplayAddress.Physical) display.address).getPort();
+    }
+
     private int getScreenOffTimeout() {
         synchronized (mLock) {
             return mScreenOffTimeoutMs;
@@ -277,10 +374,9 @@
     @VisibleForTesting
     protected class WakefulnessSessionPowerGroup {
         private static final long TIMEOUT_OFF_RESET_TIMESTAMP = -1;
-
         private int mPowerGroupId;
         private int mCurrentWakefulness;
-        private boolean mIsInteractive = false;
+        @VisibleForTesting protected boolean mIsInteractive = false;
         // state on start timestamp: will be used in state off to calculate the duration of state on
         private long mInteractiveStateOnStartTimestamp;
         @VisibleForTesting
@@ -295,6 +391,17 @@
         private int mTimeoutOverrideWakeLockCounter = 0;
         // The timestamp when Override Timeout is set to false
         private @ScreenTimeoutOverridePolicy.ReleaseReason int mTimeoutOverrideReleaseReason;
+        // The timestamp when current screen policy is set
+        private long mCurrentScreenPolicyTimestamp;
+        // current screen policy
+        private int mCurrentScreenPolicy;
+        // The screen policy before the current one
+        private int mPrevScreenPolicy;
+        // The previous screen policy duration
+        private int mPrevScreenPolicyDurationMs;
+        // The past dim duration
+        @VisibleForTesting protected int mPastDimDurationMs;
+        private long mInteractiveOffTimestamp;
         // The timestamp when state off by timeout occurs
         // will set TIMEOUT_OFF_RESET_TIMESTAMP if state on or state off by power button
         private long mTimeoutOffTimestamp;
@@ -307,6 +414,10 @@
             mPrevUserActivityEvent = DEFAULT_USER_ACTIVITY;
             mPrevUserActivityTimestamp = -1;
             mPowerGroupId = powerGroupId;
+            mCurrentScreenPolicy = mPrevScreenPolicy = POLICY_BRIGHT;
+            mCurrentScreenPolicyTimestamp = 0;
+            mPrevScreenPolicyDurationMs = 0;
+            mPastDimDurationMs = 0;
         }
 
         public void notifyUserActivity(long eventTime, @PowerManager.UserActivityEvent int event) {
@@ -320,6 +431,21 @@
             mCurrentUserActivityTimestamp = eventTime;
         }
 
+        public void onScreenPolicyUpdate(long eventTime, int newPolicy) {
+            if (newPolicy == mCurrentScreenPolicy) {
+                return;
+            }
+
+            if (newPolicy == POLICY_BRIGHT) {
+                checkAndLogDimIfQualified(POLICY_REASON_BRIGHT_UNDIM, eventTime);
+            }
+
+            mPrevScreenPolicy = mCurrentScreenPolicy;
+            mCurrentScreenPolicy = newPolicy;
+            mPrevScreenPolicyDurationMs = (int) (eventTime - mCurrentScreenPolicyTimestamp);
+            mCurrentScreenPolicyTimestamp = eventTime;
+        }
+
         public void onWakefulnessChangeStarted(int wakefulness, int changeReason, long eventTime) {
             mCurrentWakefulness = wakefulness;
             if (mIsInteractive == isInteractive(wakefulness)) {
@@ -331,10 +457,10 @@
                 mInteractiveStateOnStartTimestamp = eventTime;
 
                 // Log the outcome of screen timeout override (USER INITIATED REVERT),
-                // when user initiates to revert the screen off state in a short period.
+                // when user initiates to revert the off state in a short period.
                 if (mTimeoutOffTimestamp != TIMEOUT_OFF_RESET_TIMESTAMP) {
-                    long offToOnDurationMs = eventTime - mTimeoutOffTimestamp;
-                    if (offToOnDurationMs < TIMEOUT_USER_INITIATED_REVERT_THRESHOLD_MILLIS) {
+                    long timeoutOffToOnDurationMs = eventTime - mTimeoutOffTimestamp;
+                    if (timeoutOffToOnDurationMs < USER_INITIATED_REVERT_THRESHOLD_MILLIS) {
                         mWakefulnessSessionFrameworkStatsLogger.logTimeoutOverrideEvent(
                                 mPowerGroupId,
                                 OVERRIDE_OUTCOME_TIMEOUT_USER_INITIATED_REVERT,
@@ -344,11 +470,15 @@
                     }
                     mTimeoutOffTimestamp = TIMEOUT_OFF_RESET_TIMESTAMP;
                 }
+
+                checkAndLogDimIfQualified(POLICY_REASON_BRIGHT_INITIATED_REVERT, eventTime);
+
             } else {
                 int lastUserActivity = mCurrentUserActivityEvent;
                 long lastUserActivityDurationMs = eventTime - mCurrentUserActivityTimestamp;
                 @OffReason int interactiveStateOffReason = OFF_REASON_UNKNOWN;
                 int reducedInteractiveStateOnDurationMs = 0;
+                mInteractiveOffTimestamp = eventTime;
 
                 if (changeReason == PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON) {
                     interactiveStateOffReason = OFF_REASON_POWER_BUTTON;
@@ -369,6 +499,9 @@
                         mSendOverrideTimeoutLogTimestamp = eventTime;
                         mTimeoutOverrideReleaseReason = RELEASE_REASON_UNKNOWN; // reset the reason
                     }
+
+                    checkAndLogDimIfQualified(POLICY_REASON_OFF_POWER_BUTTON, eventTime);
+
                 } else if (changeReason == PowerManager.GO_TO_SLEEP_REASON_TIMEOUT) {
                     // Interactive Off reason is timeout
                     interactiveStateOffReason = OFF_REASON_TIMEOUT;
@@ -393,6 +526,8 @@
                         // state instantly
                         mTimeoutOffTimestamp = eventTime;
                     }
+
+                    checkAndLogDimIfQualified(POLICY_REASON_OFF_TIMEOUT, eventTime);
                 }
 
                 long interactiveStateOnDurationMs =
@@ -462,6 +597,106 @@
             }
         }
 
+        private void checkAndLogDimIfQualified(
+                @PolicyReason int reasonToBeChecked, long eventTime) {
+            // Only log dim event when DEFAULT_DISPLAY
+            if (mPowerGroupId != DEFAULT_DISPLAY) {
+                return;
+            }
+
+            int dimDurationMs = 0;
+            int lastUserActivity = mCurrentUserActivityEvent;
+            int lastUserActivityDurationMs = (int) (eventTime - mCurrentUserActivityTimestamp);
+            switch (reasonToBeChecked) {
+                case POLICY_REASON_OFF_TIMEOUT: {
+                    // The policy ordering:
+                    // (1) --DIM--OFF/DOZE->| or (2) --DIM->| because OFF/DOZE hasn't been updated.
+                    dimDurationMs = (int) (eventTime - mCurrentScreenPolicyTimestamp); //(1)--DIM->|
+                    if (mPrevScreenPolicy == POLICY_DIM) {  // for (2) --DIM--OFF/DOZE->|
+                        dimDurationMs = mPrevScreenPolicyDurationMs;
+                    }
+                    mWakefulnessSessionFrameworkStatsLogger.logDimEvent(
+                            mPhysicalDisplayPortIdForDefaultDisplay,
+                            reasonToBeChecked,
+                            lastUserActivity,
+                            lastUserActivityDurationMs,
+                            dimDurationMs,
+                            mScreenOffTimeoutMs);
+                    mPastDimDurationMs = dimDurationMs;
+                    return;
+                }
+                case POLICY_REASON_OFF_POWER_BUTTON: {
+                    // Power Off will be triggered by USER_ACTIVITY_EVENT_BUTTON
+                    // The metric wants to record the previous activity before EVENT_BUTTON
+                    lastUserActivity = mPrevUserActivityEvent;
+                    lastUserActivityDurationMs = (int) (eventTime - mPrevUserActivityTimestamp);
+                    // the policy ordering:
+                    // (1) ---BRIGHT->| or (2) ---DIM->| because OFF/DOZE hasn't been updated
+                    dimDurationMs = 0; // for (1) ---BRIGHT->| which doesn't have dim (no need log)
+                    if (mCurrentScreenPolicy == POLICY_DIM) { // for (2) ---DIM->|
+                        dimDurationMs = (int) (eventTime - mCurrentScreenPolicyTimestamp);
+                        mWakefulnessSessionFrameworkStatsLogger.logDimEvent(
+                                mPhysicalDisplayPortIdForDefaultDisplay,
+                                reasonToBeChecked,
+                                lastUserActivity,
+                                lastUserActivityDurationMs,
+                                dimDurationMs,
+                                mScreenOffTimeoutMs);
+                        mHandler.removeCallbacksAndMessages(HANDLER_TOKEN);
+                    }
+
+                    mPastDimDurationMs = dimDurationMs;
+                    return;
+                }
+                case POLICY_REASON_BRIGHT_UNDIM: {
+                    // Has checked the latest screen policy is POLICY_BRIGHT in onScreenPolicyUpdate
+                    if (mCurrentScreenPolicy == POLICY_DIM) { // policy ordering: --DIM--BRIGHT->|
+                        int savedDimDurationMs = (int) (eventTime - mCurrentScreenPolicyTimestamp);
+                        int savedLastUserActivity = lastUserActivity;
+                        int savedLastUserActivityDurationMs = lastUserActivityDurationMs;
+
+                        // For the undim case --DIM--BRIGHT->|, it needs wait 500 ms to
+                        // differentiate between "power button off" case, which is
+                        // --DIM--BRIGHT(<500ms)--OFF/DOZE->|
+                        // [Method] Wait 500 ms to see whether triggers power button off or not.
+                        // [Reason] We got --DIM--BRIGHT->|. However, if BRIGHT is so short (<500ms)
+                        //          and follows OFF/DOZE, it represents power button off, not undim.
+                        //          It is normal to have a short BRIGHT for power button off because
+                        //          the system need to play an animation before off.
+                        mHandler.postDelayed(() -> {
+                            mWakefulnessSessionFrameworkStatsLogger.logDimEvent(
+                                    mPhysicalDisplayPortIdForDefaultDisplay,
+                                    reasonToBeChecked,
+                                    savedLastUserActivity,
+                                    savedLastUserActivityDurationMs,
+                                    savedDimDurationMs,
+                                    mScreenOffTimeoutMs);
+                            mPastDimDurationMs = savedDimDurationMs;
+                        }, HANDLER_TOKEN, SCREEN_POLICY_DIM_POWER_OFF_BRIGHT_THRESHOLD_MILLIS);
+                    }
+                    return;
+                }
+                case POLICY_REASON_BRIGHT_INITIATED_REVERT: {
+                    // the dimDuration in BRIGHT_INITIATE_REVERT is for the dim duration before
+                    // screen interactive off (mPastDimDurationMs)
+                    long offToOnDurationMs = eventTime - mInteractiveOffTimestamp;
+                    if (mPastDimDurationMs > 0
+                            && offToOnDurationMs < USER_INITIATED_REVERT_THRESHOLD_MILLIS) {
+                        mWakefulnessSessionFrameworkStatsLogger.logDimEvent(
+                                mPhysicalDisplayPortIdForDefaultDisplay,
+                                reasonToBeChecked,
+                                lastUserActivity,
+                                lastUserActivityDurationMs,
+                                mPastDimDurationMs,
+                                mScreenOffTimeoutMs);
+                    }
+                    return;
+                }
+                default:
+                    return;
+            }
+        }
+
         void dump(IndentingPrintWriter writer) {
             final long now = mClock.uptimeMillis();
 
@@ -475,6 +710,12 @@
             final long prevUserActivityDurationMs = now - mPrevUserActivityTimestamp;
             writer.println("previous user activity duration: " + prevUserActivityDurationMs);
             writer.println("is in override timeout: " + isInOverrideTimeout());
+            writer.println("mIsInteractive: " + mIsInteractive);
+            writer.println("current screen policy: " + mCurrentScreenPolicy);
+            final long currentScreenPolicyDurationMs = now - mCurrentScreenPolicyTimestamp;
+            writer.println("current screen policy duration: " + currentScreenPolicyDurationMs);
+            writer.println("previous screen policy: " + mPrevScreenPolicy);
+            writer.println("past screen policy duration: " + mPrevScreenPolicyDurationMs);
             writer.decreaseIndent();
         }
     }
@@ -512,6 +753,24 @@
                     (long) defaultTimeoutMs);
         }
 
+        public void logDimEvent(
+                int physicalDisplayPortId,
+                @PolicyReason int policyReason,
+                @PowerManager.UserActivityEvent int userActivityEvent,
+                int lastUserActivityEventDurationMs,
+                int dimDurationMs,
+                int defaultTimeoutMs) {
+            int logUserActivityEvent = convertToLogUserActivityEvent(userActivityEvent);
+            FrameworkStatsLog.write(
+                    FrameworkStatsLog.SCREEN_DIM_REPORTED,
+                    physicalDisplayPortId,
+                    policyReason,
+                    logUserActivityEvent,
+                    lastUserActivityEventDurationMs,
+                    dimDurationMs,
+                    defaultTimeoutMs);
+        }
+
         private static final int USER_ACTIVITY_OTHER = FrameworkStatsLog
                 .SCREEN_INTERACTIVE_SESSION_REPORTED__LAST_USER_ACTIVITY_EVENT__OTHER;
 
@@ -591,5 +850,13 @@
         Clock getClock() {
             return SystemClock::uptimeMillis;
         }
+
+        Handler getHandler() {
+            return BackgroundThread.getHandler();
+        }
+
+        DisplayManagerInternal getDisplayManagerInternal() {
+            return LocalServices.getService(DisplayManagerInternal.class);
+        }
     }
 }
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index 44b414f..78636a7 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -98,7 +98,6 @@
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayDeque;
 import java.util.ArrayList;
-import java.util.function.Consumer;
 import java.util.function.Predicate;
 
 /**
@@ -290,12 +289,10 @@
                 getTopApp(mDisplayContent.mChangingContainers, false /* ignoreHidden */);
         final WindowManager.LayoutParams animLp = getAnimLp(animLpActivity);
 
-        // Check if there is any override
-        if (!overrideWithTaskFragmentRemoteAnimation(transit, activityTypes)) {
-            // Unfreeze the windows that were previously frozen for TaskFragment animation.
-            unfreezeEmbeddedChangingWindows();
-            overrideWithRemoteAnimationIfSet(animLpActivity, transit, activityTypes);
-        }
+        // No AE remote animation with Shell transition.
+        // Unfreeze the windows that were previously frozen for TaskFragment animation.
+        unfreezeEmbeddedChangingWindows();
+        overrideWithRemoteAnimationIfSet(animLpActivity, transit, activityTypes);
 
         final boolean voiceInteraction = containsVoiceInteraction(mDisplayContent.mClosingApps)
                 || containsVoiceInteraction(mDisplayContent.mOpeningApps);
@@ -726,64 +723,6 @@
     }
 
     /**
-     * Overrides the pending transition with the remote animation defined by the
-     * {@link ITaskFragmentOrganizer} if all windows in the transition are children of
-     * {@link TaskFragment} that are organized by the same organizer.
-     *
-     * @return {@code true} if the transition is overridden.
-     */
-    private boolean overrideWithTaskFragmentRemoteAnimation(@TransitionOldType int transit,
-            ArraySet<Integer> activityTypes) {
-        if (transitionMayContainNonAppWindows(transit)) {
-            return false;
-        }
-        if (!transitionContainsTaskFragmentWithBoundsOverride()) {
-            // No need to play TaskFragment remote animation if all embedded TaskFragment in the
-            // transition fill the Task.
-            return false;
-        }
-
-        final Task task = findParentTaskForAllEmbeddedWindows();
-        final ITaskFragmentOrganizer organizer = findTaskFragmentOrganizer(task);
-        final RemoteAnimationDefinition definition = organizer != null
-                ? mDisplayContent.mAtmService.mTaskFragmentOrganizerController
-                    .getRemoteAnimationDefinition(organizer)
-                : null;
-        final RemoteAnimationAdapter adapter = definition != null
-                ? definition.getAdapter(transit, activityTypes)
-                : null;
-        if (adapter == null) {
-            return false;
-        }
-        mDisplayContent.mAppTransition.overridePendingAppTransitionRemote(
-                adapter, false /* sync */, true /*isActivityEmbedding*/);
-        ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
-                "Override with TaskFragment remote animation for transit=%s",
-                AppTransition.appTransitionOldToString(transit));
-
-        final int organizerUid = mDisplayContent.mAtmService.mTaskFragmentOrganizerController
-                .getTaskFragmentOrganizerUid(organizer);
-        final boolean shouldDisableInputForRemoteAnimation = !task.isFullyTrustedEmbedding(
-                organizerUid);
-        final RemoteAnimationController remoteAnimationController =
-                mDisplayContent.mAppTransition.getRemoteAnimationController();
-        if (shouldDisableInputForRemoteAnimation && remoteAnimationController != null) {
-            // We are going to use client-driven animation, Disable all input on activity windows
-            // during the animation (unless it is fully trusted) to ensure it is safe to allow
-            // client to animate the surfaces.
-            // This is needed for all activity windows in the animation Task.
-            remoteAnimationController.setOnRemoteAnimationReady(() -> {
-                final Consumer<ActivityRecord> updateActivities =
-                        activity -> activity.setDropInputForAnimation(true);
-                task.forAllActivities(updateActivities);
-            });
-            ProtoLog.d(WM_DEBUG_APP_TRANSITIONS, "Task=%d contains embedded TaskFragment."
-                    + " Disabled all input during TaskFragment remote animation.", task.mTaskId);
-        }
-        return true;
-    }
-
-    /**
      * Overrides the pending transition with the remote animation defined for the transition in the
      * set of defined remote animations in the app window token.
      */
diff --git a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
index b6b6cf2..439c7bb 100644
--- a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
@@ -46,7 +46,6 @@
 import android.util.ArraySet;
 import android.util.Slog;
 import android.util.SparseArray;
-import android.view.RemoteAnimationDefinition;
 import android.view.WindowManager;
 import android.window.ITaskFragmentOrganizer;
 import android.window.ITaskFragmentOrganizerController;
@@ -58,8 +57,8 @@
 import android.window.WindowContainerTransaction;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.protolog.ProtoLogGroup;
 import com.android.internal.protolog.ProtoLog;
+import com.android.internal.protolog.ProtoLogGroup;
 import com.android.window.flags.Flags;
 
 import java.lang.annotation.Retention;
@@ -146,13 +145,6 @@
         private final boolean mIsSystemOrganizer;
 
         /**
-         * {@link RemoteAnimationDefinition} for embedded activities transition animation that is
-         * organized by this organizer.
-         */
-        @Nullable
-        private RemoteAnimationDefinition mRemoteAnimationDefinition;
-
-        /**
          * Map from {@link TaskFragmentTransaction#getTransactionToken()} to the
          * {@link Transition#getSyncId()} that has been deferred. {@link TransitionController} will
          * wait until the organizer finished handling the {@link TaskFragmentTransaction}.
@@ -533,50 +525,6 @@
     }
 
     @Override
-    public void registerRemoteAnimations(@NonNull ITaskFragmentOrganizer organizer,
-            @NonNull RemoteAnimationDefinition definition) {
-        final int pid = Binder.getCallingPid();
-        final int uid = Binder.getCallingUid();
-        synchronized (mGlobalLock) {
-            ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER,
-                    "Register remote animations for organizer=%s uid=%d pid=%d",
-                    organizer.asBinder(), uid, pid);
-            final TaskFragmentOrganizerState organizerState =
-                    mTaskFragmentOrganizerState.get(organizer.asBinder());
-            if (organizerState == null) {
-                throw new IllegalStateException("The organizer hasn't been registered.");
-            }
-            if (organizerState.mRemoteAnimationDefinition != null) {
-                throw new IllegalStateException(
-                        "The organizer has already registered remote animations="
-                                + organizerState.mRemoteAnimationDefinition);
-            }
-
-            definition.setCallingPidUid(pid, uid);
-            organizerState.mRemoteAnimationDefinition = definition;
-        }
-    }
-
-    @Override
-    public void unregisterRemoteAnimations(@NonNull ITaskFragmentOrganizer organizer) {
-        final int pid = Binder.getCallingPid();
-        final long uid = Binder.getCallingUid();
-        synchronized (mGlobalLock) {
-            ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER,
-                    "Unregister remote animations for organizer=%s uid=%d pid=%d",
-                    organizer.asBinder(), uid, pid);
-            final TaskFragmentOrganizerState organizerState =
-                    mTaskFragmentOrganizerState.get(organizer.asBinder());
-            if (organizerState == null) {
-                Slog.e(TAG, "The organizer hasn't been registered.");
-                return;
-            }
-
-            organizerState.mRemoteAnimationDefinition = null;
-        }
-    }
-
-    @Override
     public void onTransactionHandled(@NonNull IBinder transactionToken,
             @NonNull WindowContainerTransaction wct,
             @WindowManager.TransitionType int transitionType, boolean shouldApplyIndependently) {
@@ -617,25 +565,6 @@
         }
     }
 
-    /**
-     * Gets the {@link RemoteAnimationDefinition} set on the given organizer if exists. Returns
-     * {@code null} if it doesn't.
-     */
-    @Nullable
-    public RemoteAnimationDefinition getRemoteAnimationDefinition(
-            @NonNull ITaskFragmentOrganizer organizer) {
-        synchronized (mGlobalLock) {
-            final TaskFragmentOrganizerState organizerState =
-                    mTaskFragmentOrganizerState.get(organizer.asBinder());
-            if (organizerState == null) {
-                Slog.e(TAG, "TaskFragmentOrganizer has been unregistered or died when trying"
-                        + " to play animation on its organized windows.");
-                return null;
-            }
-            return organizerState.mRemoteAnimationDefinition;
-        }
-    }
-
     int getTaskFragmentOrganizerUid(@NonNull ITaskFragmentOrganizer organizer) {
         final TaskFragmentOrganizerState state = validateAndGetState(organizer);
         return state.mOrganizerUid;
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecNetworkTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecNetworkTest.java
index 10f4308..599a3b8 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecNetworkTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecNetworkTest.java
@@ -396,7 +396,7 @@
     }
 
     @Test
-    public void cecDevices_tracking_updatesPhysicalAddress() {
+    public void cecDevices_tracking_updatesPhysicalAddress_add() {
         int logicalAddress = Constants.ADDR_PLAYBACK_1;
         int initialPhysicalAddress = 0x1000;
         int updatedPhysicalAddress = 0x2000;
@@ -415,11 +415,12 @@
         assertThat(cecDeviceInfo.getPhysicalAddress()).isEqualTo(updatedPhysicalAddress);
         assertThat(cecDeviceInfo.getDeviceType()).isEqualTo(type);
 
-        // ADD for physical address first detected
-        // UPDATE for updating device with new physical address
+        // Handle case where PA is changed: Update CEC device information by calling
+        // addCecDevice().
         assertThat(mDeviceEventListenerStatuses).containsExactly(
                 HdmiControlManager.DEVICE_EVENT_ADD_DEVICE,
-                HdmiControlManager.DEVICE_EVENT_UPDATE_DEVICE);
+                HdmiControlManager.DEVICE_EVENT_REMOVE_DEVICE,
+                HdmiControlManager.DEVICE_EVENT_ADD_DEVICE);
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/power/WakefulnessSessionObserverTest.java b/services/tests/servicestests/src/com/android/server/power/WakefulnessSessionObserverTest.java
index 698f094..6b32be0 100644
--- a/services/tests/servicestests/src/com/android/server/power/WakefulnessSessionObserverTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/WakefulnessSessionObserverTest.java
@@ -16,9 +16,14 @@
 
 package com.android.server.power;
 
+import static android.hardware.display.DisplayManagerInternal.DisplayPowerRequest.POLICY_BRIGHT;
+import static android.hardware.display.DisplayManagerInternal.DisplayPowerRequest.POLICY_DIM;
+import static android.hardware.display.DisplayManagerInternal.DisplayPowerRequest.POLICY_OFF;
 import static android.os.PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON;
 import static android.os.PowerManager.GO_TO_SLEEP_REASON_TIMEOUT;
 import static android.os.PowerManager.WAKE_REASON_POWER_BUTTON;
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.DEFAULT_DISPLAY_GROUP;
 
 import static com.android.server.power.ScreenTimeoutOverridePolicy.RELEASE_REASON_UNKNOWN;
 import static com.android.server.power.ScreenTimeoutOverridePolicy.RELEASE_REASON_USER_ACTIVITY_TOUCH;
@@ -27,6 +32,10 @@
 import static com.android.server.power.WakefulnessSessionObserver.OVERRIDE_OUTCOME_CANCEL_USER_INTERACTION;
 import static com.android.server.power.WakefulnessSessionObserver.OVERRIDE_OUTCOME_TIMEOUT_SUCCESS;
 import static com.android.server.power.WakefulnessSessionObserver.OVERRIDE_OUTCOME_TIMEOUT_USER_INITIATED_REVERT;
+import static com.android.server.power.WakefulnessSessionObserver.POLICY_REASON_BRIGHT_INITIATED_REVERT;
+import static com.android.server.power.WakefulnessSessionObserver.POLICY_REASON_BRIGHT_UNDIM;
+import static com.android.server.power.WakefulnessSessionObserver.POLICY_REASON_OFF_POWER_BUTTON;
+import static com.android.server.power.WakefulnessSessionObserver.POLICY_REASON_OFF_TIMEOUT;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -40,18 +49,24 @@
 import android.content.Context;
 import android.content.ContextWrapper;
 import android.content.res.Resources;
+import android.hardware.display.DisplayManagerInternal;
+import android.os.Handler;
 import android.os.PowerManager;
 import android.os.PowerManagerInternal;
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.test.mock.MockContentResolver;
+import android.view.DisplayAddress;
+import android.view.DisplayInfo;
 
 import androidx.test.filters.SmallTest;
 import androidx.test.platform.app.InstrumentationRegistry;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.internal.R;
 import com.android.internal.util.test.FakeSettingsProvider;
 import com.android.server.testutils.OffsettableClock;
+import com.android.server.testutils.TestHandler;
 
 import org.junit.After;
 import org.junit.Before;
@@ -65,25 +80,18 @@
 public class WakefulnessSessionObserverTest {
     private static final int DEFAULT_SCREEN_OFF_TIMEOUT_MS = 30000;
     private static final int OVERRIDE_SCREEN_OFF_TIMEOUT_MS = 15000;
+    private static final int DISPLAY_PORT = 0xFF;
+    private static final long DISPLAY_MODEL = 0xEEEEEEEEL;
     private WakefulnessSessionObserver mWakefulnessSessionObserver;
     private Context mContext;
     private OffsettableClock mTestClock;
     @Mock
     private WakefulnessSessionObserver.WakefulnessSessionFrameworkStatsLogger
             mWakefulnessSessionFrameworkStatsLogger;
-    private WakefulnessSessionObserver.Injector mInjector =
-            new WakefulnessSessionObserver.Injector() {
-                @Override
-                WakefulnessSessionObserver.WakefulnessSessionFrameworkStatsLogger
-                        getWakefulnessSessionFrameworkStatsLogger() {
-                    return mWakefulnessSessionFrameworkStatsLogger;
-                }
-                @Override
-                WakefulnessSessionObserver.Clock getClock() {
-                    return mTestClock::now;
-                }
-            };
+    @Mock
+    private DisplayManagerInternal mDisplayManagerInternal;
 
+    private TestHandler mHandler;
     @Before
     public void setUp() {
         mTestClock = new OffsettableClock.Stopped();
@@ -95,7 +103,7 @@
 
         final Resources res = spy(mContext.getResources());
         doReturn(OVERRIDE_SCREEN_OFF_TIMEOUT_MS).when(res).getInteger(
-                com.android.internal.R.integer.config_screenTimeoutOverride);
+                R.integer.config_screenTimeoutOverride);
         when(mContext.getResources()).thenReturn(res);
         FakeSettingsProvider.clearSettingsProvider();
         MockContentResolver mockContentResolver = new MockContentResolver();
@@ -104,7 +112,32 @@
         Settings.System.putIntForUser(mockContentResolver, Settings.System.SCREEN_OFF_TIMEOUT,
                 DEFAULT_SCREEN_OFF_TIMEOUT_MS, UserHandle.USER_CURRENT);
 
-        mWakefulnessSessionObserver = new WakefulnessSessionObserver(mContext, mInjector);
+        final DisplayInfo info = new DisplayInfo();
+        info.address = DisplayAddress.fromPortAndModel(DISPLAY_PORT, DISPLAY_MODEL);
+        mHandler = new TestHandler(null);
+        mWakefulnessSessionObserver = new WakefulnessSessionObserver(
+                mContext, new WakefulnessSessionObserver.Injector() {
+                    @Override
+                    WakefulnessSessionObserver.WakefulnessSessionFrameworkStatsLogger
+                            getWakefulnessSessionFrameworkStatsLogger() {
+                        return mWakefulnessSessionFrameworkStatsLogger;
+                    }
+                    @Override
+                    WakefulnessSessionObserver.Clock getClock() {
+                        return mTestClock::now;
+                    }
+                    @Override
+                    Handler getHandler() {
+                        return mHandler;
+                    }
+                    @Override
+                    DisplayManagerInternal getDisplayManagerInternal() {
+                        when(mDisplayManagerInternal.getDisplayInfo(DEFAULT_DISPLAY))
+                                .thenReturn(info);
+                        return mDisplayManagerInternal;
+                    }
+                }
+        );
     }
 
     @After
@@ -317,6 +350,167 @@
                         DEFAULT_SCREEN_OFF_TIMEOUT_MS); // default timeout ms
     }
 
+    @Test
+    public void testOnScreenPolicyUpdate_OffByTimeout() {
+        int userActivity = PowerManager.USER_ACTIVITY_EVENT_ATTENTION;
+        long userActivityTimestamp = mTestClock.now();
+        mWakefulnessSessionObserver.notifyUserActivity(
+                userActivityTimestamp, DEFAULT_DISPLAY_GROUP, userActivity);
+        mWakefulnessSessionObserver.onScreenPolicyUpdate(
+                mTestClock.now(), DEFAULT_DISPLAY_GROUP, POLICY_DIM);
+        mWakefulnessSessionObserver.onWakefulnessChangeStarted(
+                DEFAULT_DISPLAY_GROUP, PowerManagerInternal.WAKEFULNESS_AWAKE,
+                WAKE_REASON_POWER_BUTTON, mTestClock.now());
+        int advancedTime = 5;
+        advanceTime(advancedTime);
+        mWakefulnessSessionObserver.onScreenPolicyUpdate(mTestClock.now(), DEFAULT_DISPLAY_GROUP,
+                POLICY_OFF);
+        mWakefulnessSessionObserver.onWakefulnessChangeStarted(
+                DEFAULT_DISPLAY_GROUP, PowerManagerInternal.WAKEFULNESS_ASLEEP,
+                GO_TO_SLEEP_REASON_TIMEOUT, mTestClock.now());
+
+        verify(mWakefulnessSessionFrameworkStatsLogger)
+                .logDimEvent(
+                        DISPLAY_PORT, // physical display port id
+                        POLICY_REASON_OFF_TIMEOUT, // policy reason
+                        userActivity, // last user activity event
+                        advancedTime, // last user activity timestamp
+                        advancedTime, // dim duration ms
+                        DEFAULT_SCREEN_OFF_TIMEOUT_MS); // default Timeout Ms
+    }
+
+    @Test
+    public void testOnScreenPolicyUpdate_NoLogging_NotDefaultDisplayGroup() {
+        int powerGroupId = 1;
+        int userActivity = PowerManager.USER_ACTIVITY_EVENT_ATTENTION;
+        long userActivityTimestamp = mTestClock.now();
+        int advancedTime = 5;
+        mWakefulnessSessionObserver.notifyUserActivity(
+                userActivityTimestamp, powerGroupId, userActivity);
+        mWakefulnessSessionObserver.onScreenPolicyUpdate(
+                mTestClock.now(), powerGroupId, POLICY_DIM);
+        advanceTime(advancedTime);
+        mWakefulnessSessionObserver.onScreenPolicyUpdate(mTestClock.now(), powerGroupId,
+                POLICY_OFF);
+
+        verify(mWakefulnessSessionFrameworkStatsLogger, never())
+                .logDimEvent(
+                        DISPLAY_PORT, // physical display port id
+                        POLICY_REASON_OFF_TIMEOUT, // policy reason
+                        userActivity, // last user activity event
+                        advancedTime, // last user activity timestamp
+                        advancedTime, // dim duration ms
+                        DEFAULT_SCREEN_OFF_TIMEOUT_MS); // default Timeout Ms
+    }
+
+    @Test
+    public void testOnScreenPolicyUpdate_OffByPowerButton() {
+        // ----- initialize start -----
+        mWakefulnessSessionObserver.onWakefulnessChangeStarted(
+                DEFAULT_DISPLAY_GROUP, PowerManagerInternal.WAKEFULNESS_AWAKE,
+                WAKE_REASON_POWER_BUTTON, mTestClock.now());
+
+        int userActivity = PowerManager.USER_ACTIVITY_EVENT_ACCESSIBILITY;
+        long userActivityTimestamp = mTestClock.now();
+        mWakefulnessSessionObserver.notifyUserActivity(
+                userActivityTimestamp, DEFAULT_DISPLAY_GROUP, userActivity);
+        mWakefulnessSessionObserver.onScreenPolicyUpdate(
+                mTestClock.now(), DEFAULT_DISPLAY_GROUP, POLICY_DIM);
+        // ----- initialize end -----
+
+        int dimDuration = 500;
+        advanceTime(dimDuration);
+        int userActivityDuration = dimDuration;
+        mWakefulnessSessionObserver.notifyUserActivity(
+                mTestClock.now(), DEFAULT_DISPLAY_GROUP, PowerManager.USER_ACTIVITY_EVENT_BUTTON);
+        mWakefulnessSessionObserver.onWakefulnessChangeStarted(
+                DEFAULT_DISPLAY_GROUP, PowerManagerInternal.WAKEFULNESS_ASLEEP,
+                GO_TO_SLEEP_REASON_POWER_BUTTON, mTestClock.now());
+
+        verify(mWakefulnessSessionFrameworkStatsLogger)
+                .logDimEvent(
+                        DISPLAY_PORT, // physical display port id
+                        POLICY_REASON_OFF_POWER_BUTTON, // policy reason
+                        userActivity, // last user activity event
+                        userActivityDuration, // last user activity timestamp
+                        dimDuration, // dim duration ms
+                        DEFAULT_SCREEN_OFF_TIMEOUT_MS); // default Timeout Ms
+        assertThat(mHandler.getPendingMessages()).isEmpty();
+    }
+
+    @Test
+    public void testOnScreenPolicyUpdate_Undim() {
+        // ----- initialize start -----
+        int userActivity = PowerManager.USER_ACTIVITY_EVENT_TOUCH;
+        long userActivityTimestamp = mTestClock.now();
+        mWakefulnessSessionObserver.notifyUserActivity(
+                userActivityTimestamp, DEFAULT_DISPLAY_GROUP, userActivity);
+        mWakefulnessSessionObserver.onScreenPolicyUpdate(
+                mTestClock.now(), DEFAULT_DISPLAY_GROUP, POLICY_DIM);
+        mWakefulnessSessionObserver.mPowerGroups.get(DEFAULT_DISPLAY_GROUP).mIsInteractive = true;
+        // ----- initialize end -----
+
+        int dimDurationMs = 5;
+        advanceTime(dimDurationMs);
+        mWakefulnessSessionObserver.onScreenPolicyUpdate(
+                mTestClock.now(), DEFAULT_DISPLAY_GROUP, POLICY_BRIGHT);
+
+        int expectedLastUserActivityTimeMs = (int) (mTestClock.now() - userActivityTimestamp);
+
+        mHandler.flush();
+        verify(mWakefulnessSessionFrameworkStatsLogger)
+                .logDimEvent(
+                        DISPLAY_PORT, // physical display port id
+                        POLICY_REASON_BRIGHT_UNDIM, // policy reason
+                        userActivity, // last user activity event
+                        expectedLastUserActivityTimeMs, // last user activity timestamp
+                        dimDurationMs, // dim duration ms
+                        DEFAULT_SCREEN_OFF_TIMEOUT_MS); // default Timeout Ms
+    }
+
+    @Test
+    public void testOnScreenPolicyUpdate_BrightInitiatedRevert() {
+        // ----- initialize start -----
+        mWakefulnessSessionObserver.onScreenPolicyUpdate(
+                mTestClock.now(), DEFAULT_DISPLAY_GROUP, POLICY_DIM);
+        int dimDurationMs = 500;
+        advanceTime(dimDurationMs);
+        int userActivity = PowerManager.USER_ACTIVITY_EVENT_BUTTON;
+        long userActivityTimestamp = mTestClock.now();
+        mWakefulnessSessionObserver.notifyUserActivity(
+                userActivityTimestamp, DEFAULT_DISPLAY_GROUP, userActivity);
+        int userActivityTime = 5;
+        advanceTime(userActivityTime);
+        dimDurationMs += userActivityTime;
+        mWakefulnessSessionObserver.onScreenPolicyUpdate(
+                mTestClock.now(), DEFAULT_DISPLAY_GROUP, POLICY_OFF);
+        mWakefulnessSessionObserver.onWakefulnessChangeStarted(
+                DEFAULT_DISPLAY_GROUP, PowerManagerInternal.WAKEFULNESS_ASLEEP,
+                GO_TO_SLEEP_REASON_POWER_BUTTON, mTestClock.now());
+
+        mWakefulnessSessionObserver.mPowerGroups.get(DEFAULT_DISPLAY_GROUP)
+                .mPastDimDurationMs = dimDurationMs;
+        // ----- initialize end -----
+
+        int advancedTime = 5;
+        advanceTime(advancedTime); // shorter than 5000 ms
+        userActivityTime += advancedTime;
+        mWakefulnessSessionObserver.onScreenPolicyUpdate(mTestClock.now(), DEFAULT_DISPLAY_GROUP,
+                POLICY_BRIGHT);
+        mWakefulnessSessionObserver.onWakefulnessChangeStarted(
+                DEFAULT_DISPLAY_GROUP, PowerManagerInternal.WAKEFULNESS_AWAKE,
+                WAKE_REASON_POWER_BUTTON, mTestClock.now());
+
+        verify(mWakefulnessSessionFrameworkStatsLogger)
+                .logDimEvent(
+                        DISPLAY_PORT, // physical display port id
+                        POLICY_REASON_BRIGHT_INITIATED_REVERT, // policy reason
+                        userActivity, // last user activity event
+                        userActivityTime, // last user activity timestamp
+                        dimDurationMs, // dim duration ms
+                        DEFAULT_SCREEN_OFF_TIMEOUT_MS); // default Timeout Ms
+    }
+
     private void advanceTime(long timeMs) {
         mTestClock.fastForward(timeMs);
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
index 0c1fbf3..af4394a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
@@ -25,15 +25,11 @@
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
 import static android.view.WindowManager.TRANSIT_CHANGE;
 import static android.view.WindowManager.TRANSIT_CLOSE;
-import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_CLOSE;
 import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN;
 import static android.view.WindowManager.TRANSIT_OLD_DREAM_ACTIVITY_CLOSE;
 import static android.view.WindowManager.TRANSIT_OLD_DREAM_ACTIVITY_OPEN;
 import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_UNOCCLUDE;
 import static android.view.WindowManager.TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE;
-import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_CHANGE;
-import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_CLOSE;
-import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_OPEN;
 import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN;
 import static android.view.WindowManager.TRANSIT_OPEN;
 import static android.view.WindowManager.TRANSIT_TO_FRONT;
@@ -41,7 +37,6 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
-import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -51,14 +46,11 @@
 import static org.junit.Assume.assumeFalse;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 
 import android.annotation.Nullable;
-import android.graphics.Rect;
-import android.gui.DropInputMode;
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -841,353 +833,6 @@
     }
 
     @Test
-    public void testOverrideTaskFragmentAdapter_overrideWithEmbeddedActivity() {
-        final Task task = createTask(mDisplayContent);
-        final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
-        final TestRemoteAnimationRunner remoteAnimationRunner = new TestRemoteAnimationRunner();
-        setupTaskFragmentRemoteAnimation(organizer, remoteAnimationRunner);
-
-        // Create a TaskFragment with embedded activity.
-        final TaskFragment taskFragment = createTaskFragmentWithEmbeddedActivity(task, organizer);
-        final ActivityRecord activity = taskFragment.getTopMostActivity();
-        prepareActivityForAppTransition(activity);
-        spyOn(mDisplayContent.mAppTransition);
-
-        // Prepare and start transition.
-        prepareAndTriggerAppTransition(activity, null /* closingActivity */, taskFragment);
-        mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
-
-        // Animation run by the remote handler.
-        assertTrue(remoteAnimationRunner.isAnimationStarted());
-    }
-
-    @Test
-    public void testOverrideTaskFragmentAdapter_noOverrideWithOnlyTaskFragmentFillingTask() {
-        final Task task = createTask(mDisplayContent);
-        final ActivityRecord closingActivity = createActivityRecord(task);
-        final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
-        final TestRemoteAnimationRunner remoteAnimationRunner = new TestRemoteAnimationRunner();
-        setupTaskFragmentRemoteAnimation(organizer, remoteAnimationRunner);
-
-        // Create a TaskFragment with embedded activity.
-        final TaskFragment taskFragment = createTaskFragmentWithEmbeddedActivity(task, organizer);
-
-        // Make sure the TaskFragment is not embedded.
-        assertFalse(taskFragment.isEmbeddedWithBoundsOverride());
-        final ActivityRecord openingActivity = taskFragment.getTopMostActivity();
-        prepareActivityForAppTransition(closingActivity);
-        prepareActivityForAppTransition(openingActivity);
-        final int uid = 12345;
-        closingActivity.info.applicationInfo.uid = uid;
-        openingActivity.info.applicationInfo.uid = uid;
-        task.effectiveUid = uid;
-        spyOn(mDisplayContent.mAppTransition);
-
-        // Prepare and start transition.
-        prepareAndTriggerAppTransition(openingActivity, closingActivity,
-                null /* changingTaskFragment */);
-        mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
-
-        // Animation is not run by the remote handler because the activity is filling the Task.
-        assertFalse(remoteAnimationRunner.isAnimationStarted());
-    }
-
-    @Test
-    public void testOverrideTaskFragmentAdapter_overrideWithTaskFragmentNotFillingTask() {
-        final Task task = createTask(mDisplayContent);
-        final ActivityRecord closingActivity = createActivityRecord(task);
-        final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
-        final TestRemoteAnimationRunner remoteAnimationRunner = new TestRemoteAnimationRunner();
-        setupTaskFragmentRemoteAnimation(organizer, remoteAnimationRunner);
-
-        // Create a TaskFragment with embedded activity.
-        final TaskFragment taskFragment = createTaskFragmentWithEmbeddedActivity(task, organizer);
-
-        // Make sure the TaskFragment is embedded.
-        taskFragment.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
-        final Rect embeddedBounds = new Rect(task.getBounds());
-        embeddedBounds.right = embeddedBounds.left + embeddedBounds.width() / 2;
-        taskFragment.setBounds(embeddedBounds);
-        assertTrue(taskFragment.isEmbeddedWithBoundsOverride());
-        final ActivityRecord openingActivity = taskFragment.getTopMostActivity();
-        prepareActivityForAppTransition(closingActivity);
-        prepareActivityForAppTransition(openingActivity);
-        final int uid = 12345;
-        closingActivity.info.applicationInfo.uid = uid;
-        openingActivity.info.applicationInfo.uid = uid;
-        task.effectiveUid = uid;
-        spyOn(mDisplayContent.mAppTransition);
-
-        // Prepare and start transition.
-        prepareAndTriggerAppTransition(openingActivity, closingActivity,
-                null /* changingTaskFragment */);
-        mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
-
-        // Animation run by the remote handler.
-        assertTrue(remoteAnimationRunner.isAnimationStarted());
-    }
-
-    @Test
-    public void testOverrideTaskFragmentAdapter_overrideWithNonEmbeddedActivity() {
-        final Task task = createTask(mDisplayContent);
-        final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
-        final TestRemoteAnimationRunner remoteAnimationRunner = new TestRemoteAnimationRunner();
-        setupTaskFragmentRemoteAnimation(organizer, remoteAnimationRunner);
-
-        // Closing non-embedded activity.
-        final ActivityRecord closingActivity = createActivityRecord(task);
-        prepareActivityForAppTransition(closingActivity);
-        // Opening TaskFragment with embedded activity.
-        final TaskFragment taskFragment = createTaskFragmentWithEmbeddedActivity(task, organizer);
-        final ActivityRecord openingActivity = taskFragment.getTopMostActivity();
-        prepareActivityForAppTransition(openingActivity);
-        task.effectiveUid = openingActivity.getUid();
-        spyOn(mDisplayContent.mAppTransition);
-
-        // Prepare and start transition.
-        prepareAndTriggerAppTransition(openingActivity, closingActivity, taskFragment);
-        mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
-
-        // Animation run by the remote handler.
-        assertTrue(remoteAnimationRunner.isAnimationStarted());
-    }
-
-    @Test
-    public void testOverrideTaskFragmentAdapter_overrideEmbeddedActivityWithDiffUid() {
-        final Task task = createTask(mDisplayContent);
-        final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
-        final TestRemoteAnimationRunner remoteAnimationRunner = new TestRemoteAnimationRunner();
-        setupTaskFragmentRemoteAnimation(organizer, remoteAnimationRunner);
-
-        // Closing TaskFragment with embedded activity.
-        final TaskFragment taskFragment1 = createTaskFragmentWithEmbeddedActivity(task, organizer);
-        final ActivityRecord closingActivity = taskFragment1.getTopMostActivity();
-        prepareActivityForAppTransition(closingActivity);
-        closingActivity.info.applicationInfo.uid = 12345;
-        // Opening TaskFragment with embedded activity with different UID.
-        final TaskFragment taskFragment2 = createTaskFragmentWithEmbeddedActivity(task, organizer);
-        final ActivityRecord openingActivity = taskFragment2.getTopMostActivity();
-        prepareActivityForAppTransition(openingActivity);
-        openingActivity.info.applicationInfo.uid = 54321;
-        spyOn(mDisplayContent.mAppTransition);
-
-        // Prepare and start transition.
-        prepareAndTriggerAppTransition(openingActivity, closingActivity, taskFragment1);
-        mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
-
-        // Animation run by the remote handler.
-        assertTrue(remoteAnimationRunner.isAnimationStarted());
-    }
-
-    @Test
-    public void testOverrideTaskFragmentAdapter_noOverrideWithTwoApps() {
-        final Task task = createTask(mDisplayContent);
-        final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
-        final TestRemoteAnimationRunner remoteAnimationRunner = new TestRemoteAnimationRunner();
-        setupTaskFragmentRemoteAnimation(organizer, remoteAnimationRunner);
-
-        // Closing activity in Task1.
-        final ActivityRecord closingActivity = createActivityRecord(mDisplayContent);
-        prepareActivityForAppTransition(closingActivity);
-        // Opening TaskFragment with embedded activity in Task2.
-        final TaskFragment taskFragment = createTaskFragmentWithEmbeddedActivity(task, organizer);
-        final ActivityRecord openingActivity = taskFragment.getTopMostActivity();
-        prepareActivityForAppTransition(openingActivity);
-        spyOn(mDisplayContent.mAppTransition);
-
-        // Prepare and start transition.
-        prepareAndTriggerAppTransition(openingActivity, closingActivity, taskFragment);
-        mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
-
-        // Animation not run by the remote handler.
-        assertFalse(remoteAnimationRunner.isAnimationStarted());
-    }
-
-    @Test
-    public void testOverrideTaskFragmentAdapter_noOverrideNonEmbeddedActivityWithDiffUid() {
-        final Task task = createTask(mDisplayContent);
-        final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
-        final TestRemoteAnimationRunner remoteAnimationRunner = new TestRemoteAnimationRunner();
-        setupTaskFragmentRemoteAnimation(organizer, remoteAnimationRunner);
-
-        // Closing TaskFragment with embedded activity.
-        final TaskFragment taskFragment = createTaskFragmentWithEmbeddedActivity(task, organizer);
-        final ActivityRecord closingActivity = taskFragment.getTopMostActivity();
-        prepareActivityForAppTransition(closingActivity);
-        closingActivity.info.applicationInfo.uid = 12345;
-        task.effectiveUid = closingActivity.getUid();
-        // Opening non-embedded activity with different UID.
-        final ActivityRecord openingActivity = createActivityRecord(task);
-        prepareActivityForAppTransition(openingActivity);
-        openingActivity.info.applicationInfo.uid = 54321;
-        spyOn(mDisplayContent.mAppTransition);
-
-        // Prepare and start transition.
-        prepareAndTriggerAppTransition(openingActivity, closingActivity, taskFragment);
-        mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
-
-        // Animation should not run by the remote handler when there are non-embedded activities of
-        // different UID.
-        assertFalse(remoteAnimationRunner.isAnimationStarted());
-    }
-
-    @Test
-    public void testOverrideTaskFragmentAdapter_noOverrideWithWallpaper() {
-        final Task task = createTask(mDisplayContent);
-        final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
-        final TestRemoteAnimationRunner remoteAnimationRunner = new TestRemoteAnimationRunner();
-        setupTaskFragmentRemoteAnimation(organizer, remoteAnimationRunner);
-
-        // Create a TaskFragment with embedded activity.
-        final TaskFragment taskFragment = createTaskFragmentWithEmbeddedActivity(task, organizer);
-        final ActivityRecord activity = taskFragment.getTopMostActivity();
-        prepareActivityForAppTransition(activity);
-        // Set wallpaper as visible.
-        final WallpaperWindowToken wallpaperWindowToken = new WallpaperWindowToken(mWm,
-                mock(IBinder.class), true, mDisplayContent, true /* ownerCanManageAppTokens */);
-        spyOn(mDisplayContent.mWallpaperController);
-        doReturn(true).when(mDisplayContent.mWallpaperController).isWallpaperVisible();
-        spyOn(mDisplayContent.mAppTransition);
-
-        // Prepare and start transition.
-        prepareAndTriggerAppTransition(activity, null /* closingActivity */, taskFragment);
-        mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
-
-        // Animation should not run by the remote handler when there is wallpaper in the transition.
-        assertFalse(remoteAnimationRunner.isAnimationStarted());
-    }
-
-    @Test
-    public void testOverrideTaskFragmentAdapter_inputProtectedForUntrustedAnimation() {
-        final Task task = createTask(mDisplayContent);
-        final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
-        final TestRemoteAnimationRunner remoteAnimationRunner = new TestRemoteAnimationRunner();
-        setupTaskFragmentRemoteAnimation(organizer, remoteAnimationRunner);
-
-        // Create a TaskFragment with embedded activities, one is trusted embedded, and the other
-        // one is untrusted embedded.
-        final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm)
-                .setParentTask(task)
-                .createActivityCount(2)
-                .setOrganizer(organizer)
-                .build();
-        final ActivityRecord activity0 = taskFragment.getChildAt(0).asActivityRecord();
-        final ActivityRecord activity1 = taskFragment.getChildAt(1).asActivityRecord();
-        // Also create a non-embedded activity in the Task.
-        final ActivityRecord activity2 = new ActivityBuilder(mAtm).build();
-        task.addChild(activity2, POSITION_BOTTOM);
-        prepareActivityForAppTransition(activity0);
-        prepareActivityForAppTransition(activity1);
-        prepareActivityForAppTransition(activity2);
-        doReturn(false).when(taskFragment).isAllowedToEmbedActivityInTrustedMode(activity0);
-        doReturn(true).when(taskFragment).isAllowedToEmbedActivityInTrustedMode(activity1);
-        spyOn(mDisplayContent.mAppTransition);
-
-        // Prepare and start transition.
-        prepareAndTriggerAppTransition(activity1, null /* closingActivity */, taskFragment);
-        mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
-
-        // The animation will be animated remotely by client and all activities are input disabled
-        // for untrusted animation.
-        assertTrue(remoteAnimationRunner.isAnimationStarted());
-        verify(activity0).setDropInputForAnimation(true);
-        verify(activity1).setDropInputForAnimation(true);
-        verify(activity2).setDropInputForAnimation(true);
-        verify(activity0).setDropInputMode(DropInputMode.ALL);
-        verify(activity1).setDropInputMode(DropInputMode.ALL);
-        verify(activity2).setDropInputMode(DropInputMode.ALL);
-
-        // Reset input after animation is finished.
-        clearInvocations(activity0);
-        clearInvocations(activity1);
-        clearInvocations(activity2);
-        remoteAnimationRunner.finishAnimation();
-
-        verify(activity0).setDropInputForAnimation(false);
-        verify(activity1).setDropInputForAnimation(false);
-        verify(activity2).setDropInputForAnimation(false);
-        verify(activity0).setDropInputMode(DropInputMode.OBSCURED);
-        verify(activity1).setDropInputMode(DropInputMode.NONE);
-        verify(activity2).setDropInputMode(DropInputMode.NONE);
-    }
-
-    /**
-     * Since we don't have any use case to rely on handling input during animation, disable it even
-     * if it is trusted embedding so that it could cover some edge-cases when a previously trusted
-     * host starts doing something bad.
-     */
-    @Test
-    public void testOverrideTaskFragmentAdapter_inputProtectedForTrustedAnimation() {
-        final Task task = createTask(mDisplayContent);
-        final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
-        final TestRemoteAnimationRunner remoteAnimationRunner = new TestRemoteAnimationRunner();
-        setupTaskFragmentRemoteAnimation(organizer, remoteAnimationRunner);
-
-        // Create a TaskFragment with only trusted embedded activity
-        final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm)
-                .setParentTask(task)
-                .createActivityCount(1)
-                .setOrganizer(organizer)
-                .build();
-        final ActivityRecord activity = taskFragment.getChildAt(0).asActivityRecord();
-        prepareActivityForAppTransition(activity);
-        doReturn(true).when(taskFragment).isAllowedToEmbedActivityInTrustedMode(activity);
-        spyOn(mDisplayContent.mAppTransition);
-
-        // Prepare and start transition.
-        prepareAndTriggerAppTransition(activity, null /* closingActivity */, taskFragment);
-        mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
-
-        // The animation will be animated remotely by client and all activities are input disabled
-        // for untrusted animation.
-        assertTrue(remoteAnimationRunner.isAnimationStarted());
-        verify(activity).setDropInputForAnimation(true);
-        verify(activity).setDropInputMode(DropInputMode.ALL);
-
-        // Reset input after animation is finished.
-        clearInvocations(activity);
-        remoteAnimationRunner.finishAnimation();
-
-        verify(activity).setDropInputForAnimation(false);
-        verify(activity).setDropInputMode(DropInputMode.NONE);
-    }
-
-    /**
-     * We don't need to drop input for fully trusted embedding (system app, and embedding in the
-     * same app). This will allow users to do fast tapping.
-     */
-    @Test
-    public void testOverrideTaskFragmentAdapter_noInputProtectedForFullyTrustedAnimation() {
-        final Task task = createTask(mDisplayContent);
-        final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
-        final TestRemoteAnimationRunner remoteAnimationRunner = new TestRemoteAnimationRunner();
-        setupTaskFragmentRemoteAnimation(organizer, remoteAnimationRunner);
-
-        // Create a TaskFragment with only trusted embedded activity
-        final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm)
-                .setParentTask(task)
-                .createActivityCount(1)
-                .setOrganizer(organizer)
-                .build();
-        final ActivityRecord activity = taskFragment.getChildAt(0).asActivityRecord();
-        prepareActivityForAppTransition(activity);
-        final int uid = mAtm.mTaskFragmentOrganizerController.getTaskFragmentOrganizerUid(
-                getITaskFragmentOrganizer(organizer));
-        doReturn(true).when(task).isFullyTrustedEmbedding(uid);
-        spyOn(mDisplayContent.mAppTransition);
-
-        // Prepare and start transition.
-        prepareAndTriggerAppTransition(activity, null /* closingActivity */, taskFragment);
-        mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
-
-        // The animation will be animated remotely by client, but input should not be dropped for
-        // fully trusted.
-        assertTrue(remoteAnimationRunner.isAnimationStarted());
-        verify(activity, never()).setDropInputForAnimation(true);
-        verify(activity, never()).setDropInputMode(DropInputMode.ALL);
-    }
-
-    @Test
     public void testTransitionGoodToGoForTaskFragments() {
         final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
         final Task task = createTask(mDisplayContent);
@@ -1253,22 +898,6 @@
         verify(mDisplayContent.mAppTransition).goodToGo(anyInt(), any());
     }
 
-    /** Registers remote animation for the organizer. */
-    private void setupTaskFragmentRemoteAnimation(TaskFragmentOrganizer organizer,
-            TestRemoteAnimationRunner remoteAnimationRunner) {
-        final RemoteAnimationAdapter adapter = new RemoteAnimationAdapter(
-                remoteAnimationRunner, 10, 1);
-        final ITaskFragmentOrganizer iOrganizer = getITaskFragmentOrganizer(organizer);
-        final RemoteAnimationDefinition definition = new RemoteAnimationDefinition();
-        definition.addRemoteAnimation(TRANSIT_OLD_TASK_FRAGMENT_CHANGE, adapter);
-        definition.addRemoteAnimation(TRANSIT_OLD_TASK_FRAGMENT_OPEN, adapter);
-        definition.addRemoteAnimation(TRANSIT_OLD_TASK_FRAGMENT_CLOSE, adapter);
-        definition.addRemoteAnimation(TRANSIT_OLD_ACTIVITY_OPEN, adapter);
-        definition.addRemoteAnimation(TRANSIT_OLD_ACTIVITY_CLOSE, adapter);
-        registerTaskFragmentOrganizer(iOrganizer);
-        mAtm.mTaskFragmentOrganizerController.registerRemoteAnimations(iOrganizer, definition);
-    }
-
     private static ITaskFragmentOrganizer getITaskFragmentOrganizer(
             TaskFragmentOrganizer organizer) {
         return ITaskFragmentOrganizer.Stub.asInterface(organizer.getOrganizerToken().asBinder());
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
index a71b81e..d013053 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
@@ -90,7 +90,6 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.platform.test.annotations.Presubmit;
-import android.view.RemoteAnimationDefinition;
 import android.view.SurfaceControl;
 import android.window.IRemoteTransition;
 import android.window.ITaskFragmentOrganizer;
@@ -140,7 +139,6 @@
     private IBinder mFragmentToken;
     private WindowContainerTransaction mTransaction;
     private WindowContainerToken mFragmentWindowToken;
-    private RemoteAnimationDefinition mDefinition;
     private IBinder mErrorToken;
     private Rect mTaskFragBounds;
 
@@ -169,7 +167,6 @@
         mTransaction = new WindowContainerTransaction();
         mTransaction.setTaskFragmentOrganizer(mIOrganizer);
         mFragmentWindowToken = mTaskFragment.mRemoteToken.toWindowContainerToken();
-        mDefinition = new RemoteAnimationDefinition();
         mErrorToken = new Binder();
         final Rect displayBounds = mDisplayContent.getBounds();
         mTaskFragBounds = new Rect(displayBounds.left, displayBounds.top, displayBounds.centerX(),
@@ -579,17 +576,6 @@
     }
 
     @Test
-    public void testRegisterRemoteAnimations() {
-        mController.registerRemoteAnimations(mIOrganizer, mDefinition);
-
-        assertEquals(mDefinition, mController.getRemoteAnimationDefinition(mIOrganizer));
-
-        mController.unregisterRemoteAnimations(mIOrganizer);
-
-        assertNull(mController.getRemoteAnimationDefinition(mIOrganizer));
-    }
-
-    @Test
     public void testApplyTransaction_disallowRemoteTransitionForNonSystemOrganizer() {
         mTransaction.setRelativeBounds(mFragmentWindowToken, new Rect(0, 0, 100, 100));
         mTaskFragment.setTaskFragmentOrganizer(mOrganizerToken, 10 /* uid */,