Make sure instance launch trace for an id only appears once
A consecutive launch may contain multiple activities with different
windowing modes or target displays. The separated transition should
not end the trace of the initial launch.
e.g. start A---------------------->A drawn
|-A starts B-->B drawn (on a different display)
There will still be 2 traces "launching: A" and "launching: B".
But there should be only one trace "launchingActivity" which is
ended by "A drawn".
Bug: 231612200
Test: atest ActivityMetricsLaunchObserverTests# \
testConsecutiveLaunchOnDifferentDisplay
Change-Id: Ied39e000ef6c8c60b56e228cbdc86b6f9dbe2e6c
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index f2bcd1d..d6c0ab6 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -192,11 +192,10 @@
/** The sequence id for trace. It is used to map the traces before resolving intent. */
private static int sTraceSeqId;
/** The trace format is "launchingActivity#$seqId:$state(:$packageName)". */
- final String mTraceName;
+ String mTraceName;
LaunchingState() {
if (!Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
- mTraceName = null;
return;
}
// Use an id because the launching app is not yet known before resolving intent.
@@ -205,8 +204,14 @@
Trace.asyncTraceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, mTraceName, 0);
}
- void stopTrace(boolean abort) {
+ void stopTrace(boolean abort, TransitionInfo endInfo) {
if (mTraceName == null) return;
+ if (!abort && endInfo != mAssociatedTransitionInfo) {
+ // Multiple TransitionInfo can be associated with the same LaunchingState (e.g. a
+ // launching activity launches another activity in a different windowing mode or
+ // display). Only the original associated info can emit a "completed" trace.
+ return;
+ }
Trace.asyncTraceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER, mTraceName, 0);
final String launchResult;
if (mAssociatedTransitionInfo == null) {
@@ -218,6 +223,7 @@
}
// Put a supplement trace as the description of the async trace with the same id.
Trace.instant(Trace.TRACE_TAG_ACTIVITY_MANAGER, mTraceName + launchResult);
+ mTraceName = null;
}
@VisibleForTesting
@@ -321,7 +327,11 @@
mProcessSwitch = processSwitch;
mTransitionDeviceUptimeMs = launchingState.mCurrentUpTimeMs;
setLatestLaunchedActivity(r);
- launchingState.mAssociatedTransitionInfo = this;
+ // The launching state can be reused by consecutive launch. Its original association
+ // shouldn't be changed by a separated transition.
+ if (launchingState.mAssociatedTransitionInfo == null) {
+ launchingState.mAssociatedTransitionInfo = this;
+ }
if (options != null) {
final SourceInfo sourceInfo = options.getSourceInfo();
if (sourceInfo != null) {
@@ -908,7 +918,7 @@
return;
}
if (DEBUG_METRICS) Slog.i(TAG, "abort launch cause=" + cause);
- state.stopTrace(true /* abort */);
+ state.stopTrace(true /* abort */, null /* endInfo */);
launchObserverNotifyIntentFailed(state.mCurrentTransitionStartTimeNs);
}
@@ -924,7 +934,7 @@
Slog.i(TAG, "done abort=" + abort + " cause=" + cause + " timestamp=" + timestampNs
+ " info=" + info);
}
- info.mLaunchingState.stopTrace(abort);
+ info.mLaunchingState.stopTrace(abort, info);
stopLaunchTrace(info);
final Boolean isHibernating =
mLastHibernationStates.remove(info.mLastLaunchedActivity.packageName);
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 2fea228..5b909a3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
@@ -532,6 +532,9 @@
transitToDrawnAndVerifyOnLaunchFinished(mTopActivity);
setLastExpectedStartedId(activityOnNewDisplay);
transitToDrawnAndVerifyOnLaunchFinished(activityOnNewDisplay);
+
+ assertWithMessage("The launching state must not include the separated launch")
+ .that(mLaunchingState.contains(activityOnNewDisplay)).isFalse();
}
@Test