From ef4353ebe2f2ae339e3f5f3391ec3eb50e102472 Mon Sep 17 00:00:00 2001
From: Riddle Hsu <riddlehsu@google.com>
Date: Wed, 18 Oct 2023 16:51:12 +0800
Subject: [PATCH] Apply perf session for regular window animation

This keeps the same behavior:
 - App transition/rotation/recents
   -> Set sf early wake
   -> Pause snapshot persist queue
   -> Use high refresh rate
 - Window container with running SurfaceAnimator
   -> Use high refresh rate

Also
 - Centralize perf sessions to display (fix leaked sessions).
 - Add support to reuse session for flexibility of management.
 - Fix non-paired session of transition. Because the transition
   can be aborted without playing.
 - Fix non-paired trace of SystemPerformanceHinter. Because the
   begin/end of name may not match if there are multiple sessions.
 - Use asyncTraceForTrack, which reduces extra rows in perfetto.

Bug: 305987082
Test: TransitionTests#testTransitionsTriggerPerformanceHints
      WindowContainerTests#testRemoveImmediatelyClearsLeash

Change-Id: If5cf41b9a0586f5a0a76d4a9044a5489900502df
---
 .../window/SystemPerformanceHinter.java       | 117 ++++++++++++++----
 .../com/android/server/wm/DisplayContent.java |  50 ++++++++
 .../com/android/server/wm/Transition.java     |  16 ++-
 .../server/wm/TransitionController.java       |  58 +++------
 .../com/android/server/wm/WindowAnimator.java |  47 ++++---
 .../server/wm/WindowManagerService.java       |   6 +-
 .../server/testutils/StubTransaction.java     |  14 ++-
 .../android/server/wm/TransitionTests.java    |  57 ++++-----
 .../server/wm/WindowContainerTests.java       |   6 +
 9 files changed, 248 insertions(+), 123 deletions(-)

diff --git a/core/java/android/window/SystemPerformanceHinter.java b/core/java/android/window/SystemPerformanceHinter.java
index 2736b68845a2b..5b0d8d1233c63 100644
--- a/core/java/android/window/SystemPerformanceHinter.java
+++ b/core/java/android/window/SystemPerformanceHinter.java
@@ -29,11 +29,11 @@ import android.content.Context;
 import android.os.PerformanceHintManager;
 import android.os.Trace;
 import android.util.Log;
+import android.view.Display;
 import android.view.SurfaceControl;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
-import java.util.Random;
 import java.util.function.Supplier;
 
 /**
@@ -45,6 +45,7 @@ import java.util.function.Supplier;
 public class SystemPerformanceHinter {
     private static final String TAG = "SystemPerformanceHinter";
 
+    private static final int HINT_NO_OP = 0;
     // Change app and SF wakeup times to allow sf more time to composite a frame
     public static final int HINT_SF_EARLY_WAKEUP = 1 << 0;
     // Force max refresh rate
@@ -88,14 +89,17 @@ public class SystemPerformanceHinter {
         private final @HintFlags int hintFlags;
         private final String reason;
         private final int displayId;
-        private final int traceCookie;
+        private String mTraceName;
 
         protected HighPerfSession(@HintFlags int hintFlags, int displayId, @NonNull String reason) {
             this.hintFlags = hintFlags;
             this.reason = reason;
             this.displayId = displayId;
-            this.traceCookie = new Random().nextInt();
-            if (hintFlags != 0) {
+        }
+
+        /** Makes this session active. It is no-op if this session is already active. */
+        public void start() {
+            if (!mActiveSessions.contains(this)) {
                 startSession(this);
             }
         }
