summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmds/statsd/src/atoms.proto2
-rw-r--r--core/java/android/app/WaitResult.java18
-rw-r--r--services/core/java/com/android/server/wm/ActivityMetricsLogger.java36
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java14
-rw-r--r--services/core/java/com/android/server/wm/ActivityStarter.java4
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskSupervisor.java9
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java46
7 files changed, 91 insertions, 38 deletions
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index dbb3ecd0bc36..5affd6a2dcb3 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -3764,6 +3764,7 @@ message AppStartOccurred {
WARM = 1;
HOT = 2;
COLD = 3;
+ RELAUNCH = 4;
}
// The transition type.
optional TransitionType type = 3;
@@ -3825,6 +3826,7 @@ message AppStartCanceled {
WARM = 1;
HOT = 2;
COLD = 3;
+ RELAUNCH = 4;
}
// The transition type.
optional TransitionType type = 3;
diff --git a/core/java/android/app/WaitResult.java b/core/java/android/app/WaitResult.java
index d65be9bded4f..77891e0fd007 100644
--- a/core/java/android/app/WaitResult.java
+++ b/core/java/android/app/WaitResult.java
@@ -40,14 +40,21 @@ public class WaitResult implements Parcelable {
*/
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = {"LAUNCH_STATE_"}, value = {
+ LAUNCH_STATE_UNKNOWN,
LAUNCH_STATE_COLD,
LAUNCH_STATE_WARM,
- LAUNCH_STATE_HOT
+ LAUNCH_STATE_HOT,
+ LAUNCH_STATE_RELAUNCH
})
public @interface LaunchState {
}
/**
+ * Not considered as a launch event, e.g. the activity is already on top.
+ */
+ public static final int LAUNCH_STATE_UNKNOWN = 0;
+
+ /**
* Cold launch sequence: a new process has started.
*/
public static final int LAUNCH_STATE_COLD = 1;
@@ -62,6 +69,13 @@ public class WaitResult implements Parcelable {
*/
public static final int LAUNCH_STATE_HOT = 3;
+ /**
+ * Relaunch launch sequence: process reused, but activity has to be destroyed and created.
+ * E.g. the current device configuration is different from the background activity that will be
+ * brought to foreground, and the activity doesn't declare to handle the change.
+ */
+ public static final int LAUNCH_STATE_RELAUNCH = 4;
+
public static final int INVALID_DELAY = -1;
public int result;
public boolean timeout;
@@ -124,6 +138,8 @@ public class WaitResult implements Parcelable {
return "WARM";
case LAUNCH_STATE_HOT:
return "HOT";
+ case LAUNCH_STATE_RELAUNCH:
+ return "RELAUNCH";
default:
return "UNKNOWN (" + type + ")";
}
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index d8d1a6563675..b4ca7c5f6ff1 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -3,8 +3,10 @@ package com.android.server.wm;
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.INVALID_DELAY;
import static android.app.WaitResult.LAUNCH_STATE_COLD;
import static android.app.WaitResult.LAUNCH_STATE_HOT;
+import static android.app.WaitResult.LAUNCH_STATE_RELAUNCH;
import static android.app.WaitResult.LAUNCH_STATE_WARM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
@@ -130,7 +132,6 @@ class ActivityMetricsLogger {
* transition, in the case the launch is standalone (e.g. from recents).
*/
private static final int IGNORE_CALLER = -1;
- private static final int INVALID_DELAY = -1;
// Preallocated strings we are sending to tron, so we don't have to allocate a new one every
// time we log.
@@ -220,6 +221,8 @@ class ActivityMetricsLogger {
boolean mLoggedStartingWindowDrawn;
/** If the any app transitions have been logged as starting. */
boolean mLoggedTransitionStarting;
+ /** Whether any activity belonging to this transition has relaunched. */
+ boolean mRelaunched;
/** Non-null if the application has reported drawn but its window hasn't. */
@Nullable Runnable mPendingFullyDrawn;
@@ -351,6 +354,7 @@ class ActivityMetricsLogger {
*/
final int windowsFullyDrawnDelayMs;
final int activityRecordIdHashCode;
+ final boolean relaunched;
private TransitionInfoSnapshot(TransitionInfo info) {
this(info, info.mLastLaunchedActivity, INVALID_DELAY);
@@ -379,6 +383,7 @@ class ActivityMetricsLogger {
launchedActivityShortComponentName = launchedActivity.shortComponentName;
activityRecordIdHashCode = System.identityHashCode(launchedActivity);
this.windowsFullyDrawnDelayMs = windowsFullyDrawnDelayMs;
+ relaunched = info.mRelaunched;
}
@WaitResult.LaunchState int getLaunchState() {
@@ -386,7 +391,7 @@ class ActivityMetricsLogger {
case TYPE_TRANSITION_WARM_LAUNCH:
return LAUNCH_STATE_WARM;
case TYPE_TRANSITION_HOT_LAUNCH:
- return LAUNCH_STATE_HOT;
+ return relaunched ? LAUNCH_STATE_RELAUNCH : LAUNCH_STATE_HOT;
case TYPE_TRANSITION_COLD_LAUNCH:
return LAUNCH_STATE_COLD;
default:
@@ -673,6 +678,13 @@ class ActivityMetricsLogger {
}
}
+ void notifyActivityRelaunched(ActivityRecord r) {
+ final TransitionInfo info = getActiveTransitionInfo(r);
+ if (info != null) {
+ info.mRelaunched = true;
+ }
+ }
+
/** Makes sure that the reference to the removed activity is cleared. */
void notifyActivityRemoved(@NonNull ActivityRecord r) {
mLastTransitionInfo.remove(r);
@@ -800,13 +812,13 @@ class ActivityMetricsLogger {
FrameworkStatsLog.APP_START_CANCELED,
activity.info.applicationInfo.uid,
activity.packageName,
- convertAppStartTransitionType(type),
+ getAppStartTransitionType(type, info.mRelaunched),
activity.info.name);
if (DEBUG_METRICS) {
Slog.i(TAG, String.format("APP_START_CANCELED(%s, %s, %s, %s)",
activity.info.applicationInfo.uid,
activity.packageName,
- convertAppStartTransitionType(type),
+ getAppStartTransitionType(type, info.mRelaunched),
activity.info.name));
}
}
@@ -871,7 +883,7 @@ class ActivityMetricsLogger {
FrameworkStatsLog.APP_START_OCCURRED,
info.applicationInfo.uid,
info.packageName,
- convertAppStartTransitionType(info.type),
+ getAppStartTransitionType(info.type, info.relaunched),
info.launchedActivityName,
info.launchedActivityLaunchedFromPackage,
isInstantApp,
@@ -891,7 +903,7 @@ class ActivityMetricsLogger {
Slog.i(TAG, String.format("APP_START_OCCURRED(%s, %s, %s, %s, %s)",
info.applicationInfo.uid,
info.packageName,
- convertAppStartTransitionType(info.type),
+ getAppStartTransitionType(info.type, info.relaunched),
info.launchedActivityName,
info.launchedActivityLaunchedFromPackage));
}
@@ -918,7 +930,7 @@ class ActivityMetricsLogger {
Log.i(TAG, sb.toString());
}
- private int convertAppStartTransitionType(int tronType) {
+ private static int getAppStartTransitionType(int tronType, boolean relaunched) {
if (tronType == TYPE_TRANSITION_COLD_LAUNCH) {
return FrameworkStatsLog.APP_START_OCCURRED__TYPE__COLD;
}
@@ -926,17 +938,13 @@ class ActivityMetricsLogger {
return FrameworkStatsLog.APP_START_OCCURRED__TYPE__WARM;
}
if (tronType == TYPE_TRANSITION_HOT_LAUNCH) {
- return FrameworkStatsLog.APP_START_OCCURRED__TYPE__HOT;
+ return relaunched
+ ? FrameworkStatsLog.APP_START_OCCURRED__TYPE__RELAUNCH
+ : FrameworkStatsLog.APP_START_OCCURRED__TYPE__HOT;
}
return FrameworkStatsLog.APP_START_OCCURRED__TYPE__UNKNOWN;
}
- /** @return the last known window drawn delay of the given activity. */
- int getLastDrawnDelayMs(ActivityRecord r) {
- final TransitionInfo info = mLastTransitionInfo.get(r);
- return info != null ? info.mWindowsDrawnDelayMs : INVALID_DELAY;
- }
-
/** @see android.app.Activity#reportFullyDrawn */
TransitionInfoSnapshot logAppTransitionReportedDrawn(ActivityRecord r,
boolean restoredFromBundle) {
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 22ebf306c474..875eba11fbcc 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -226,7 +226,7 @@ import android.app.ActivityOptions;
import android.app.PendingIntent;
import android.app.PictureInPictureParams;
import android.app.ResultInfo;
-import android.app.WaitResult.LaunchState;
+import android.app.WaitResult;
import android.app.servertransaction.ActivityConfigurationChangeItem;
import android.app.servertransaction.ActivityLifecycleItem;
import android.app.servertransaction.ActivityRelaunchItem;
@@ -3131,6 +3131,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
void finishRelaunching() {
+ mTaskSupervisor.getActivityMetricsLogger().notifyActivityRelaunched(this);
unfreezeBounds();
if (mPendingRelaunchCount > 0) {
@@ -5436,14 +5437,15 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
.getActivityMetricsLogger().notifyWindowsDrawn(this, timestampNs);
final boolean validInfo = info != null;
final int windowsDrawnDelayMs = validInfo ? info.windowsDrawnDelayMs : INVALID_DELAY;
- final @LaunchState int launchState = validInfo ? info.getLaunchState() : -1;
+ final @WaitResult.LaunchState int launchState =
+ validInfo ? info.getLaunchState() : WaitResult.LAUNCH_STATE_UNKNOWN;
// The activity may have been requested to be invisible (another activity has been launched)
// so there is no valid info. But if it is the current top activity (e.g. sleeping), the
// invalid state is still reported to make sure the waiting result is notified.
if (validInfo || this == getDisplayArea().topRunningActivity()) {
mTaskSupervisor.reportActivityLaunchedLocked(false /* timeout */, this,
windowsDrawnDelayMs, launchState);
- mTaskSupervisor.stopWaitingForActivityVisible(this, windowsDrawnDelayMs);
+ mTaskSupervisor.stopWaitingForActivityVisible(this, windowsDrawnDelayMs, launchState);
}
finishLaunchTickingLocked();
if (task != null) {
@@ -7156,11 +7158,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
} else {
mRelaunchReason = RELAUNCH_REASON_NONE;
}
- if (!attachedToProcess()) {
- ProtoLog.v(WM_DEBUG_CONFIGURATION,
- "Config is destroying non-running %s", this);
- destroyImmediately("config");
- } else if (mState == PAUSING) {
+ if (mState == PAUSING) {
// A little annoying: we are waiting for this activity to finish pausing. Let's not
// do anything now, but just flag that it needs to be restarted when done pausing.
ProtoLog.v(WM_DEBUG_CONFIGURATION,
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index f472a5dd7ba9..5208fd5dac3a 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -28,8 +28,6 @@ import static android.app.ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION;
import static android.app.ActivityManager.START_SUCCESS;
import static android.app.ActivityManager.START_TASK_TO_FRONT;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
-import static android.app.WaitResult.LAUNCH_STATE_COLD;
-import static android.app.WaitResult.LAUNCH_STATE_HOT;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
@@ -819,8 +817,6 @@ class ActivityStarter {
break;
}
case START_TASK_TO_FRONT: {
- mRequest.waitResult.launchState =
- r.attachedToProcess() ? LAUNCH_STATE_HOT : LAUNCH_STATE_COLD;
// ActivityRecord may represent a different activity, but it should not be
// in the resumed state.
if (r.nowVisible && r.isState(RESUMED)) {
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index e91a6d8e2439..01d77d5b6cc8 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -553,14 +553,16 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
// down to the max limit while they are still waiting to finish.
mFinishingActivities.remove(r);
- stopWaitingForActivityVisible(r, WaitResult.INVALID_DELAY);
+ stopWaitingForActivityVisible(r);
}
+ /** There is no valid launch time, just stop waiting. */
void stopWaitingForActivityVisible(ActivityRecord r) {
- stopWaitingForActivityVisible(r, getActivityMetricsLogger().getLastDrawnDelayMs(r));
+ stopWaitingForActivityVisible(r, WaitResult.INVALID_DELAY, WaitResult.LAUNCH_STATE_UNKNOWN);
}
- void stopWaitingForActivityVisible(ActivityRecord r, long totalTime) {
+ void stopWaitingForActivityVisible(ActivityRecord r, long totalTime,
+ @WaitResult.LaunchState int launchState) {
boolean changed = false;
for (int i = mWaitingForActivityVisible.size() - 1; i >= 0; --i) {
final WaitInfo w = mWaitingForActivityVisible.get(i);
@@ -570,6 +572,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
result.timeout = false;
result.who = w.getComponent();
result.totalTime = totalTime;
+ result.launchState = launchState;
mWaitingForActivityVisible.remove(w);
}
}
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 338ed066e1e4..f1dc098fccf6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
@@ -31,6 +31,7 @@ import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.timeout;
import android.app.ActivityOptions;
@@ -53,6 +54,7 @@ import org.mockito.ArgumentMatcher;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;
+import java.util.function.ToIntFunction;
/**
* Tests for the {@link ActivityMetricsLaunchObserver} class.
@@ -158,6 +160,41 @@ public class ActivityMetricsLaunchObserverTests extends WindowTestsBase {
verifyNoMoreInteractions(mLaunchObserver);
}
+ @Test
+ public void testLaunchState() {
+ final ToIntFunction<Boolean> launchTemplate = doRelaunch -> {
+ clearInvocations(mLaunchObserver);
+ onActivityLaunched(mTopActivity);
+ notifyTransitionStarting(mTopActivity);
+ if (doRelaunch) {
+ mActivityMetricsLogger.notifyActivityRelaunched(mTopActivity);
+ }
+ final ActivityMetricsLogger.TransitionInfoSnapshot info =
+ notifyWindowsDrawn(mTopActivity);
+ verifyOnActivityLaunchFinished(mTopActivity);
+ return info.getLaunchState();
+ };
+
+ final WindowProcessController app = mTopActivity.app;
+ // Assume that the process is started (ActivityBuilder has mocked the returned value of
+ // ATMS#getProcessController) but the activity has not attached process.
+ mTopActivity.app = null;
+ assertWithMessage("Warm launch").that(launchTemplate.applyAsInt(false /* doRelaunch */))
+ .isEqualTo(WaitResult.LAUNCH_STATE_WARM);
+
+ mTopActivity.app = app;
+ assertWithMessage("Hot launch").that(launchTemplate.applyAsInt(false /* doRelaunch */))
+ .isEqualTo(WaitResult.LAUNCH_STATE_HOT);
+
+ assertWithMessage("Relaunch").that(launchTemplate.applyAsInt(true /* doRelaunch */))
+ .isEqualTo(WaitResult.LAUNCH_STATE_RELAUNCH);
+
+ mTopActivity.app = null;
+ doReturn(null).when(mAtm).getProcessController(app.mName, app.mUid);
+ assertWithMessage("Cold launch").that(launchTemplate.applyAsInt(false /* doRelaunch */))
+ .isEqualTo(WaitResult.LAUNCH_STATE_COLD);
+ }
+
private void onActivityLaunched(ActivityRecord activity) {
onIntentStarted(activity.intent);
notifyActivityLaunched(START_SUCCESS, activity);
@@ -168,15 +205,10 @@ public class ActivityMetricsLaunchObserverTests extends WindowTestsBase {
@Test
public void testOnActivityLaunchFinished() {
- // Assume that the process is started (ActivityBuilder has mocked the returned value of
- // ATMS#getProcessController) but the activity has not attached process.
- mTopActivity.app = null;
onActivityLaunched(mTopActivity);
notifyTransitionStarting(mTopActivity);
- final ActivityMetricsLogger.TransitionInfoSnapshot info = notifyWindowsDrawn(mTopActivity);
- assertWithMessage("Warm launch").that(info.getLaunchState())
- .isEqualTo(WaitResult.LAUNCH_STATE_WARM);
+ notifyWindowsDrawn(mTopActivity);
verifyOnActivityLaunchFinished(mTopActivity);
verifyNoMoreInteractions(mLaunchObserver);
@@ -231,8 +263,6 @@ public class ActivityMetricsLaunchObserverTests extends WindowTestsBase {
assertWithMessage("Record start source").that(info.sourceType)
.isEqualTo(SourceInfo.TYPE_LAUNCHER);
assertWithMessage("Record event time").that(info.sourceEventDelayMs).isAtLeast(10);
- assertWithMessage("Hot launch").that(info.getLaunchState())
- .isEqualTo(WaitResult.LAUNCH_STATE_HOT);
verifyAsync(mLaunchObserver).onReportFullyDrawn(eqProto(mTopActivity), anyLong());
verifyOnActivityLaunchFinished(mTopActivity);