AM: Report launch state with activity launch info
Bug: 119988524
Test: atest ActivityMetricsLoggerTests
Test: adb shell am start -W ...
Change-Id: I1d323e84d725722a198a60c51884dba897ec253f
diff --git a/core/java/android/app/WaitResult.java b/core/java/android/app/WaitResult.java
index 5baf2e2..ad9f680 100644
--- a/core/java/android/app/WaitResult.java
+++ b/core/java/android/app/WaitResult.java
@@ -16,11 +16,14 @@
package android.app;
+import android.annotation.IntDef;
import android.content.ComponentName;
import android.os.Parcel;
import android.os.Parcelable;
import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
/**
* Information returned after waiting for an activity start.
@@ -28,11 +31,43 @@
* @hide
*/
public class WaitResult implements Parcelable {
+
+ /**
+ * The state at which a launch sequence had started.
+ *
+ * @see <a href="https://developer.android.com/topic/performance/vitals/launch-time"App startup
+ * time</a>
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"LAUNCH_STATE_"}, value = {
+ LAUNCH_STATE_COLD,
+ LAUNCH_STATE_WARM,
+ LAUNCH_STATE_HOT
+ })
+ public @interface LaunchState {
+ }
+
+ /**
+ * Cold launch sequence: a new process has started.
+ */
+ public static final int LAUNCH_STATE_COLD = 1;
+
+ /**
+ * Warm launch sequence: process reused, but activity has to be created.
+ */
+ public static final int LAUNCH_STATE_WARM = 2;
+
+ /**
+ * Hot launch sequence: process reused, activity brought-to-top.
+ */
+ public static final int LAUNCH_STATE_HOT = 3;
+
public static final int INVALID_DELAY = -1;
public int result;
public boolean timeout;
public ComponentName who;
public long totalTime;
+ public @LaunchState int launchState;
public WaitResult() {
}
@@ -48,6 +83,7 @@
dest.writeInt(timeout ? 1 : 0);
ComponentName.writeToParcel(who, dest);
dest.writeLong(totalTime);
+ dest.writeInt(launchState);
}
public static final Parcelable.Creator<WaitResult> CREATOR
@@ -68,6 +104,7 @@
timeout = source.readInt() != 0;
who = ComponentName.readFromParcel(source);
totalTime = source.readLong();
+ launchState = source.readInt();
}
public void dump(PrintWriter pw, String prefix) {
@@ -76,5 +113,19 @@
pw.println(prefix + " timeout=" + timeout);
pw.println(prefix + " who=" + who);
pw.println(prefix + " totalTime=" + totalTime);
+ pw.println(prefix + " launchState=" + launchState);
+ }
+
+ public static String launchStateToString(@LaunchState int type) {
+ switch (type) {
+ case LAUNCH_STATE_COLD:
+ return "COLD";
+ case LAUNCH_STATE_WARM:
+ return "WARM";
+ case LAUNCH_STATE_HOT:
+ return "HOT";
+ default:
+ return "UNKNOWN (" + type + ")";
+ }
}
}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 67a4d14..6405932 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -20,6 +20,7 @@
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.ActivityTaskManager.RESIZE_MODE_SYSTEM;
import static android.app.ActivityTaskManager.RESIZE_MODE_USER;
+import static android.app.WaitResult.launchStateToString;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.view.Display.INVALID_DISPLAY;
@@ -491,6 +492,7 @@
final long endTime = SystemClock.uptimeMillis();
PrintWriter out = mWaitOption ? pw : getErrPrintWriter();
boolean launched = false;
+ boolean hotLaunch = false;
switch (res) {
case ActivityManager.START_SUCCESS:
launched = true;
@@ -516,6 +518,8 @@
break;
case ActivityManager.START_TASK_TO_FRONT:
launched = true;
+ //TODO(b/120981435) remove special case
+ hotLaunch = true;
out.println(
"Warning: Activity not started, its current "
+ "task has been brought to the front");
@@ -563,6 +567,9 @@
result.who = intent.getComponent();
}
pw.println("Status: " + (result.timeout ? "timeout" : "ok"));
+ final @WaitResult.LaunchState int launchState =
+ hotLaunch ? WaitResult.LAUNCH_STATE_HOT : result.launchState;
+ pw.println("LaunchState: " + launchStateToString(launchState));
if (result.who != null) {
pw.println("Activity: " + result.who.flattenToShortString());
}
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index 12690a9..1023182 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -3,6 +3,9 @@
import static android.app.ActivityManager.START_SUCCESS;
import static android.app.ActivityManager.START_TASK_TO_FRONT;
import static android.app.ActivityManager.processStateAmToProto;
+import static android.app.WaitResult.LAUNCH_STATE_COLD;
+import static android.app.WaitResult.LAUNCH_STATE_HOT;
+import static android.app.WaitResult.LAUNCH_STATE_WARM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
@@ -80,6 +83,7 @@
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_TIMEOUT;
+import android.app.WaitResult;
import android.app.WindowConfiguration.WindowingMode;
import android.content.Context;
import android.content.Intent;
@@ -101,10 +105,10 @@
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.os.BackgroundThread;
import com.android.internal.os.SomeArgs;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.server.LocalServices;
/**
@@ -259,6 +263,19 @@
activityRecordIdHashCode = System.identityHashCode(launchedActivity);
this.windowsFullyDrawnDelayMs = windowsFullyDrawnDelayMs;
}
+
+ @WaitResult.LaunchState int getLaunchState() {
+ switch (type) {
+ case TYPE_TRANSITION_WARM_LAUNCH:
+ return LAUNCH_STATE_WARM;
+ case TYPE_TRANSITION_HOT_LAUNCH:
+ return LAUNCH_STATE_HOT;
+ case TYPE_TRANSITION_COLD_LAUNCH:
+ return LAUNCH_STATE_COLD;
+ default:
+ return -1;
+ }
+ }
}
ActivityMetricsLogger(ActivityStackSupervisor supervisor, Context context, Looper looper) {
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index daf4b8b..793da12 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -122,8 +122,7 @@
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_FREE_RESIZE;
import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE;
-import static com.android.server.wm.ActivityTaskManagerService
- .RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
+import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
import static com.android.server.wm.IdentifierProto.HASH_CODE;
import static com.android.server.wm.IdentifierProto.TITLE;
import static com.android.server.wm.IdentifierProto.USER_ID;
@@ -145,6 +144,7 @@
import android.app.PendingIntent;
import android.app.PictureInPictureParams;
import android.app.ResultInfo;
+import android.app.WaitResult.LaunchState;
import android.app.servertransaction.ActivityConfigurationChangeItem;
import android.app.servertransaction.ActivityLifecycleItem;
import android.app.servertransaction.ActivityRelaunchItem;
@@ -2158,7 +2158,7 @@
.getActivityMetricsLogger().logAppTransitionReportedDrawn(this, restoredFromBundle);
if (info != null) {
mStackSupervisor.reportActivityLaunchedLocked(false /* timeout */, this,
- info.windowsFullyDrawnDelayMs);
+ info.windowsFullyDrawnDelayMs, info.getLaunchState());
}
}
@@ -2182,8 +2182,9 @@
final WindowingModeTransitionInfoSnapshot info = mStackSupervisor
.getActivityMetricsLogger().notifyWindowsDrawn(getWindowingMode(), timestamp);
final int windowsDrawnDelayMs = info != null ? info.windowsDrawnDelayMs : INVALID_DELAY;
+ final @LaunchState int launchState = info != null ? info.getLaunchState() : -1;
mStackSupervisor.reportActivityLaunchedLocked(false /* timeout */, this,
- windowsDrawnDelayMs);
+ windowsDrawnDelayMs, launchState);
mStackSupervisor.sendWaitingVisibleReportLocked(this);
finishLaunchTickingLocked();
if (task != null) {
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index f3c5630..aad2b49 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -598,7 +598,8 @@
}
}
- void reportActivityLaunchedLocked(boolean timeout, ActivityRecord r, long totalTime) {
+ void reportActivityLaunchedLocked(boolean timeout, ActivityRecord r, long totalTime,
+ @WaitResult.LaunchState int launchState) {
boolean changed = false;
for (int i = mWaitingActivityLaunched.size() - 1; i >= 0; i--) {
WaitResult w = mWaitingActivityLaunched.remove(i);
@@ -609,6 +610,7 @@
w.who = new ComponentName(r.info.packageName, r.info.name);
}
w.totalTime = totalTime;
+ w.launchState = launchState;
// Do not modify w.result.
}
}
@@ -1242,7 +1244,8 @@
mHandler.removeMessages(IDLE_TIMEOUT_MSG, r);
r.finishLaunchTickingLocked();
if (fromTimeout) {
- reportActivityLaunchedLocked(fromTimeout, r, INVALID_DELAY);
+ reportActivityLaunchedLocked(fromTimeout, r, INVALID_DELAY,
+ -1 /* launchState */);
}
// This is a hack to semi-deal with a race condition
diff --git a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
index eed8ae7..5ea8ff1 100644
--- a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
+++ b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
@@ -39,7 +39,6 @@
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
-import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
@@ -840,6 +839,7 @@
/* SAMPLE OUTPUT : Cold launch
Starting: Intent { cmp=com.google.android.calculator/com.android.calculator2.Calculator }
Status: ok
+ LaunchState: COLD
Activity: com.google.android.calculator/com.android.calculator2.Calculator
TotalTime: 357
WaitTime: 377
@@ -848,6 +848,7 @@
Starting: Intent { cmp=com.google.android.calculator/com.android.calculator2.Calculator }
Warning: Activity not started, its current task has been brought to the front
Status: ok
+ LaunchState: HOT
Activity: com.google.android.calculator/com.android.calculator2.CalculatorGoogle
TotalTime: 60
WaitTime: 67