@@ -103,15 +107,36 @@ public class SystemPerformanceHinter {
         /**
          * Closes this session.
          */
+        @Override
         public void close() {
-            if (hintFlags != 0) {
-                endSession(this);
-            }
+            endSession(this);
         }
 
+        @Override
         public void finalize() {
             close();
         }
+
+        boolean asyncTraceBegin() {
+            if (!Trace.isTagEnabled(mTraceTag)) {
+                mTraceName = null;
+                return false;
+            }
+            if (mTraceName == null) {
+                mTraceName = "PerfSession-d" + displayId + "-" + reason;
+            }
+            Trace.asyncTraceForTrackBegin(mTraceTag, TAG, mTraceName,
+                    System.identityHashCode(this));
+            return true;
+        }
+
+        boolean asyncTraceEnd() {
+            if (mTraceName == null) {
+                return false;
+            }
+            Trace.asyncTraceForTrackEnd(mTraceTag, TAG, System.identityHashCode(this));
+            return true;
+        }
     }
 
     /**
@@ -119,14 +144,22 @@ public class SystemPerformanceHinter {
      */
     private class NoOpHighPerfSession extends HighPerfSession {
         public NoOpHighPerfSession() {
-            super(0 /* hintFlags */, -1 /* displayId */, "");
+            super(HINT_NO_OP, Display.INVALID_DISPLAY, "");
+        }
+
+        @Override
+        public void start() {
         }
 
+        @Override
         public void close() {
             // Do nothing
         }
     }
 
+    /** The tag category of trace. */
+    public long mTraceTag = Trace.TRACE_TAG_APP;
+
     // The active sessions
     private final ArrayList<HighPerfSession> mActiveSessions = new ArrayList<>();
     private final SurfaceControl.Transaction mTransaction;
