AML: Do not merge launch events on different displays

Generally multi-display is visually independent, so the launch
events on different displays should be tracked separately.

Bug: 123355661
Bug: 135197913
Test: atest ActivityMetricsLaunchObserverTests# \
            testConsecutiveLaunchOnDifferentDisplay

Change-Id: I84e1e800654a440782ff9a85124449ce6d8078ba
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index c8357e2..1c010c7 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -543,9 +543,10 @@
             return;
         }
 
-        if (info != null) {
-            // If we are already in an existing transition, only update the activity name, but not
-            // the other attributes.
+        if (info != null
+                && info.mLastLaunchedActivity.mDisplayContent == launchedActivity.mDisplayContent) {
+            // If we are already in an existing transition on the same display, only update the
+            // activity name, but not the other attributes.
 
             if (DEBUG_METRICS) Slog.i(TAG, "notifyActivityLaunched update launched activity");
             // Coalesce multiple (trampoline) activities from a single sequence together.
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
index 12074dc3..3eee031 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
@@ -44,6 +44,7 @@
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.mockito.ArgumentMatcher;
 
 import java.util.Arrays;
@@ -57,6 +58,7 @@
  */
 @SmallTest
 @Presubmit
+@RunWith(WindowTestRunner.class)
 public class ActivityMetricsLaunchObserverTests extends ActivityTestsBase {
     private ActivityMetricsLogger mActivityMetricsLogger;
     private ActivityMetricsLogger.LaunchingState mLaunchingState;
@@ -116,11 +118,18 @@
         return argThat(new ActivityRecordMatcher(record));
     }
 
-    static <T> T verifyAsync(T mock) {
+    private <T> T verifyAsync(T mock) {
+        // With WindowTestRunner, all test methods are inside WM lock, so we have to unblock any
+        // messages that are waiting for the lock.
+        waitHandlerIdle(mService.mH);
         // AMLO callbacks happen on a separate thread than AML calls, so we need to use a timeout.
         return verify(mock, timeout(TimeUnit.SECONDS.toMillis(5)));
     }
 
+    private void verifyOnActivityLaunchFinished(ActivityRecord activity) {
+        verifyAsync(mLaunchObserver).onActivityLaunchFinished(eqProto(activity), anyLong());
+    }
+
     private void onIntentStarted(Intent intent) {
         notifyActivityLaunching(intent);
 
@@ -159,7 +168,7 @@
         notifyTransitionStarting(mTopActivity);
         notifyWindowsDrawn(mTopActivity);
 
-        verifyAsync(mLaunchObserver).onActivityLaunchFinished(eqProto(mTopActivity), anyLong());
+        verifyOnActivityLaunchFinished(mTopActivity);
         verifyNoMoreInteractions(mLaunchObserver);
     }
 
@@ -210,7 +219,7 @@
         notifyWindowsDrawn(mTopActivity);
 
         verifyAsync(mLaunchObserver).onReportFullyDrawn(eqProto(mTopActivity), anyLong());
-        verifyAsync(mLaunchObserver).onActivityLaunchFinished(eqProto(mTopActivity), anyLong());
+        verifyOnActivityLaunchFinished(mTopActivity);
         verifyNoMoreInteractions(mLaunchObserver);
     }
 
@@ -267,7 +276,7 @@
 
         notifyWindowsDrawn(mTopActivity);
 
-        verifyAsync(mLaunchObserver).onActivityLaunchFinished(eqProto(mTopActivity), anyLong());
+        verifyOnActivityLaunchFinished(mTopActivity);
         verifyNoMoreInteractions(mLaunchObserver);
     }
 
@@ -322,16 +331,40 @@
 
         assertWithMessage("Different callers should get 2 indepedent launching states")
                 .that(previousState).isNotEqualTo(mLaunchingState);
-
-        notifyTransitionStarting(otherActivity);
-        notifyWindowsDrawn(otherActivity);
-
-        verifyAsync(mLaunchObserver).onActivityLaunchFinished(eqProto(otherActivity), anyLong());
+        transitToDrawnAndVerifyOnLaunchFinished(otherActivity);
 
         // The first transition should still be valid.
-        notifyTransitionStarting(mTopActivity);
-        notifyWindowsDrawn(mTopActivity);
+        transitToDrawnAndVerifyOnLaunchFinished(mTopActivity);
+    }
 
-        verifyAsync(mLaunchObserver).onActivityLaunchFinished(eqProto(mTopActivity), anyLong());
+    @Test
+    public void testConsecutiveLaunchOnDifferentDisplay() {
+        onActivityLaunched(mTopActivity);
+
+        final ActivityStack stack = new StackBuilder(mRootActivityContainer)
+                .setDisplay(addNewDisplayContentAt(DisplayContent.POSITION_BOTTOM))
+                .setCreateActivity(false)
+                .build();
+        final ActivityRecord activityOnNewDisplay = new ActivityBuilder(mService)
+                .setStack(stack)
+                .setCreateTask(true)
+                .setProcessName("new")
+                .build();
+
+        // Before TopActivity is drawn, it launches another activity on a different display.
+        mActivityMetricsLogger.notifyActivityLaunching(activityOnNewDisplay.intent,
+                mTopActivity /* caller */);
+        notifyActivityLaunched(START_SUCCESS, activityOnNewDisplay);
+
+        // There should be 2 events instead of coalescing as one event.
+        transitToDrawnAndVerifyOnLaunchFinished(mTopActivity);
+        transitToDrawnAndVerifyOnLaunchFinished(activityOnNewDisplay);
+    }
+
+    private void transitToDrawnAndVerifyOnLaunchFinished(ActivityRecord activity) {
+        notifyTransitionStarting(activity);
+        notifyWindowsDrawn(activity);
+
+        verifyOnActivityLaunchFinished(activity);
     }
 }