diff options
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); |