@@ -134,7 +167,6 @@ public class SystemPerformanceHinter {
     private @Nullable PerformanceHintManager.Session mAdpfSession;
     private @Nullable DisplayRootProvider mDisplayRootProvider;
 
-
     /**
      * Constructor for the hinter.
      * @hide
@@ -167,12 +199,12 @@ public class SystemPerformanceHinter {
         mAdpfSession = adpfSession;
     }
 
-    /**
-     * Starts a session that requires high performance.
-     * @hide
-     */
-    public HighPerfSession startSession(@HintFlags int hintFlags, int displayId,
+    /** Creates a session that requires high performance. */
+    public HighPerfSession createSession(@HintFlags int hintFlags, int displayId,
             @NonNull String reason) {
+        if (hintFlags == HINT_NO_OP) {
+            throw new IllegalArgumentException("Not allow empty hint flags");
+        }
         if (mDisplayRootProvider == null && (hintFlags & HINT_SF_FRAME_RATE) != 0) {
             throw new IllegalArgumentException(
                     "Using SF frame rate hints requires a valid display root provider");
@@ -193,9 +225,20 @@ public class SystemPerformanceHinter {
     }
 
     /**
-     * Starts a session that requires high performance.
+     * Starts a new session that requires high performance.
      */
+    public HighPerfSession startSession(@HintFlags int hintFlags, int displayId,
+            @NonNull String reason) {
+        final HighPerfSession session = createSession(hintFlags, displayId, reason);
+        if (session.hintFlags != HINT_NO_OP) {
+            startSession(session);
+        }
+        return session;
+    }
+
+    /** Starts the session that requires high performance. */
     private void startSession(HighPerfSession session) {
+        final boolean isTraceEnabled = session.asyncTraceBegin();
         int oldGlobalFlags = calculateActiveHintFlags(HINT_GLOBAL);
         int oldPerDisplayFlags = calculateActiveHintFlagsForDisplay(HINT_PER_DISPLAY,
                 session.displayId);
@@ -217,19 +260,24 @@ public class SystemPerformanceHinter {
             mTransaction.setFrameRateCategory(
                     displaySurfaceControl, FRAME_RATE_CATEGORY_HIGH, /* smoothSwitchOnly= */ false);
             transactionChanged = true;
-            Trace.beginAsyncSection("PerfHint-framerate-" + session.displayId + "-"
-                    + session.reason, session.traceCookie);
+            if (isTraceEnabled) {
+                asyncTraceBegin(HINT_SF_FRAME_RATE, session.displayId);
+            }
         }
 
         // Global flags
         if (nowEnabled(oldGlobalFlags, newGlobalFlags, HINT_SF_EARLY_WAKEUP)) {
             mTransaction.setEarlyWakeupStart();
             transactionChanged = true;
-            Trace.beginAsyncSection("PerfHint-early_wakeup-" + session.reason, session.traceCookie);
+            if (isTraceEnabled) {
+                asyncTraceBegin(HINT_SF_EARLY_WAKEUP, Display.INVALID_DISPLAY);
+            }
         }
         if (nowEnabled(oldGlobalFlags, newGlobalFlags, HINT_ADPF)) {
             mAdpfSession.sendHint(PerformanceHintManager.Session.CPU_LOAD_UP);
-            Trace.beginAsyncSection("PerfHint-adpf-" + session.reason, session.traceCookie);
+            if (isTraceEnabled) {
+                asyncTraceBegin(HINT_ADPF, Display.INVALID_DISPLAY);
+            }
         }
         if (transactionChanged) {
             mTransaction.applyAsyncUnsafe();
@@ -240,6 +288,7 @@ public class SystemPerformanceHinter {
      * Ends a session that requires high performance.
      */
     private void endSession(HighPerfSession session) {
+        final boolean isTraceEnabled = session.asyncTraceEnd();
         int oldGlobalFlags = calculateActiveHintFlags(HINT_GLOBAL);
         int oldPerDisplayFlags = calculateActiveHintFlagsForDisplay(HINT_PER_DISPLAY,
                 session.displayId);
@@ -261,19 +310,24 @@ public class SystemPerformanceHinter {
             mTransaction.setFrameRateCategory(displaySurfaceControl, FRAME_RATE_CATEGORY_DEFAULT,
                     /* smoothSwitchOnly= */ false);
             transactionChanged = true;
-            Trace.endAsyncSection("PerfHint-framerate-" + session.displayId + "-" + session.reason,
-                    session.traceCookie);
+            if (isTraceEnabled) {
+                asyncTraceEnd(HINT_SF_FRAME_RATE);
+            }
         }
 
         // Global flags
         if (nowDisabled(oldGlobalFlags, newGlobalFlags, HINT_SF_EARLY_WAKEUP)) {
             mTransaction.setEarlyWakeupEnd();
             transactionChanged = true;
-            Trace.endAsyncSection("PerfHint-early_wakeup-" + session.reason, session.traceCookie);
+            if (isTraceEnabled) {
+                asyncTraceEnd(HINT_SF_EARLY_WAKEUP);
+            }
         }
         if (nowDisabled(oldGlobalFlags, newGlobalFlags, HINT_ADPF)) {
             mAdpfSession.sendHint(PerformanceHintManager.Session.CPU_LOAD_RESET);
-            Trace.endAsyncSection("PerfHint-adpf-" + session.reason, session.traceCookie);
+            if (isTraceEnabled) {
+                asyncTraceEnd(HINT_ADPF);
+            }
         }
         if (transactionChanged) {
             mTransaction.applyAsyncUnsafe();
@@ -323,6 +377,23 @@ public class SystemPerformanceHinter {
         return flags;
     }
 
+    private void asyncTraceBegin(@HintFlags int flag, int displayId) {
+        final String prefix = switch (flag) {
+            case HINT_SF_EARLY_WAKEUP -> "PerfHint-early_wakeup";
+            case HINT_SF_FRAME_RATE -> "PerfHint-framerate";
+            case HINT_ADPF -> "PerfHint-adpf";
+            default -> "PerfHint-" + flag;
+        };
+        final String name = displayId != Display.INVALID_DISPLAY
+                ? (prefix + "-d" + displayId) : prefix;
+        Trace.asyncTraceForTrackBegin(mTraceTag, TAG, name,
+                flag ^ System.identityHashCode(this));
+    }
+
+    private void asyncTraceEnd(@HintFlags int flag) {
+        Trace.asyncTraceForTrackEnd(mTraceTag, TAG, flag ^ System.identityHashCode(this));
+    }
+
     /**
      * Dumps the existing sessions.
      */
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index f31490060fc87..697402016f353 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -160,6 +160,7 @@ import static com.android.server.wm.WindowState.RESIZE_HANDLE_WIDTH_IN_DP;
 import static com.android.server.wm.WindowStateAnimator.READY_TO_SHOW;
 import static com.android.server.wm.utils.RegionUtils.forEachRectReverse;
 import static com.android.server.wm.utils.RegionUtils.rectListToRegion;
+import static com.android.window.flags.Flags.explicitRefreshRateHints;
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
@@ -245,6 +246,7 @@ import android.window.DisplayWindowPolicyController;
 import android.window.IDisplayAreaOrganizer;
 import android.window.ScreenCapture;
 import android.window.ScreenCapture.SynchronousScreenCaptureListener;
+import android.window.SystemPerformanceHinter;
 import android.window.TransitionRequestInfo;
 
 import com.android.internal.R;
@@ -571,6 +573,15 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
 
     private final SurfaceSession mSession = new SurfaceSession();
 
+    /**
+     * A perf hint session which will boost the refresh rate for the display and change sf duration
+     * to handle larger workloads.
+     */
+    private SystemPerformanceHinter.HighPerfSession mTransitionPrefSession;
+
+    /** A perf hint session which will boost the refresh rate. */
+    private SystemPerformanceHinter.HighPerfSession mHighFrameRateSession;
+
     /**
      * Window that is currently interacting with the user. This window is responsible for receiving
      * key events and pointer events from the user.
@@ -761,6 +772,9 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
      */
     boolean mDontMoveToTop;
 
+    /** Whether this display contains a WindowContainer with running SurfaceAnimator. */
+    boolean mLastContainsRunningSurfaceAnimator;
+
     /** Used for windows that want to keep the screen awake. */
     private PowerManager.WakeLock mHoldScreenWakeLock;
 
@@ -3447,6 +3461,42 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
         return dockFrame.bottom - imeFrame.top;
     }
 
+    void enableHighPerfTransition(boolean enable) {
+        if (!explicitRefreshRateHints()) {
+            if (enable) {
+                getPendingTransaction().setEarlyWakeupStart();
+            } else {
+                getPendingTransaction().setEarlyWakeupEnd();
+            }
+            return;
+        }
+        if (enable) {
+            if (mTransitionPrefSession == null) {
+                mTransitionPrefSession = mWmService.mSystemPerformanceHinter.createSession(
+                        SystemPerformanceHinter.HINT_SF, mDisplayId, "Transition");
+            }
+            mTransitionPrefSession.start();
+        } else if (mTransitionPrefSession != null) {
+            mTransitionPrefSession.close();
+        }
+    }
+
+    void enableHighFrameRate(boolean enable) {
+        if (!explicitRefreshRateHints()) {
+            // Done by RefreshRatePolicy.
+            return;
+        }
+        if (enable) {
+            if (mHighFrameRateSession == null) {
+                mHighFrameRateSession = mWmService.mSystemPerformanceHinter.createSession(
+                        SystemPerformanceHinter.HINT_SF_FRAME_RATE, mDisplayId, "WindowAnimation");
+            }
+            mHighFrameRateSession.start();
+        } else if (mHighFrameRateSession != null) {
+            mHighFrameRateSession.close();
+        }
+    }
+
     void rotateBounds(@Rotation int oldRotation, @Rotation int newRotation, Rect inOutBounds) {
         // Get display bounds on oldRotation as parent bounds for the rotation.
         getBounds(mTmpRect, oldRotation);
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index f6b4972de2509..9594c65ad5fc3 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -249,7 +249,6 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
 
     private boolean mIsSeamlessRotation = false;
     private IContainerFreezer mContainerFreezer = null;
-    private final SurfaceControl.Transaction mTmpTransaction = new SurfaceControl.Transaction();
 
     /**
      * {@code true} if some other operation may have caused the originally-recorded state (in
@@ -628,11 +627,7 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
 
         mLogger.mStartTimeNs = SystemClock.elapsedRealtimeNanos();
 
-        mController.updateAnimatingState(mTmpTransaction);
-        // merge into the next-time the global transaction is applied. This is too-early to set
-        // early-wake anyways, so we don't need to apply immediately (in fact applying right now
-        // can preempt more-important work).
-        SurfaceControl.mergeToGlobalTransaction(mTmpTransaction);
+        mController.updateAnimatingState();
     }
 
     /**
@@ -704,7 +699,11 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
         if (dc == null || mTargetDisplays.contains(dc)) return;
         mTargetDisplays.add(dc);
         addOnTopTasks(dc, mOnTopTasksStart);
-        mController.startPerfHintForDisplay(dc.mDisplayId);
+        // Handle the case {transition.start(); applyTransaction(wct);} that the animating state
+        // is set before collecting participants.
+        if (mController.isAnimating()) {
+            dc.enableHighPerfTransition(true);
+        }
     }
 
     /**
@@ -1407,8 +1406,7 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
                     false /* forceRelayout */);
         }
         cleanUpInternal();
-        mController.updateAnimatingState(mTmpTransaction);
-        mTmpTransaction.apply();
+        mController.updateAnimatingState();
 
         // Handle back animation if it's already started.
         mController.mAtm.mBackNavigationController.onTransitionFinish(mTargets, this);
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index 28c24c82e8305..c134464bc33a6 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -22,10 +22,8 @@ import static android.view.WindowManager.TRANSIT_CLOSE;
 import static android.view.WindowManager.TRANSIT_FLAG_IS_RECENTS;
 import static android.view.WindowManager.TRANSIT_NONE;
 import static android.view.WindowManager.TRANSIT_OPEN;
-import static android.window.SystemPerformanceHinter.HINT_SF;
 
 import static com.android.server.wm.ActivityTaskManagerService.POWER_MODE_REASON_CHANGE_DISPLAY;
-import static com.android.window.flags.Flags.explicitRefreshRateHints;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -41,18 +39,14 @@ import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.util.ArrayMap;
-import android.util.ArraySet;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.TimeUtils;
 import android.util.proto.ProtoOutputStream;
-import android.view.SurfaceControl;
 import android.view.WindowManager;
 import android.window.ITransitionMetricsReporter;
 import android.window.ITransitionPlayer;
 import android.window.RemoteTransition;
-import android.window.SystemPerformanceHinter;
-import android.window.SystemPerformanceHinter.HighPerfSession;
 import android.window.TransitionInfo;
 import android.window.TransitionRequestInfo;
 import android.window.WindowContainerTransaction;
@@ -131,7 +125,6 @@ class TransitionController {
     SnapshotController mSnapshotController;
     TransitionTracer mTransitionTracer;
 
-    private SystemPerformanceHinter mSystemPerformanceHinter;
     private boolean mFullReadyTracking = false;
 
     private final ArrayList<WindowManagerInternal.AppTransitionListener> mLegacyListeners =
@@ -185,24 +178,6 @@ class TransitionController {
 
     private final IBinder.DeathRecipient mTransitionPlayerDeath;
 
-    /**
-     * Tracks active perf sessions that boost frame rate and hint sf to increase its
-     * estimated work duration.
-     */
-    private final ArraySet<HighPerfSession> mHighPerfSessions = new ArraySet<>();
-
-
-    /**
-     * Starts a perf hint session which will boost the refresh rate for the display and change
-     * sf duration to handle larger workloads.
-     */
-    void startPerfHintForDisplay(int displayId) {
-        if (explicitRefreshRateHints()) {
-            mHighPerfSessions.add(mSystemPerformanceHinter.startSession(HINT_SF, displayId,
-                    "Transition collected"));
-        }
-    }
-
     static class QueuedTransition {
         final Transition mTransition;
         final OnStartCollect mOnStartCollect;
@@ -282,15 +257,9 @@ class TransitionController {
         mIsWaitingForDisplayEnabled = !wms.mDisplayEnabled;
         registerLegacyListener(wms.mActivityManagerAppTransitionNotifier);
         setSyncEngine(wms.mSyncEngine);
-        setSystemPerformanceHinter(wms.mSystemPerformanceHinter);
         mFullReadyTracking = Flags.transitReadyTracking();
     }
 
-    @VisibleForTesting
-    void setSystemPerformanceHinter(SystemPerformanceHinter hinter) {
-        mSystemPerformanceHinter = hinter;
-    }
-
     @VisibleForTesting
     void setSyncEngine(BLASTSyncEngine syncEngine) {
         mSyncEngine = syncEngine;
@@ -508,7 +477,7 @@ class TransitionController {
         return false;
     }
 
-    /** @return {@code true} if wc is in a participant subtree */
+    /** Returns {@code true} if the display contains a running or pending transition. */
     boolean isTransitionOnDisplay(@NonNull DisplayContent dc) {
         if (mCollectingTransition != null && mCollectingTransition.isOnDisplay(dc)) {
             return true;
@@ -1240,12 +1209,22 @@ class TransitionController {
         }
     }
 
-    void updateAnimatingState(SurfaceControl.Transaction t) {
+    /** Returns {@code true} if a transition is playing or the collecting transition is started. */
+    boolean isAnimating() {
+        return mAnimatingState;
+    }
+
+    void updateAnimatingState() {
         final boolean animatingState = !mPlayingTransitions.isEmpty()
                     || (mCollectingTransition != null && mCollectingTransition.isStarted());
         if (animatingState && !mAnimatingState) {
-            if (!explicitRefreshRateHints()) {
-                t.setEarlyWakeupStart();
+            // Note that Transition#start() can be called before adding participants, so the
+            // enableHighPerfTransition(true) is also called in Transition#recordDisplay.
+            for (int i = mAtm.mRootWindowContainer.getChildCount() - 1; i >= 0; i--) {
+                final DisplayContent dc = mAtm.mRootWindowContainer.getChildAt(i);
+                if (isTransitionOnDisplay(dc)) {
+                    dc.enableHighPerfTransition(true);
+                }
             }
             // Usually transitions put quite a load onto the system already (with all the things
             // happening in app), so pause task snapshot persisting to not increase the load.
@@ -1253,18 +1232,13 @@ class TransitionController {
             mAnimatingState = true;
             Transition.asyncTraceBegin("animating", 0x41bfaf1 /* hashcode of TAG */);
         } else if (!animatingState && mAnimatingState) {
-            if (!explicitRefreshRateHints()) {
-                t.setEarlyWakeupEnd();
+            for (int i = mAtm.mRootWindowContainer.getChildCount() - 1; i >= 0; i--) {
+                mAtm.mRootWindowContainer.getChildAt(i).enableHighPerfTransition(false);
             }
             mAtm.mWindowManager.scheduleAnimationLocked();
             mSnapshotController.setPause(false);
             mAnimatingState = false;
             Transition.asyncTraceEnd(0x41bfaf1 /* hashcode of TAG */);
-            // We close all perf sessions here when all transitions finish. The sessions are created
-            // when we collect transitions because we have access to the display id.
-            for (HighPerfSession perfSession : mHighPerfSessions) {
-                perfSession.close();
-            }
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index adc0595f305b7..e95d2651504b9 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -117,6 +117,9 @@ public class WindowAnimator {
         scheduleAnimation();
 
         final RootWindowContainer root = mService.mRoot;
+        final boolean useShellTransition = root.mTransitionController.isShellTransitionsEnabled();
+        final int animationFlags = useShellTransition ? CHILDREN : (TRANSITION | CHILDREN);
+        boolean rootAnimating = false;
         mCurrentTime = frameTimeNs / TimeUtils.NANOS_PER_MS;
         mBulkUpdateParams = 0;
         root.mOrientationChangeComplete = true;
@@ -149,6 +152,17 @@ public class WindowAnimator {
                     accessibilityController.drawMagnifiedRegionBorderIfNeeded(dc.mDisplayId,
                             mTransaction);
                 }
+
+                if (dc.isAnimating(animationFlags, ANIMATION_TYPE_ALL)) {
+                    rootAnimating = true;
+                    if (!dc.mLastContainsRunningSurfaceAnimator) {
+                        dc.mLastContainsRunningSurfaceAnimator = true;
+                        dc.enableHighFrameRate(true);
+                    }
+                } else if (dc.mLastContainsRunningSurfaceAnimator) {
+                    dc.mLastContainsRunningSurfaceAnimator = false;
+                    dc.enableHighFrameRate(false);
+                }
             }
 
             cancelAnimation();
@@ -168,8 +182,6 @@ public class WindowAnimator {
             mService.mWindowPlacerLocked.requestTraversal();
         }
 
-        final boolean rootAnimating = root.isAnimating(TRANSITION | CHILDREN /* flags */,
-                ANIMATION_TYPE_ALL /* typesToCheck */);
         if (rootAnimating && !mLastRootAnimating) {
             Trace.asyncTraceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "animating", 0);
         }
@@ -179,20 +191,10 @@ public class WindowAnimator {
         }
         mLastRootAnimating = rootAnimating;
 
-        final boolean runningExpensiveAnimations =
-                root.isAnimating(TRANSITION | CHILDREN /* flags */,
-                        ANIMATION_TYPE_APP_TRANSITION | ANIMATION_TYPE_SCREEN_ROTATION
-                                | ANIMATION_TYPE_RECENTS /* typesToCheck */);
-        if (runningExpensiveAnimations && !mRunningExpensiveAnimations) {
-            // Usually app transitions put quite a load onto the system already (with all the things
-            // happening in app), so pause snapshot persisting to not increase the load.
-            mService.mSnapshotController.setPause(true);
-            mTransaction.setEarlyWakeupStart();
-        } else if (!runningExpensiveAnimations && mRunningExpensiveAnimations) {
-            mService.mSnapshotController.setPause(false);
-            mTransaction.setEarlyWakeupEnd();
+        // APP_TRANSITION, SCREEN_ROTATION, TYPE_RECENTS are handled by shell transition.
+        if (!useShellTransition) {
+            updateRunningExpensiveAnimationsLegacy();
         }
-        mRunningExpensiveAnimations = runningExpensiveAnimations;
 
         SurfaceControl.mergeToGlobalTransaction(mTransaction);
         mService.closeSurfaceTransaction("WindowAnimator");
@@ -208,6 +210,21 @@ public class WindowAnimator {
         }
     }
 
+    private void updateRunningExpensiveAnimationsLegacy() {
+        final boolean runningExpensiveAnimations =
+                mService.mRoot.isAnimating(TRANSITION | CHILDREN /* flags */,
+                        ANIMATION_TYPE_APP_TRANSITION | ANIMATION_TYPE_SCREEN_ROTATION
+                                | ANIMATION_TYPE_RECENTS /* typesToCheck */);
+        if (runningExpensiveAnimations && !mRunningExpensiveAnimations) {
+            mService.mSnapshotController.setPause(true);
+            mTransaction.setEarlyWakeupStart();
+        } else if (!runningExpensiveAnimations && mRunningExpensiveAnimations) {
+            mService.mSnapshotController.setPause(false);
+            mTransaction.setEarlyWakeupEnd();
+        }
+        mRunningExpensiveAnimations = runningExpensiveAnimations;
+    }
+
     private static String bulkUpdateParamsToString(int bulkUpdateParams) {
         StringBuilder builder = new StringBuilder(128);
         if ((bulkUpdateParams & WindowSurfacePlacer.SET_UPDATE_ROTATION) != 0) {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 4a074ff25c741..a90e08e4f3725 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -1346,8 +1346,8 @@ public class WindowManagerService extends IWindowManager.Stub
                 DisplayContent dc = mRoot.getDisplayContent(displayId);
                 return (dc == null) ? null : dc.getSurfaceControl();
             }
-
         }, mTransactionFactory);
+        mSystemPerformanceHinter.mTraceTag = TRACE_TAG_WINDOW_MANAGER;
     }
 
     DisplayAreaPolicy.Provider getDisplayAreaPolicyProvider() {
@@ -7195,6 +7195,10 @@ public class WindowManagerService extends IWindowManager.Stub
                 pw.println(separator);
             }
             mConstants.dump(pw);
+            if (dumpAll) {
+                pw.println(separator);
+            }
+            mSystemPerformanceHinter.dump(pw, "");
         }
     }
 
diff --git a/services/tests/servicestests/utils/com/android/server/testutils/StubTransaction.java b/services/tests/servicestests/utils/com/android/server/testutils/StubTransaction.java
index 34e8ff2ec5c4f..b8dcecdbfb959 100644
--- a/services/tests/servicestests/utils/com/android/server/testutils/StubTransaction.java
+++ b/services/tests/servicestests/utils/com/android/server/testutils/StubTransaction.java
@@ -30,8 +30,6 @@ import android.view.InputWindowHandle;
 import android.view.Surface;
 import android.view.SurfaceControl;
 
-import com.android.server.testutils.StubTransaction;
-
 import java.util.HashSet;
 import java.util.concurrent.Executor;
 
@@ -261,6 +259,18 @@ public class StubTransaction extends SurfaceControl.Transaction {
         return this;
     }
 
+    @Override
+    public SurfaceControl.Transaction setFrameRateCategory(SurfaceControl sc, int category,
+            boolean smoothSwitchOnly) {
+        return this;
+    }
+
+    @Override
+    public SurfaceControl.Transaction setFrameRateSelectionStrategy(SurfaceControl sc,
+            int strategy) {
+        return this;
+    }
+
     @Override
     public SurfaceControl.Transaction unsetColor(SurfaceControl sc) {
         return this;
diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
index 47730237f675b..baf259461847c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -2438,41 +2438,36 @@ public class TransitionTests extends WindowTestsBase {
 
     @Test
     public void testTransitionsTriggerPerformanceHints() {
-        assumeTrue(explicitRefreshRateHints());
-        SystemPerformanceHinter systemPerformanceHinter = mock(SystemPerformanceHinter.class);
-        final TransitionController controller = new TestTransitionController(mAtm);
+        final boolean explicitRefreshRateHints = explicitRefreshRateHints();
+        final var session = new SystemPerformanceHinter.HighPerfSession[1];
+        if (explicitRefreshRateHints) {
+            final SystemPerformanceHinter perfHinter = mWm.mSystemPerformanceHinter;
+            spyOn(perfHinter);
+            doAnswer(invocation -> {
+                session[0] = (SystemPerformanceHinter.HighPerfSession) invocation.callRealMethod();
+                return session[0];
+            }).when(perfHinter).createSession(anyInt(), anyInt(), anyString());
+        }
+        final TransitionController controller = mDisplayContent.mTransitionController;
         final TestTransitionPlayer player = registerTestTransitionPlayer();
-
-        mSyncEngine = createTestBLASTSyncEngine();
-        controller.setSyncEngine(mSyncEngine);
-        controller.setSystemPerformanceHinter(systemPerformanceHinter);
-        SystemPerformanceHinter.HighPerfSession session = mock(
-                SystemPerformanceHinter.HighPerfSession.class);
-        doReturn(session).when(systemPerformanceHinter).startSession(anyInt(), anyInt(),
-                anyString());
-
+        final ActivityRecord app = new ActivityBuilder(mAtm).setCreateTask(true).build();
         final Transition transitA = createTestTransition(TRANSIT_OPEN, controller);
-        final Task task = createTask(mDisplayContent,
-                WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD);
-        final ActivityRecord act = createActivityRecord(task);
-        act.setVisibleRequested(true);
-        act.setVisible(true);
-
-        controller.startCollectOrQueue(transitA, (deferred) -> {
-        });
-        transitA.collect(act);
-
-        verify(systemPerformanceHinter).startSession(
-                eq(SystemPerformanceHinter.HINT_SF), anyInt(), eq("Transition collected"));
+        controller.moveToCollecting(transitA);
+        transitA.collectExistenceChange(app);
+        controller.requestStartTransition(transitA, app.getTask(),
+                null /* remoteTransition */, null /* displayChange */);
+        player.start();
 
-        transitA.start();
-        transitA.setAllReady();
+        verify(mDisplayContent).enableHighPerfTransition(true);
+        if (explicitRefreshRateHints) {
+            verify(session[0]).start();
+        }
 
-        // Aborting here doesn't abort the transition, it aborts the sync allowing the transition to
-        // finish successfully.
-        mSyncEngine.abort(transitA.getSyncId());
-        controller.finishTransition(transitA);
-        verify(session).close();
+        player.finish();
+        verify(mDisplayContent).enableHighPerfTransition(false);
+        if (explicitRefreshRateHints) {
+            verify(session[0]).close();
+        }
     }
 
     @Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
index a82459fc1baa0..55a00fc0ec89a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
@@ -293,6 +293,7 @@ public class WindowContainerTests extends WindowTestsBase {
     public void testRemoveImmediatelyClearsLeash() {
         final AnimationAdapter animAdapter = mock(AnimationAdapter.class);
         final WindowToken token = createTestWindowToken(TYPE_APPLICATION_OVERLAY, mDisplayContent);
+        mWm.mAnimator.ready();
         final SurfaceControl.Transaction t = token.getPendingTransaction();
         token.startAnimation(t, animAdapter, false /* hidden */,
                 SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION);
@@ -300,9 +301,14 @@ public class WindowContainerTests extends WindowTestsBase {
                 ArgumentCaptor.forClass(SurfaceControl.class);
         verify(animAdapter).startAnimation(leashCaptor.capture(), eq(t), anyInt(), any());
         assertTrue(token.mSurfaceAnimator.hasLeash());
+        waitUntilWindowAnimatorIdle();
+        verify(mDisplayContent).enableHighFrameRate(true);
+
         token.removeImmediately();
         assertFalse(token.mSurfaceAnimator.hasLeash());
         verify(t).remove(eq(leashCaptor.getValue()));
+        waitUntilWindowAnimatorIdle();
+        verify(mDisplayContent).enableHighFrameRate(false);
     }
 
     @Test
-- 
GitLab