Merge "Use LatencyTracker to log the time of recents animation"
diff --git a/core/java/com/android/internal/util/LatencyTracker.java b/core/java/com/android/internal/util/LatencyTracker.java
index 8012540..254c299 100644
--- a/core/java/com/android/internal/util/LatencyTracker.java
+++ b/core/java/com/android/internal/util/LatencyTracker.java
@@ -86,6 +86,11 @@
      */
     public static final int ACTION_FACE_WAKE_AND_UNLOCK = 7;
 
+    /**
+     * Time between the swipe-up gesture and window drawn of recents activity.
+     */
+    public static final int ACTION_START_RECENTS_ANIMATION = 8;
+
     private static final String[] NAMES = new String[]{
             "expand panel",
             "toggle recents",
@@ -94,7 +99,9 @@
             "check credential unlocked",
             "turn on screen",
             "rotate the screen",
-            "face wake-and-unlock"};
+            "face wake-and-unlock",
+            "start recents-animation",
+    };
 
     private static final int[] STATSD_ACTION = new int[]{
             FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_EXPAND_PANEL,
@@ -105,6 +112,7 @@
             FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_TURN_ON_SCREEN,
             FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_ROTATE_SCREEN,
             FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_FACE_WAKE_AND_UNLOCK,
+            FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_START_RECENTS_ANIMATION,
     };
 
     private static LatencyTracker sLatencyTracker;
@@ -170,6 +178,8 @@
                 return "ACTION_ROTATE_SCREEN";
             case 8:
                 return "ACTION_FACE_WAKE_AND_UNLOCK";
+            case 9:
+                return "ACTION_START_RECENTS_ANIMATION";
             default:
                 throw new IllegalArgumentException("Invalid action");
         }
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index 6a50b79..b084787 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -631,6 +631,10 @@
         if (info.mLoggedTransitionStarting && info.allDrawn()) {
             done(false /* abort */, info, "notifyWindowsDrawn - all windows drawn", timestampNs);
         }
+        if (r.mWmService.isRecentsAnimationTarget(r)) {
+            r.mWmService.getRecentsAnimationController().logRecentsAnimationStartTime(
+                    info.mSourceEventDelayMs + info.mWindowsDrawnDelayMs);
+        }
         return infoSnapshot;
     }
 
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index 25670af..edd01eb 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -57,7 +57,9 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.inputmethod.SoftInputShowHideReason;
+import com.android.internal.os.BackgroundThread;
 import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.util.LatencyTracker;
 import com.android.internal.util.function.pooled.PooledConsumer;
 import com.android.internal.util.function.pooled.PooledFunction;
 import com.android.internal.util.function.pooled.PooledLambda;
@@ -84,6 +86,11 @@
 public class RecentsAnimationController implements DeathRecipient {
     private static final String TAG = RecentsAnimationController.class.getSimpleName();
     private static final long FAILSAFE_DELAY = 1000;
+    /**
+     * If the recents animation is canceled before the delay since the window drawn, do not log the
+     * action because the duration is too small that may be just a mistouch,
+     */
+    private static final long LATENCY_TRACKER_LOG_DELAY_MS = 300;
 
     public static final int REORDER_KEEP_IN_PLACE = 0;
     public static final int REORDER_MOVE_TO_TOP = 1;
@@ -123,7 +130,7 @@
     private boolean mPendingStart = true;
 
     // Set when the animation has been canceled
-    private boolean mCanceled;
+    private volatile boolean mCanceled;
 
     // Whether or not the input consumer is enabled. The input consumer must be both registered and
     // enabled for it to start intercepting touch events.
@@ -595,6 +602,15 @@
         return adapter.createRemoteAnimationTarget();
     }
 
+    void logRecentsAnimationStartTime(int durationMs) {
+        BackgroundThread.getHandler().postDelayed(() -> {
+            if (!mCanceled) {
+                mService.mLatencyTracker.logAction(LatencyTracker.ACTION_START_RECENTS_ANIMATION,
+                        durationMs);
+            }
+        }, LATENCY_TRACKER_LOG_DELAY_MS);
+    }
+
     private boolean removeTaskInternal(int taskId) {
         boolean result = false;
         for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 617b21c..6f10edf 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -1040,7 +1040,7 @@
 
     private WindowContentFrameStats mTempWindowRenderStats;
 
-    private final LatencyTracker mLatencyTracker;
+    final LatencyTracker mLatencyTracker;
 
     /**
      * Whether the UI is currently running in touch mode (not showing
@@ -1124,18 +1124,16 @@
             // While running a recents animation, this will get called early because we show the
             // recents animation target activity immediately when the animation starts. Defer the
             // mLaunchTaskBehind updates until recents animation finishes.
-            final boolean isRecentsAnimationTarget = getRecentsAnimationController() != null
-                    && getRecentsAnimationController().isTargetApp(atoken);
-            if (atoken.mLaunchTaskBehind && !isRecentsAnimationTarget) {
+            if (atoken.mLaunchTaskBehind && !isRecentsAnimationTarget(atoken)) {
                 mAtmService.mTaskSupervisor.scheduleLaunchTaskBehindComplete(atoken.token);
                 atoken.mLaunchTaskBehind = false;
             } else {
                 atoken.updateReportedVisibilityLocked();
                 // We should also defer sending the finished callback until the recents animation
                 // successfully finishes.
-                if (atoken.mEnteringAnimation && !isRecentsAnimationTarget) {
+                if (atoken.mEnteringAnimation && !isRecentsAnimationTarget(atoken)) {
                     atoken.mEnteringAnimation = false;
-                    if (atoken != null && atoken.attachedToProcess()) {
+                    if (atoken.attachedToProcess()) {
                         try {
                             atoken.app.getThread().scheduleEnterAnimationComplete(atoken.appToken);
                         } catch (RemoteException e) {
@@ -2972,6 +2970,10 @@
         }
     }
 
+    boolean isRecentsAnimationTarget(ActivityRecord r) {
+        return mRecentsAnimationController != null && mRecentsAnimationController.isTargetApp(r);
+    }
+
     void setWindowOpaqueLocked(IBinder token, boolean isOpaque) {
         final ActivityRecord wtoken = mRoot.getActivityRecord(token);
         if (wtoken != null) {