Merge "AML: Support concurrent launches"
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index a593ef8..de51d4b 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -64,25 +64,24 @@
import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_TIMEOUT;
import static com.android.server.wm.EventLogTags.WM_ACTIVITY_LAUNCH_TIME;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.WaitResult;
import android.app.WindowConfiguration.WindowingMode;
import android.content.ComponentName;
-import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.dex.ArtManagerInternal;
import android.content.pm.dex.PackageOptimizationInfo;
import android.metrics.LogMaker;
-import android.os.Handler;
+import android.os.Binder;
import android.os.Looper;
-import android.os.Message;
import android.os.SystemClock;
import android.os.Trace;
+import android.util.ArrayMap;
import android.util.EventLog;
import android.util.Log;
import android.util.Slog;
-import android.util.SparseArray;
-import android.util.SparseIntArray;
import android.util.StatsLog;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
@@ -90,16 +89,23 @@
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.util.function.pooled.PooledLambda;
import com.android.server.LocalServices;
+import java.util.ArrayList;
+import java.util.LinkedList;
import java.util.concurrent.TimeUnit;
/**
* Listens to activity launches, transitions, visibility changes and window drawn callbacks to
* determine app launch times and draw delays. Source of truth for activity metrics and provides
* data for Tron, logcat, event logs and {@link android.app.WaitResult}.
- *
+ * <p>
+ * A typical sequence of a launch event could be:
+ * {@link #notifyActivityLaunching}, {@link #notifyActivityLaunched},
+ * {@link #notifyStartingWindowDrawn} (optional), {@link #notifyTransitionStarting}
+ * {@link #notifyWindowsDrawn}.
+ * <p>
* Tests:
* atest CtsWindowManagerDeviceTestCases:ActivityMetricsLoggerTests
*/
@@ -115,12 +121,14 @@
private static final int WINDOW_STATE_ASSISTANT = 3;
private static final int WINDOW_STATE_INVALID = -1;
- private static final long INVALID_START_TIME = -1;
+ /**
+ * The flag for {@link #notifyActivityLaunching} to skip associating a new launch with an active
+ * 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;
private static final int INVALID_TRANSITION_TYPE = -1;
- private static final int MSG_CHECK_VISIBILITY = 0;
-
// Preallocated strings we are sending to tron, so we don't have to allocate a new one every
// time we log.
private static final String[] TRON_WINDOW_STATE_VARZ_STRINGS = {
@@ -129,27 +137,12 @@
private int mWindowState = WINDOW_STATE_STANDARD;
private long mLastLogTimeSecs;
private final ActivityStackSupervisor mSupervisor;
- private final Context mContext;
private final MetricsLogger mMetricsLogger = new MetricsLogger();
- // set to INVALID_START_TIME in reset.
- // set to valid value in notifyActivityLaunching
- private long mCurrentTransitionStartTimeNs = INVALID_START_TIME;
- private long mLastTransitionStartTimeNs = INVALID_START_TIME;
-
- private int mCurrentTransitionDeviceUptime;
- private int mCurrentTransitionDelayMs;
-
- /** If the any app transitions have been logged as starting, after the latest reset. */
- private boolean mLoggedTransitionStarting;
-
- /** Map : @WindowingMode int => WindowingModeTransitionInfo */
- private final SparseArray<WindowingModeTransitionInfo> mWindowingModeTransitionInfo =
- new SparseArray<>();
- /** Map : @WindowingMode int => WindowingModeTransitionInfo */
- private final SparseArray<WindowingModeTransitionInfo> mLastWindowingModeTransitionInfo =
- new SparseArray<>();
- private final H mHandler;
+ /** All active transitions. */
+ private final ArrayList<TransitionInfo> mTransitionInfoList = new ArrayList<>();
+ /** Map : Last launched activity => {@link TransitionInfo} */
+ private final ArrayMap<ActivityRecord, TransitionInfo> mLastTransitionInfo = new ArrayMap<>();
private ArtManagerInternal mArtManagerInternal;
private final StringBuilder mStringBuilder = new StringBuilder();
@@ -161,54 +154,151 @@
private final LaunchObserverRegistryImpl mLaunchObserver;
@VisibleForTesting static final int LAUNCH_OBSERVER_ACTIVITY_RECORD_PROTO_CHUNK_SIZE = 512;
- private final class H extends Handler {
+ /**
+ * The information created when an intent is incoming but we do not yet know whether it will be
+ * launched successfully.
+ */
+ static final class LaunchingState {
+ /** The timestamp of {@link #notifyActivityLaunching}. */
+ private long mCurrentTransitionStartTimeNs;
+ /** Non-null when a {@link TransitionInfo} is created for this state. */
+ private TransitionInfo mAssociatedTransitionInfo;
- public H(Looper looper) {
- super(looper);
- }
-
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_CHECK_VISIBILITY:
- final SomeArgs args = (SomeArgs) msg.obj;
- checkVisibility((Task) args.arg1, (ActivityRecord) args.arg2);
- break;
- }
+ @VisibleForTesting
+ boolean allDrawn() {
+ return mAssociatedTransitionInfo != null && mAssociatedTransitionInfo.allDrawn();
}
}
- private final class WindowingModeTransitionInfo {
+ /** The information created when an activity is confirmed to be launched. */
+ private static final class TransitionInfo {
+ /**
+ * The field to lookup and update an existing transition efficiently between
+ * {@link #notifyActivityLaunching} and {@link #notifyActivityLaunched}.
+ *
+ * @see LaunchingState#mAssociatedTransitionInfo
+ */
+ final LaunchingState mLaunchingState;
+ /**
+ * The timestamp of the first {@link #notifyActivityLaunching}. It can be used as a key for
+ * observer to identify which callbacks belong to a launch event.
+ */
+ final long mTransitionStartTimeNs;
+ /** The device uptime in seconds when this transition info is created. */
+ final int mCurrentTransitionDeviceUptime;
+ /** The type can be cold (new process), warm (new activity), or hot (bring to front). */
+ final int mTransitionType;
+ /** Whether the process was already running when the transition started. */
+ final boolean mProcessRunning;
+ /** The activities that should be drawn. */
+ final LinkedList<ActivityRecord> mPendingDrawActivities = new LinkedList<>();
/** The latest activity to have been launched. */
- private ActivityRecord launchedActivity;
- private int startResult;
- private boolean currentTransitionProcessRunning;
+ @NonNull ActivityRecord mLastLaunchedActivity;
+
+ /** The time from {@link #mTransitionStartTimeNs} to {@link #notifyTransitionStarting}. */
+ int mCurrentTransitionDelayMs;
+ /** The time from {@link #mTransitionStartTimeNs} to {@link #notifyStartingWindowDrawn}. */
+ int mStartingWindowDelayMs = INVALID_DELAY;
+ /** The time from {@link #mTransitionStartTimeNs} to {@link #notifyBindApplication}. */
+ int mBindApplicationDelayMs = INVALID_DELAY;
/** Elapsed time from when we launch an activity to when its windows are drawn. */
- private int windowsDrawnDelayMs;
- private int startingWindowDelayMs = INVALID_DELAY;
- private int bindApplicationDelayMs = INVALID_DELAY;
- private int reason = APP_TRANSITION_TIMEOUT;
- // TODO(b/132736359) The number may need to consider the visibility change.
- private int numUndrawnActivities = 1;
+ int mWindowsDrawnDelayMs;
+ /** The reason why the transition started (see ActivityManagerInternal.APP_TRANSITION_*). */
+ int mReason = APP_TRANSITION_TIMEOUT;
+ /** The flag ensures that {@link #mStartingWindowDelayMs} is only set once. */
+ boolean mLoggedStartingWindowDrawn;
+ /** If the any app transitions have been logged as starting. */
+ boolean mLoggedTransitionStarting;
+
/** Non-null if the application has reported drawn but its window hasn't. */
- private Runnable pendingFullyDrawn;
- private boolean loggedStartingWindowDrawn;
- private boolean launchTraceActive;
+ @Nullable Runnable mPendingFullyDrawn;
+ /** Non-null if the trace is active. */
+ @Nullable String mLaunchTraceName;
+
+ /** @return Non-null if there will be a window drawn event for the launch. */
+ @Nullable
+ static TransitionInfo create(@NonNull ActivityRecord r,
+ @NonNull LaunchingState launchingState, boolean processRunning, int startResult) {
+ int transitionType = INVALID_TRANSITION_TYPE;
+ if (processRunning) {
+ if (startResult == START_SUCCESS) {
+ transitionType = TYPE_TRANSITION_WARM_LAUNCH;
+ } else if (startResult == START_TASK_TO_FRONT) {
+ transitionType = TYPE_TRANSITION_HOT_LAUNCH;
+ }
+ } else if (startResult == START_SUCCESS || startResult == START_TASK_TO_FRONT) {
+ // Task may still exist when cold launching an activity and the start result will be
+ // set to START_TASK_TO_FRONT. Treat this as a COLD launch.
+ transitionType = TYPE_TRANSITION_COLD_LAUNCH;
+ }
+ if (transitionType == INVALID_TRANSITION_TYPE) {
+ // That means the startResult is neither START_SUCCESS nor START_TASK_TO_FRONT.
+ return null;
+ }
+ return new TransitionInfo(r, launchingState, transitionType, processRunning);
+ }
+
+ /** Use {@link TransitionInfo#create} instead to ensure the transition type is valid. */
+ private TransitionInfo(ActivityRecord r, LaunchingState launchingState, int transitionType,
+ boolean processRunning) {
+ mLaunchingState = launchingState;
+ mTransitionStartTimeNs = launchingState.mCurrentTransitionStartTimeNs;
+ mTransitionType = transitionType;
+ mProcessRunning = processRunning;
+ mCurrentTransitionDeviceUptime =
+ (int) TimeUnit.MILLISECONDS.toSeconds(SystemClock.uptimeMillis());
+ setLatestLaunchedActivity(r);
+ launchingState.mAssociatedTransitionInfo = this;
+ }
/**
* Remembers the latest launched activity to represent the final transition. This also
- * increments the number of activities that should be drawn, so a consecutive launching
- * sequence can be coalesced as one event.
+ * tracks the activities that should be drawn, so a consecutive launching sequence can be
+ * coalesced as one event.
*/
void setLatestLaunchedActivity(ActivityRecord r) {
- if (launchedActivity == r) {
+ if (mLastLaunchedActivity == r) {
return;
}
- launchedActivity = r;
+ mLastLaunchedActivity = r;
+ if (!r.noDisplay) {
+ if (DEBUG_METRICS) Slog.i(TAG, "Add pending draw " + r);
+ mPendingDrawActivities.add(r);
+ }
+ }
+
+ /** @return {@code true} if the activity matches a launched activity in this transition. */
+ boolean contains(ActivityRecord r) {
+ return r == mLastLaunchedActivity || mPendingDrawActivities.contains(r);
+ }
+
+ /** Called when the activity is drawn or won't be drawn. */
+ void removePendingDrawActivity(ActivityRecord r) {
+ if (DEBUG_METRICS) Slog.i(TAG, "Remove pending draw " + r);
+ mPendingDrawActivities.remove(r);
+ }
+
+ boolean allDrawn() {
+ return mPendingDrawActivities.isEmpty();
+ }
+
+ int calculateCurrentDelay() {
+ return calculateDelay(SystemClock.elapsedRealtimeNanos());
+ }
+
+ int calculateDelay(long timestampNs) {
+ // Shouldn't take more than 25 days to launch an app, so int is fine here.
+ return (int) TimeUnit.NANOSECONDS.toMillis(timestampNs - mTransitionStartTimeNs);
+ }
+
+ @Override
+ public String toString() {
+ return "TransitionInfo{" + Integer.toHexString(System.identityHashCode(this))
+ + " a=" + mLastLaunchedActivity + " ua=" + mPendingDrawActivities + "}";
}
}
- final class WindowingModeTransitionInfoSnapshot {
+ static final class TransitionInfoSnapshot {
final private ApplicationInfo applicationInfo;
final private WindowProcessController processRecord;
final String packageName;
@@ -231,17 +321,12 @@
final int windowsFullyDrawnDelayMs;
final int activityRecordIdHashCode;
- private WindowingModeTransitionInfoSnapshot(WindowingModeTransitionInfo info) {
- this(info, info.launchedActivity);
+ private TransitionInfoSnapshot(TransitionInfo info) {
+ this(info, info.mLastLaunchedActivity, INVALID_DELAY);
}
- private WindowingModeTransitionInfoSnapshot(WindowingModeTransitionInfo info,
- ActivityRecord launchedActivity) {
- this(info, launchedActivity, INVALID_DELAY);
- }
-
- private WindowingModeTransitionInfoSnapshot(WindowingModeTransitionInfo info,
- ActivityRecord launchedActivity, int windowsFullyDrawnDelayMs) {
+ private TransitionInfoSnapshot(TransitionInfo info, ActivityRecord launchedActivity,
+ int windowsFullyDrawnDelayMs) {
applicationInfo = launchedActivity.info.applicationInfo;
packageName = launchedActivity.packageName;
launchedActivityName = launchedActivity.info.name;
@@ -250,12 +335,12 @@
launchedActivityAppRecordRequiredAbi = launchedActivity.app == null
? null
: launchedActivity.app.getRequiredAbi();
- reason = info.reason;
- startingWindowDelayMs = info.startingWindowDelayMs;
- bindApplicationDelayMs = info.bindApplicationDelayMs;
- windowsDrawnDelayMs = info.windowsDrawnDelayMs;
- type = getTransitionType(info);
- processRecord = findProcessForActivity(launchedActivity);
+ reason = info.mReason;
+ startingWindowDelayMs = info.mStartingWindowDelayMs;
+ bindApplicationDelayMs = info.mBindApplicationDelayMs;
+ windowsDrawnDelayMs = info.mWindowsDrawnDelayMs;
+ type = info.mTransitionType;
+ processRecord = launchedActivity.app;
processName = launchedActivity.processName;
userId = launchedActivity.mUserId;
launchedActivityShortComponentName = launchedActivity.shortComponentName;
@@ -277,11 +362,9 @@
}
}
- ActivityMetricsLogger(ActivityStackSupervisor supervisor, Context context, Looper looper) {
+ ActivityMetricsLogger(ActivityStackSupervisor supervisor, Looper looper) {
mLastLogTimeSecs = SystemClock.elapsedRealtime() / 1000;
mSupervisor = supervisor;
- mContext = context;
- mHandler = new H(looper);
mLaunchObserver = new LaunchObserverRegistryImpl(looper);
}
@@ -291,7 +374,7 @@
// We log even if the window state hasn't changed, because the user might remain in
// home/fullscreen move forever and we would like to track this kind of behavior
// too.
- MetricsLogger.count(mContext, TRON_WINDOW_STATE_VARZ_STRINGS[mWindowState],
+ mMetricsLogger.count(TRON_WINDOW_STATE_VARZ_STRINGS[mWindowState],
(int) (now - mLastLogTimeSecs));
}
mLastLogTimeSecs = now;
@@ -332,145 +415,169 @@
}
}
+ /** @return Non-null {@link TransitionInfo} if the activity is found in an active transition. */
+ @Nullable
+ private TransitionInfo getActiveTransitionInfo(ActivityRecord r) {
+ for (int i = mTransitionInfoList.size() - 1; i >= 0; i--) {
+ final TransitionInfo info = mTransitionInfoList.get(i);
+ if (info.contains(r)) {
+ return info;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * This method should be only used by starting recents and starting from recents, or internal
+ * tests. Because it doesn't lookup caller and always creates a new launching state.
+ *
+ * @see #notifyActivityLaunching(Intent, ActivityRecord, int)
+ */
+ LaunchingState notifyActivityLaunching(Intent intent) {
+ return notifyActivityLaunching(intent, null /* caller */, IGNORE_CALLER);
+ }
+
+ /**
+ * If the caller is found in an active transition, it will be considered as consecutive launch
+ * and coalesced into the active transition.
+ *
+ * @see #notifyActivityLaunching(Intent, ActivityRecord, int)
+ */
+ LaunchingState notifyActivityLaunching(Intent intent, @Nullable ActivityRecord caller) {
+ return notifyActivityLaunching(intent, caller, Binder.getCallingUid());
+ }
+
/**
* Notifies the tracker at the earliest possible point when we are starting to launch an
- * activity.
+ * activity. The caller must ensure that {@link #notifyActivityLaunched} will be called later
+ * with the returned {@link LaunchingState}.
*/
- void notifyActivityLaunching(Intent intent) {
+ private LaunchingState notifyActivityLaunching(Intent intent, @Nullable ActivityRecord caller,
+ int callingUid) {
+ final long transitionStartTimeNs = SystemClock.elapsedRealtimeNanos();
+ TransitionInfo existingInfo = null;
+ if (callingUid != IGNORE_CALLER) {
+ // Associate the launching event to an active transition if the caller is found in its
+ // launched activities.
+ for (int i = mTransitionInfoList.size() - 1; i >= 0; i--) {
+ final TransitionInfo info = mTransitionInfoList.get(i);
+ if (caller != null && info.contains(caller)) {
+ existingInfo = info;
+ break;
+ }
+ if (existingInfo == null && callingUid == info.mLastLaunchedActivity.getUid()) {
+ // Fallback to check the most recent matched uid for the case that the caller is
+ // not an activity.
+ existingInfo = info;
+ }
+ }
+ }
if (DEBUG_METRICS) {
- Slog.i(TAG, String.format("notifyActivityLaunching: active:%b, intent:%s",
- isAnyTransitionActive(),
- intent));
+ Slog.i(TAG, "notifyActivityLaunching intent=" + intent
+ + " existingInfo=" + existingInfo);
}
- if (mCurrentTransitionStartTimeNs == INVALID_START_TIME) {
-
- mCurrentTransitionStartTimeNs = SystemClock.elapsedRealtimeNanos();
- mLastTransitionStartTimeNs = mCurrentTransitionStartTimeNs;
-
- launchObserverNotifyIntentStarted(intent, mCurrentTransitionStartTimeNs);
+ if (existingInfo == null) {
+ // Only notify the observer for a new launching event.
+ launchObserverNotifyIntentStarted(intent, transitionStartTimeNs);
+ final LaunchingState launchingState = new LaunchingState();
+ launchingState.mCurrentTransitionStartTimeNs = transitionStartTimeNs;
+ return launchingState;
}
+ existingInfo.mLaunchingState.mCurrentTransitionStartTimeNs = transitionStartTimeNs;
+ return existingInfo.mLaunchingState;
}
/**
* Notifies the tracker that the activity is actually launching.
*
- * @param resultCode one of the ActivityManager.START_* flags, indicating the result of the
- * launch
- * @param launchedActivity the activity that is being launched
+ * @param launchingState The launching state to track the new or active transition.
+ * @param resultCode One of the {@link android.app.ActivityManager}.START_* flags, indicating
+ * the result of the launch.
+ * @param launchedActivity The activity that is being launched
*/
- void notifyActivityLaunched(int resultCode, ActivityRecord launchedActivity) {
- final WindowProcessController processRecord = findProcessForActivity(launchedActivity);
- final boolean processRunning = processRecord != null;
+ void notifyActivityLaunched(@NonNull LaunchingState launchingState, int resultCode,
+ @Nullable ActivityRecord launchedActivity) {
+ if (launchedActivity == null) {
+ // The launch is aborted, e.g. intent not resolved, class not found.
+ abort(null /* info */, "nothing launched");
+ return;
+ }
+ final WindowProcessController processRecord = launchedActivity.app != null
+ ? launchedActivity.app
+ : mSupervisor.mService.getProcessController(
+ launchedActivity.processName, launchedActivity.info.applicationInfo.uid);
+ // Whether the process that will contains the activity is already running.
+ final boolean processRunning = processRecord != null;
// We consider this a "process switch" if the process of the activity that gets launched
// didn't have an activity that was in started state. In this case, we assume that lot
// of caches might be purged so the time until it produces the first frame is very
// interesting.
- final boolean processSwitch = processRecord == null
+ final boolean processSwitch = !processRunning
|| !processRecord.hasStartedActivity(launchedActivity);
- notifyActivityLaunched(resultCode, launchedActivity, processRunning, processSwitch);
- }
-
- /**
- * Notifies the tracker the the activity is actually launching.
- *
- * @param resultCode one of the ActivityManager.START_* flags, indicating the result of the
- * launch
- * @param launchedActivity the activity being launched
- * @param processRunning whether the process that will contains the activity is already running
- * @param processSwitch whether the process that will contain the activity didn't have any
- * activity that was stopped, i.e. the started activity is "switching"
- * processes
- */
- private void notifyActivityLaunched(int resultCode, ActivityRecord launchedActivity,
- boolean processRunning, boolean processSwitch) {
-
- if (DEBUG_METRICS) Slog.i(TAG, "notifyActivityLaunched"
- + " resultCode=" + resultCode
- + " launchedActivity=" + launchedActivity
- + " processRunning=" + processRunning
- + " processSwitch=" + processSwitch);
-
- // If we are already in an existing transition, only update the activity name, but not the
- // other attributes.
- final @WindowingMode int windowingMode = launchedActivity != null
- ? launchedActivity.getWindowingMode()
- : WINDOWING_MODE_UNDEFINED;
- final WindowingModeTransitionInfo info = mWindowingModeTransitionInfo.get(windowingMode);
- if (mCurrentTransitionStartTimeNs == INVALID_START_TIME) {
- // No transition is active ignore this launch.
- return;
+ final TransitionInfo info = launchingState.mAssociatedTransitionInfo;
+ if (DEBUG_METRICS) {
+ Slog.i(TAG, "notifyActivityLaunched" + " resultCode=" + resultCode
+ + " launchedActivity=" + launchedActivity + " processRunning=" + processRunning
+ + " processSwitch=" + processSwitch + " info=" + info);
}
- if (launchedActivity != null && launchedActivity.mDrawn) {
+ if (launchedActivity.mDrawn) {
// Launched activity is already visible. We cannot measure windows drawn delay.
abort(info, "launched activity already visible");
return;
}
- if (launchedActivity != null && info != null) {
+ if (info != null) {
// If we are already in an existing transition, 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.
info.setLatestLaunchedActivity(launchedActivity);
return;
}
- final boolean otherWindowModesLaunching =
- mWindowingModeTransitionInfo.size() > 0 && info == null;
- if ((!isLoggableResultCode(resultCode) || launchedActivity == null || !processSwitch
- || windowingMode == WINDOWING_MODE_UNDEFINED) && !otherWindowModesLaunching) {
- // Failed to launch or it was not a process switch, so we don't care about the timing.
- abort(info, "failed to launch or not a process switch");
+ if (!processSwitch) {
+ abort(info, "not a process switch");
return;
- } else if (otherWindowModesLaunching) {
- // Don't log this windowing mode but continue with the other windowing modes.
+ }
+
+ final TransitionInfo newInfo = TransitionInfo.create(launchedActivity, launchingState,
+ processRunning, resultCode);
+ if (newInfo == null) {
+ abort(info, "unrecognized launch");
return;
}
if (DEBUG_METRICS) Slog.i(TAG, "notifyActivityLaunched successful");
-
- // A new launch sequence [with the windowingMode] has begun.
- // Start tracking it.
- final WindowingModeTransitionInfo newInfo = new WindowingModeTransitionInfo();
- newInfo.setLatestLaunchedActivity(launchedActivity);
- newInfo.currentTransitionProcessRunning = processRunning;
- newInfo.startResult = resultCode;
- mWindowingModeTransitionInfo.put(windowingMode, newInfo);
- mLastWindowingModeTransitionInfo.put(windowingMode, newInfo);
- mCurrentTransitionDeviceUptime = (int) (SystemClock.uptimeMillis() / 1000);
- startTraces(newInfo);
+ // A new launch sequence has begun. Start tracking it.
+ mTransitionInfoList.add(newInfo);
+ mLastTransitionInfo.put(launchedActivity, newInfo);
+ startLaunchTrace(newInfo);
launchObserverNotifyActivityLaunched(newInfo);
}
/**
- * @return True if we should start logging an event for an activity start that returned
- * {@code resultCode} and that we'll indeed get a windows drawn event.
- */
- private boolean isLoggableResultCode(int resultCode) {
- return resultCode == START_SUCCESS || resultCode == START_TASK_TO_FRONT;
- }
-
- /**
* Notifies the tracker that all windows of the app have been drawn.
*/
- WindowingModeTransitionInfoSnapshot notifyWindowsDrawn(@WindowingMode int windowingMode,
- long timestampNs) {
- if (DEBUG_METRICS) Slog.i(TAG, "notifyWindowsDrawn windowingMode=" + windowingMode);
+ @Nullable
+ TransitionInfoSnapshot notifyWindowsDrawn(@NonNull ActivityRecord r, long timestampNs) {
+ if (DEBUG_METRICS) Slog.i(TAG, "notifyWindowsDrawn " + r);
- final WindowingModeTransitionInfo info = mWindowingModeTransitionInfo.get(windowingMode);
- if (info == null || info.numUndrawnActivities == 0) {
+ final TransitionInfo info = getActiveTransitionInfo(r);
+ if (info == null || info.allDrawn()) {
+ if (DEBUG_METRICS) Slog.i(TAG, "notifyWindowsDrawn no activity to be drawn");
return null;
}
- info.windowsDrawnDelayMs = calculateDelay(timestampNs);
- info.numUndrawnActivities--;
- final WindowingModeTransitionInfoSnapshot infoSnapshot =
- new WindowingModeTransitionInfoSnapshot(info);
- if (allWindowsDrawn() && mLoggedTransitionStarting) {
- reset(false /* abort */, info, "notifyWindowsDrawn - all windows drawn", timestampNs);
+ // Always calculate the delay because the caller may need to know the individual drawn time.
+ info.mWindowsDrawnDelayMs = info.calculateDelay(timestampNs);
+ info.removePendingDrawActivity(r);
+ final TransitionInfoSnapshot infoSnapshot = new TransitionInfoSnapshot(info);
+ if (info.mLoggedTransitionStarting && info.allDrawn()) {
+ done(false /* abort */, info, "notifyWindowsDrawn - all windows drawn", timestampNs);
}
return infoSnapshot;
}
@@ -478,70 +585,76 @@
/**
* Notifies the tracker that the starting window was drawn.
*/
- void notifyStartingWindowDrawn(@WindowingMode int windowingMode, long timestampNs) {
- final WindowingModeTransitionInfo info = mWindowingModeTransitionInfo.get(windowingMode);
- if (info == null || info.loggedStartingWindowDrawn) {
+ void notifyStartingWindowDrawn(@NonNull ActivityRecord r) {
+ final TransitionInfo info = getActiveTransitionInfo(r);
+ if (info == null || info.mLoggedStartingWindowDrawn) {
return;
}
- info.loggedStartingWindowDrawn = true;
- info.startingWindowDelayMs = calculateDelay(timestampNs);
+ if (DEBUG_METRICS) Slog.i(TAG, "notifyStartingWindowDrawn " + r);
+ info.mLoggedStartingWindowDrawn = true;
+ info.mStartingWindowDelayMs = info.calculateDelay(SystemClock.elapsedRealtimeNanos());
}
/**
* Notifies the tracker that the app transition is starting.
*
- * @param windowingModeToReason A map from windowing mode to a reason integer, which must be on
- * of ActivityTaskManagerInternal.APP_TRANSITION_* reasons.
+ * @param activityToReason A map from activity to a reason integer, which must be on of
+ * ActivityTaskManagerInternal.APP_TRANSITION_* reasons.
*/
- void notifyTransitionStarting(SparseIntArray windowingModeToReason, long timestampNs) {
- if (!isAnyTransitionActive() || mLoggedTransitionStarting) {
- // Ignore calls to this made after a reset and prior to notifyActivityLaunching.
-
- // Ignore any subsequent notifyTransitionStarting until the next reset.
- return;
- }
+ void notifyTransitionStarting(ArrayMap<ActivityRecord, Integer> activityToReason) {
if (DEBUG_METRICS) Slog.i(TAG, "notifyTransitionStarting");
- mCurrentTransitionDelayMs = calculateDelay(timestampNs);
- mLoggedTransitionStarting = true;
- WindowingModeTransitionInfo foundInfo = null;
- for (int index = windowingModeToReason.size() - 1; index >= 0; index--) {
- final @WindowingMode int windowingMode = windowingModeToReason.keyAt(index);
- final WindowingModeTransitionInfo info = mWindowingModeTransitionInfo.get(
- windowingMode);
- if (info == null) {
+ final long timestampNs = SystemClock.elapsedRealtimeNanos();
+ for (int index = activityToReason.size() - 1; index >= 0; index--) {
+ final ActivityRecord r = activityToReason.keyAt(index);
+ final TransitionInfo info = getActiveTransitionInfo(r);
+ if (info == null || info.mLoggedTransitionStarting) {
+ // Ignore any subsequent notifyTransitionStarting.
continue;
}
- info.reason = windowingModeToReason.valueAt(index);
- foundInfo = info;
+ if (DEBUG_METRICS) {
+ Slog.i(TAG, "notifyTransitionStarting activity=" + r + " info=" + info);
+ }
+
+ info.mCurrentTransitionDelayMs = info.calculateDelay(timestampNs);
+ info.mReason = activityToReason.valueAt(index);
+ info.mLoggedTransitionStarting = true;
+ if (info.allDrawn()) {
+ done(false /* abort */, info, "notifyTransitionStarting - all windows drawn",
+ timestampNs);
+ }
}
- if (allWindowsDrawn()) {
- // abort metrics collection if we cannot find a matching transition.
- final boolean abortMetrics = foundInfo == null;
- reset(abortMetrics, foundInfo, "notifyTransitionStarting - all windows drawn",
- timestampNs /* timestampNs */);
- }
+ }
+
+ /** Makes sure that the reference to the removed activity is cleared. */
+ void notifyActivityRemoved(@NonNull ActivityRecord r) {
+ mLastTransitionInfo.remove(r);
}
/**
* Notifies the tracker that the visibility of an app is changing.
*
- * @param activityRecord the app that is changing its visibility
+ * @param r the app that is changing its visibility
*/
- void notifyVisibilityChanged(ActivityRecord activityRecord) {
- final WindowingModeTransitionInfo info = mWindowingModeTransitionInfo.get(
- activityRecord.getWindowingMode());
+ void notifyVisibilityChanged(@NonNull ActivityRecord r) {
+ final TransitionInfo info = getActiveTransitionInfo(r);
if (info == null) {
return;
}
- if (info.launchedActivity != activityRecord) {
+ if (DEBUG_METRICS) {
+ Slog.i(TAG, "notifyVisibilityChanged " + r + " visible=" + r.mVisibleRequested
+ + " state=" + r.getState() + " finishing=" + r.finishing);
+ }
+ if (!r.mVisibleRequested || r.finishing) {
+ info.removePendingDrawActivity(r);
+ }
+ if (info.mLastLaunchedActivity != r) {
return;
}
- final Task t = activityRecord.getTask();
- final SomeArgs args = SomeArgs.obtain();
- args.arg1 = t;
- args.arg2 = activityRecord;
- mHandler.obtainMessage(MSG_CHECK_VISIBILITY, args).sendToTarget();
+ // The activity and its task are passed separately because the activity may be removed from
+ // the task later.
+ r.mAtmService.mH.sendMessage(PooledLambda.obtainMessage(
+ ActivityMetricsLogger::checkVisibility, this, r.getTask(), r));
}
/** @return {@code true} if the given task has an activity will be drawn. */
@@ -552,8 +665,7 @@
private void checkVisibility(Task t, ActivityRecord r) {
synchronized (mSupervisor.mService.mGlobalLock) {
- final WindowingModeTransitionInfo info = mWindowingModeTransitionInfo.get(
- r.getWindowingMode());
+ final TransitionInfo info = getActiveTransitionInfo(r);
// If we have an active transition that's waiting on a certain activity that will be
// invisible now, we'll never get onWindowsDrawn, so abort the transition if necessary.
@@ -565,7 +677,7 @@
// The notified activity whose visibility changed is no longer the launched activity.
// We can still wait to get onWindowsDrawn.
- if (info.launchedActivity != r) {
+ if (info.mLastLaunchedActivity != r) {
return;
}
@@ -579,11 +691,7 @@
if (DEBUG_METRICS) Slog.i(TAG, "notifyVisibilityChanged to invisible activity=" + r);
logAppTransitionCancel(info);
- // Abort if this is the only one active transition.
- if (mWindowingModeTransitionInfo.size() == 1
- && mWindowingModeTransitionInfo.get(r.getWindowingMode()) != null) {
- abort(info, "notifyVisibilityChanged to invisible");
- }
+ abort(info, "notifyVisibilityChanged to invisible");
}
}
@@ -593,137 +701,86 @@
* @param appInfo The client into which we'll call bindApplication.
*/
void notifyBindApplication(ApplicationInfo appInfo) {
- for (int i = mWindowingModeTransitionInfo.size() - 1; i >= 0; i--) {
- final WindowingModeTransitionInfo info = mWindowingModeTransitionInfo.valueAt(i);
+ for (int i = mTransitionInfoList.size() - 1; i >= 0; i--) {
+ final TransitionInfo info = mTransitionInfoList.get(i);
// App isn't attached to record yet, so match with info.
- if (info.launchedActivity.info.applicationInfo == appInfo) {
- info.bindApplicationDelayMs = calculateCurrentDelay();
+ if (info.mLastLaunchedActivity.info.applicationInfo == appInfo) {
+ info.mBindApplicationDelayMs = info.calculateCurrentDelay();
}
}
}
- @VisibleForTesting
- boolean allWindowsDrawn() {
- for (int index = mWindowingModeTransitionInfo.size() - 1; index >= 0; index--) {
- if (mWindowingModeTransitionInfo.valueAt(index).numUndrawnActivities != 0) {
- return false;
- }
- }
- return true;
- }
-
- private boolean isAnyTransitionActive() {
- return mCurrentTransitionStartTimeNs != INVALID_START_TIME
- && mWindowingModeTransitionInfo.size() > 0;
- }
-
/** Aborts tracking of current launch metrics. */
- private void abort(WindowingModeTransitionInfo info, String cause) {
- reset(true /* abort */, info, cause, 0L /* timestampNs */);
+ private void abort(TransitionInfo info, String cause) {
+ done(true /* abort */, info, cause, 0L /* timestampNs */);
}
- private void reset(boolean abort, WindowingModeTransitionInfo info, String cause,
+ /** Called when the given transition (info) is no longer active. */
+ private void done(boolean abort, @Nullable TransitionInfo info, String cause,
long timestampNs) {
- final boolean isAnyTransitionActive = isAnyTransitionActive();
if (DEBUG_METRICS) {
- Slog.i(TAG, "reset abort=" + abort + " cause=" + cause + " timestamp=" + timestampNs
- + " active=" + isAnyTransitionActive);
+ Slog.i(TAG, "done abort=" + abort + " cause=" + cause + " timestamp=" + timestampNs
+ + " info=" + info);
}
- if (!abort && isAnyTransitionActive) {
- logAppTransitionMultiEvents();
- }
- stopLaunchTrace(info);
-
- // Ignore reset-after reset.
- if (isAnyTransitionActive) {
- // LaunchObserver callbacks.
- if (abort) {
- launchObserverNotifyActivityLaunchCancelled(info);
- } else {
- launchObserverNotifyActivityLaunchFinished(info, timestampNs);
- }
- } else {
+ if (info == null) {
launchObserverNotifyIntentFailed();
- }
-
- mCurrentTransitionStartTimeNs = INVALID_START_TIME;
- mCurrentTransitionDelayMs = INVALID_DELAY;
- mLoggedTransitionStarting = false;
- mWindowingModeTransitionInfo.clear();
- }
-
- private int calculateCurrentDelay() {
- // Shouldn't take more than 25 days to launch an app, so int is fine here.
- return (int) TimeUnit.NANOSECONDS
- .toMillis(SystemClock.elapsedRealtimeNanos() - mCurrentTransitionStartTimeNs);
- }
-
- private int calculateDelay(long timestampNs) {
- // Shouldn't take more than 25 days to launch an app, so int is fine here.
- return (int) TimeUnit.NANOSECONDS.toMillis(timestampNs -
- mCurrentTransitionStartTimeNs);
- }
-
- private void logAppTransitionCancel(WindowingModeTransitionInfo info) {
- final int type = getTransitionType(info);
- if (type == INVALID_TRANSITION_TYPE) {
return;
}
+
+ stopLaunchTrace(info);
+ if (abort) {
+ launchObserverNotifyActivityLaunchCancelled(info);
+ } else {
+ logAppTransitionFinished(info);
+ launchObserverNotifyActivityLaunchFinished(info, timestampNs);
+ }
+ info.mPendingDrawActivities.clear();
+ mTransitionInfoList.remove(info);
+ }
+
+ private void logAppTransitionCancel(TransitionInfo info) {
+ final int type = info.mTransitionType;
+ final ActivityRecord activity = info.mLastLaunchedActivity;
final LogMaker builder = new LogMaker(APP_TRANSITION_CANCELLED);
- builder.setPackageName(info.launchedActivity.packageName);
+ builder.setPackageName(activity.packageName);
builder.setType(type);
- builder.addTaggedData(FIELD_CLASS_NAME, info.launchedActivity.info.name);
+ builder.addTaggedData(FIELD_CLASS_NAME, activity.info.name);
mMetricsLogger.write(builder);
StatsLog.write(
StatsLog.APP_START_CANCELED,
- info.launchedActivity.info.applicationInfo.uid,
- info.launchedActivity.packageName,
+ activity.info.applicationInfo.uid,
+ activity.packageName,
convertAppStartTransitionType(type),
- info.launchedActivity.info.name);
+ activity.info.name);
if (DEBUG_METRICS) {
Slog.i(TAG, String.format("APP_START_CANCELED(%s, %s, %s, %s)",
- info.launchedActivity.info.applicationInfo.uid,
- info.launchedActivity.packageName,
+ activity.info.applicationInfo.uid,
+ activity.packageName,
convertAppStartTransitionType(type),
- info.launchedActivity.info.name));
+ activity.info.name));
}
}
- private void logAppTransitionMultiEvents() {
- if (DEBUG_METRICS) Slog.i(TAG, "logging transition events");
- for (int index = mWindowingModeTransitionInfo.size() - 1; index >= 0; index--) {
- final WindowingModeTransitionInfo info = mWindowingModeTransitionInfo.valueAt(index);
- final int type = getTransitionType(info);
- if (type == INVALID_TRANSITION_TYPE) {
- if (DEBUG_METRICS) {
- Slog.i(TAG, "invalid transition type"
- + " processRunning=" + info.currentTransitionProcessRunning
- + " startResult=" + info.startResult);
- }
- return;
- }
+ private void logAppTransitionFinished(@NonNull TransitionInfo info) {
+ if (DEBUG_METRICS) Slog.i(TAG, "logging finished transition " + info);
- // Take a snapshot of the transition info before sending it to the handler for logging.
- // This will avoid any races with other operations that modify the ActivityRecord.
- final WindowingModeTransitionInfoSnapshot infoSnapshot =
- new WindowingModeTransitionInfoSnapshot(info);
- final int currentTransitionDeviceUptime = mCurrentTransitionDeviceUptime;
- final int currentTransitionDelayMs = mCurrentTransitionDelayMs;
- BackgroundThread.getHandler().post(() -> logAppTransition(
- currentTransitionDeviceUptime, currentTransitionDelayMs, infoSnapshot));
- BackgroundThread.getHandler().post(() -> logAppDisplayed(infoSnapshot));
- if (info.pendingFullyDrawn != null) {
- info.pendingFullyDrawn.run();
- }
-
- info.launchedActivity.info.launchToken = null;
+ // Take a snapshot of the transition info before sending it to the handler for logging.
+ // This will avoid any races with other operations that modify the ActivityRecord.
+ final TransitionInfoSnapshot infoSnapshot = new TransitionInfoSnapshot(info);
+ BackgroundThread.getHandler().post(() -> logAppTransition(
+ info.mCurrentTransitionDeviceUptime, info.mCurrentTransitionDelayMs, infoSnapshot));
+ BackgroundThread.getHandler().post(() -> logAppDisplayed(infoSnapshot));
+ if (info.mPendingFullyDrawn != null) {
+ info.mPendingFullyDrawn.run();
}
+
+ info.mLastLaunchedActivity.info.launchToken = null;
}
// This gets called on a background thread without holding the activity manager lock.
private void logAppTransition(int currentTransitionDeviceUptime, int currentTransitionDelayMs,
- WindowingModeTransitionInfoSnapshot info) {
+ TransitionInfoSnapshot info) {
final LogMaker builder = new LogMaker(APP_TRANSITION);
builder.setPackageName(info.packageName);
builder.setType(info.type);
@@ -794,7 +851,7 @@
logAppStartMemoryStateCapture(info);
}
- private void logAppDisplayed(WindowingModeTransitionInfoSnapshot info) {
+ private void logAppDisplayed(TransitionInfoSnapshot info) {
if (info.type != TYPE_TRANSITION_WARM_LAUNCH && info.type != TYPE_TRANSITION_COLD_LAUNCH) {
return;
}
@@ -825,26 +882,25 @@
return StatsLog.APP_START_OCCURRED__TYPE__UNKNOWN;
}
- /** @return the last known window drawn delay of the given windowing mode. */
- int getLastDrawnDelayMs(@WindowingMode int windowingMode) {
- final WindowingModeTransitionInfo info = mLastWindowingModeTransitionInfo.get(
- windowingMode);
- return info != null ? info.windowsDrawnDelayMs : INVALID_DELAY;
+ /** @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;
}
- WindowingModeTransitionInfoSnapshot logAppTransitionReportedDrawn(ActivityRecord r,
+ /** @see android.app.Activity#reportFullyDrawn */
+ TransitionInfoSnapshot logAppTransitionReportedDrawn(ActivityRecord r,
boolean restoredFromBundle) {
- final WindowingModeTransitionInfo info = mLastWindowingModeTransitionInfo.get(
- r.getWindowingMode());
+ final TransitionInfo info = mLastTransitionInfo.get(r);
if (info == null) {
return null;
}
- if (info.numUndrawnActivities > 0 && info.pendingFullyDrawn == null) {
+ if (!info.allDrawn() && info.mPendingFullyDrawn == null) {
// There are still undrawn activities, postpone reporting fully drawn until all of its
// windows are drawn. So that is closer to an usable state.
- info.pendingFullyDrawn = () -> {
+ info.mPendingFullyDrawn = () -> {
logAppTransitionReportedDrawn(r, restoredFromBundle);
- info.pendingFullyDrawn = null;
+ info.mPendingFullyDrawn = null;
};
return null;
}
@@ -853,39 +909,39 @@
// actually used to trace this function, but instead the logical task that this function
// fullfils (handling reportFullyDrawn() callbacks).
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
- "ActivityManager:ReportingFullyDrawn " + info.launchedActivity.packageName);
+ "ActivityManager:ReportingFullyDrawn " + info.mLastLaunchedActivity.packageName);
final LogMaker builder = new LogMaker(APP_TRANSITION_REPORTED_DRAWN);
builder.setPackageName(r.packageName);
builder.addTaggedData(FIELD_CLASS_NAME, r.info.name);
final long currentTimestampNs = SystemClock.elapsedRealtimeNanos();
- final long startupTimeMs = info.pendingFullyDrawn != null
- ? info.windowsDrawnDelayMs
- : TimeUnit.NANOSECONDS.toMillis(currentTimestampNs - mLastTransitionStartTimeNs);
+ final long startupTimeMs = info.mPendingFullyDrawn != null
+ ? info.mWindowsDrawnDelayMs
+ : TimeUnit.NANOSECONDS.toMillis(currentTimestampNs - info.mTransitionStartTimeNs);
builder.addTaggedData(APP_TRANSITION_REPORTED_DRAWN_MS, startupTimeMs);
builder.setType(restoredFromBundle
? TYPE_TRANSITION_REPORTED_DRAWN_WITH_BUNDLE
: TYPE_TRANSITION_REPORTED_DRAWN_NO_BUNDLE);
builder.addTaggedData(APP_TRANSITION_PROCESS_RUNNING,
- info.currentTransitionProcessRunning ? 1 : 0);
+ info.mProcessRunning ? 1 : 0);
mMetricsLogger.write(builder);
StatsLog.write(
StatsLog.APP_START_FULLY_DRAWN,
- info.launchedActivity.info.applicationInfo.uid,
- info.launchedActivity.packageName,
+ info.mLastLaunchedActivity.info.applicationInfo.uid,
+ info.mLastLaunchedActivity.packageName,
restoredFromBundle
? StatsLog.APP_START_FULLY_DRAWN__TYPE__WITH_BUNDLE
: StatsLog.APP_START_FULLY_DRAWN__TYPE__WITHOUT_BUNDLE,
- info.launchedActivity.info.name,
- info.currentTransitionProcessRunning,
+ info.mLastLaunchedActivity.info.name,
+ info.mProcessRunning,
startupTimeMs);
// Ends the trace started at the beginning of this function. This is located here to allow
// the trace slice to have a noticable duration.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
- final WindowingModeTransitionInfoSnapshot infoSnapshot =
- new WindowingModeTransitionInfoSnapshot(info, r, (int) startupTimeMs);
+ final TransitionInfoSnapshot infoSnapshot =
+ new TransitionInfoSnapshot(info, r, (int) startupTimeMs);
BackgroundThread.getHandler().post(() -> logAppFullyDrawn(infoSnapshot));
// Notify reportFullyDrawn event.
@@ -894,7 +950,7 @@
return infoSnapshot;
}
- private void logAppFullyDrawn(WindowingModeTransitionInfoSnapshot info) {
+ private void logAppFullyDrawn(TransitionInfoSnapshot info) {
if (info.type != TYPE_TRANSITION_WARM_LAUNCH && info.type != TYPE_TRANSITION_COLD_LAUNCH) {
return;
}
@@ -970,23 +1026,7 @@
mMetricsLogger.write(builder);
}
- private int getTransitionType(WindowingModeTransitionInfo info) {
- if (info.currentTransitionProcessRunning) {
- if (info.startResult == START_SUCCESS) {
- return TYPE_TRANSITION_WARM_LAUNCH;
- } else if (info.startResult == START_TASK_TO_FRONT) {
- return TYPE_TRANSITION_HOT_LAUNCH;
- }
- } else if (info.startResult == START_SUCCESS
- || (info.startResult == START_TASK_TO_FRONT)) {
- // Task may still exist when cold launching an activity and the start
- // result will be set to START_TASK_TO_FRONT. Treat this as a COLD launch.
- return TYPE_TRANSITION_COLD_LAUNCH;
- }
- return INVALID_TRANSITION_TYPE;
- }
-
- private void logAppStartMemoryStateCapture(WindowingModeTransitionInfoSnapshot info) {
+ private void logAppStartMemoryStateCapture(TransitionInfoSnapshot info) {
if (info.processRecord == null) {
if (DEBUG_METRICS) Slog.i(TAG, "logAppStartMemoryStateCapture processRecord null");
return;
@@ -1012,13 +1052,6 @@
memoryStat.swapInBytes);
}
- private WindowProcessController findProcessForActivity(ActivityRecord launchedActivity) {
- return launchedActivity != null
- ? mSupervisor.mService.mProcessNames.get(
- launchedActivity.processName, launchedActivity.info.applicationInfo.uid)
- : null;
- }
-
private ArtManagerInternal getArtManagerInternal() {
if (mArtManagerInternal == null) {
// Note that this may be null.
@@ -1029,30 +1062,24 @@
return mArtManagerInternal;
}
- /**
- * Starts traces for app launch.
- *
- * @param info
- * */
- private void startTraces(WindowingModeTransitionInfo info) {
- if (!Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER) || info == null
- || info.launchTraceActive) {
+ /** Starts trace for an activity is actually launching. */
+ private void startLaunchTrace(@NonNull TransitionInfo info) {
+ if (DEBUG_METRICS) Slog.i(TAG, "startLaunchTrace " + info);
+ if (!Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
return;
}
- Trace.asyncTraceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "launching: "
- + info.launchedActivity.packageName, 0);
- info.launchTraceActive = true;
+ info.mLaunchTraceName = "launching: " + info.mLastLaunchedActivity.packageName;
+ Trace.asyncTraceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, info.mLaunchTraceName, 0);
}
- private void stopLaunchTrace(WindowingModeTransitionInfo info) {
- if (info == null) {
+ /** Stops trace for the launch is completed or cancelled. */
+ private void stopLaunchTrace(@NonNull TransitionInfo info) {
+ if (DEBUG_METRICS) Slog.i(TAG, "stopLaunchTrace " + info);
+ if (info.mLaunchTraceName == null) {
return;
}
- if (info.launchTraceActive) {
- Trace.asyncTraceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER, "launching: "
- + info.launchedActivity.packageName, 0);
- info.launchTraceActive = false;
- }
+ Trace.asyncTraceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER, info.mLaunchTraceName, 0);
+ info.mLaunchTraceName = null;
}
public ActivityMetricsLaunchObserverRegistry getLaunchObserverRegistry() {
@@ -1088,16 +1115,16 @@
* Notify the {@link ActivityMetricsLaunchObserver} that the current launch sequence's activity
* has started.
*/
- private void launchObserverNotifyActivityLaunched(WindowingModeTransitionInfo info) {
+ private void launchObserverNotifyActivityLaunched(TransitionInfo info) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
"MetricsLogger:launchObserverNotifyActivityLaunched");
@ActivityMetricsLaunchObserver.Temperature int temperature =
- convertTransitionTypeToLaunchObserverTemperature(getTransitionType(info));
+ convertTransitionTypeToLaunchObserverTemperature(info.mTransitionType);
// Beginning a launch is timing sensitive and so should be observed as soon as possible.
- mLaunchObserver.onActivityLaunched(convertActivityRecordToProto(info.launchedActivity),
- temperature);
+ mLaunchObserver.onActivityLaunched(convertActivityRecordToProto(info.mLastLaunchedActivity),
+ temperature);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
@@ -1116,12 +1143,12 @@
* Notify the {@link ActivityMetricsLaunchObserver} that the current launch sequence is
* cancelled.
*/
- private void launchObserverNotifyActivityLaunchCancelled(WindowingModeTransitionInfo info) {
+ private void launchObserverNotifyActivityLaunchCancelled(TransitionInfo info) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
"MetricsLogger:launchObserverNotifyActivityLaunchCancelled");
final @ActivityMetricsLaunchObserver.ActivityRecordProto byte[] activityRecordProto =
- info != null ? convertActivityRecordToProto(info.launchedActivity) : null;
+ info != null ? convertActivityRecordToProto(info.mLastLaunchedActivity) : null;
mLaunchObserver.onActivityLaunchCancelled(activityRecordProto);
@@ -1132,14 +1159,12 @@
* Notify the {@link ActivityMetricsLaunchObserver} that the current launch sequence's activity
* has fully finished (successfully).
*/
- private void launchObserverNotifyActivityLaunchFinished(WindowingModeTransitionInfo info,
- long timestampNs) {
+ private void launchObserverNotifyActivityLaunchFinished(TransitionInfo info, long timestampNs) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
"MetricsLogger:launchObserverNotifyActivityLaunchFinished");
- mLaunchObserver
- .onActivityLaunchFinished(convertActivityRecordToProto(info.launchedActivity),
- timestampNs);
+ mLaunchObserver.onActivityLaunchFinished(
+ convertActivityRecordToProto(info.mLastLaunchedActivity), timestampNs);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 9e50dca..963e090 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -312,7 +312,7 @@
import com.android.server.policy.WindowManagerPolicy;
import com.android.server.protolog.common.ProtoLog;
import com.android.server.uri.UriPermissionOwner;
-import com.android.server.wm.ActivityMetricsLogger.WindowingModeTransitionInfoSnapshot;
+import com.android.server.wm.ActivityMetricsLogger.TransitionInfoSnapshot;
import com.android.server.wm.ActivityStack.ActivityState;
import com.android.server.wm.WindowManagerService.H;
import com.android.server.wm.utils.InsetUtils;
@@ -3010,6 +3010,7 @@
getDisplayContent().mChangingApps.remove(this);
getDisplayContent().mUnknownAppVisibilityController.appRemovedOrHidden(this);
mWmService.mTaskSnapshotController.onAppRemoved(this);
+ mStackSupervisor.getActivityMetricsLogger().notifyActivityRemoved(this);
waitingToShow = false;
if (getDisplayContent().mClosingApps.contains(this)) {
delayed = true;
@@ -4985,7 +4986,7 @@
}
void reportFullyDrawnLocked(boolean restoredFromBundle) {
- final WindowingModeTransitionInfoSnapshot info = mStackSupervisor
+ final TransitionInfoSnapshot info = mStackSupervisor
.getActivityMetricsLogger().logAppTransitionReportedDrawn(this, restoredFromBundle);
if (info != null) {
mStackSupervisor.reportActivityLaunchedLocked(false /* timeout */, this,
@@ -5017,8 +5018,8 @@
if (!drawn) {
return;
}
- final WindowingModeTransitionInfoSnapshot info = mStackSupervisor
- .getActivityMetricsLogger().notifyWindowsDrawn(getWindowingMode(), timestampNs);
+ final TransitionInfoSnapshot info = mStackSupervisor
+ .getActivityMetricsLogger().notifyWindowsDrawn(this, timestampNs);
final int windowsDrawnDelayMs = info != null ? info.windowsDrawnDelayMs : INVALID_DELAY;
final @LaunchState int launchState = info != null ? info.getLaunchState() : -1;
mStackSupervisor.reportActivityLaunchedLocked(false /* timeout */, this,
@@ -5219,7 +5220,8 @@
}
}
} else if (w.isDrawnLw()) {
- onStartingWindowDrawn(SystemClock.elapsedRealtimeNanos());
+ // The starting window for this container is drawn.
+ mStackSupervisor.getActivityMetricsLogger().notifyStartingWindowDrawn(this);
startingDisplayed = true;
}
}
@@ -5227,14 +5229,6 @@
return isInterestingAndDrawn;
}
- /** Called when the starting window for this container is drawn. */
- private void onStartingWindowDrawn(long timestampNs) {
- synchronized (mAtmService.mGlobalLock) {
- mAtmService.mStackSupervisor.getActivityMetricsLogger().notifyStartingWindowDrawn(
- getWindowingMode(), timestampNs);
- }
- }
-
/**
* Called when the key dispatching to a window associated with the app window container
* timed-out.
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index 0376d2c..d98aa6f 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -142,6 +142,7 @@
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.am.ActivityManagerService;
import com.android.server.am.UserState;
+import com.android.server.wm.ActivityMetricsLogger.LaunchingState;
import java.io.FileDescriptor;
import java.io.IOException;
@@ -443,8 +444,7 @@
mInitialized = true;
setRunningTasks(new RunningTasks());
- mActivityMetricsLogger = new ActivityMetricsLogger(this, mService.mContext,
- mHandler.getLooper());
+ mActivityMetricsLogger = new ActivityMetricsLogger(this, mHandler.getLooper());
mKeyguardController = new KeyguardController(mService, this);
mPersisterQueue = new PersisterQueue();
@@ -576,8 +576,7 @@
}
void stopWaitingForActivityVisible(ActivityRecord r) {
- stopWaitingForActivityVisible(r,
- getActivityMetricsLogger().getLastDrawnDelayMs(r.getWindowingMode()));
+ stopWaitingForActivityVisible(r, getActivityMetricsLogger().getLastDrawnDelayMs(r));
}
void stopWaitingForActivityVisible(ActivityRecord r, long totalTime) {
@@ -2762,7 +2761,8 @@
mRootActivityContainer.sendPowerHintForLaunchStartIfNeeded(
true /* forceSend */, targetActivity);
- mActivityMetricsLogger.notifyActivityLaunching(task.intent);
+ final LaunchingState launchingState =
+ mActivityMetricsLogger.notifyActivityLaunching(task.intent);
try {
mService.moveTaskToFrontLocked(null /* appThread */, null /* callingPackage */,
task.mTaskId, 0, options, true /* fromRecents */);
@@ -2770,8 +2770,8 @@
// the override pending app transition will be applied immediately.
targetActivity.applyOptionsLocked();
} finally {
- mActivityMetricsLogger.notifyActivityLaunched(START_TASK_TO_FRONT,
- targetActivity);
+ mActivityMetricsLogger.notifyActivityLaunched(launchingState,
+ START_TASK_TO_FRONT, targetActivity);
}
mService.getActivityStartController().postStartActivityProcessingForLastStarter(
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index a496396..8164bf4 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -121,6 +121,7 @@
import com.android.internal.app.IVoiceInteractor;
import com.android.server.am.PendingIntentRecord;
import com.android.server.pm.InstantAppResolver;
+import com.android.server.wm.ActivityMetricsLogger.LaunchingState;
import com.android.server.wm.ActivityStackSupervisor.PendingActivityLaunch;
import com.android.server.wm.LaunchParamsController.LaunchParams;
@@ -572,15 +573,16 @@
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
int startFlags, boolean doResume, ActivityOptions options, Task inTask) {
try {
- mSupervisor.getActivityMetricsLogger().notifyActivityLaunching(r.intent);
+ final LaunchingState launchingState = mSupervisor.getActivityMetricsLogger()
+ .notifyActivityLaunching(r.intent, r.resultTo);
mLastStartReason = "startResolvedActivity";
mLastStartActivityTimeMs = System.currentTimeMillis();
mLastStartActivityRecord = r;
mLastStartActivityResult = startActivityUnchecked(r, sourceRecord, voiceSession,
voiceInteractor, startFlags, doResume, options, inTask,
false /* restrictedBgActivity */);
- mSupervisor.getActivityMetricsLogger().notifyActivityLaunched(mLastStartActivityResult,
- mLastStartActivityRecord);
+ mSupervisor.getActivityMetricsLogger().notifyActivityLaunched(launchingState,
+ mLastStartActivityResult, mLastStartActivityRecord);
} finally {
onExecutionComplete();
}
@@ -598,8 +600,14 @@
throw new IllegalArgumentException("File descriptors passed in Intent");
}
- mSupervisor.getActivityMetricsLogger().notifyActivityLaunching(mRequest.intent);
+ final LaunchingState launchingState;
+ synchronized (mService.mGlobalLock) {
+ final ActivityRecord caller = ActivityRecord.forTokenLocked(mRequest.resultTo);
+ launchingState = mSupervisor.getActivityMetricsLogger().notifyActivityLaunching(
+ mRequest.intent, caller);
+ }
+ // Do not lock the resolving to avoid potential deadlock.
if (mRequest.activityInfo == null) {
mRequest.resolveActivity(mSupervisor);
}
@@ -643,7 +651,7 @@
// Notify ActivityMetricsLogger that the activity has launched.
// ActivityMetricsLogger will then wait for the windows to be drawn and populate
// WaitResult.
- mSupervisor.getActivityMetricsLogger().notifyActivityLaunched(res,
+ mSupervisor.getActivityMetricsLogger().notifyActivityLaunched(launchingState, res,
mLastStartActivityRecord);
return getExternalResult(mRequest.waitResult == null ? res
: waitForResult(res, mLastStartActivityRecord));
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index cce005b..25f6d6f 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -32,9 +32,7 @@
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
-import android.os.SystemClock;
import android.service.voice.IVoiceInteractionSession;
-import android.util.SparseIntArray;
import android.util.proto.ProtoOutputStream;
import com.android.internal.app.IVoiceInteractor;
@@ -154,17 +152,6 @@
IVoiceInteractor mInteractor);
/**
- * Callback for window manager to let activity manager know that we are finally starting the
- * app transition;
- *
- * @param reasons A map from windowing mode to a reason integer why the transition was started,
- * which must be one of the APP_TRANSITION_* values.
- * @param timestampNs The time at which the app transition started in
- * {@link SystemClock#elapsedRealtimeNs()} ()} timebase.
- */
- public abstract void notifyAppTransitionStarting(SparseIntArray reasons, long timestampNs);
-
- /**
* Callback for window manager to let activity manager know that the app transition was
* cancelled.
*/
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index df03940..d097368 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -220,7 +220,6 @@
import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
-import android.util.SparseIntArray;
import android.util.StatsLog;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
@@ -1228,25 +1227,23 @@
Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId) {
final WaitResult res = new WaitResult();
- synchronized (mGlobalLock) {
- enforceNotIsolatedCaller("startActivityAndWait");
- userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
- userId, "startActivityAndWait");
- // TODO: Switch to user app stacks here.
- getActivityStartController().obtainStarter(intent, "startActivityAndWait")
- .setCaller(caller)
- .setCallingPackage(callingPackage)
- .setResolvedType(resolvedType)
- .setResultTo(resultTo)
- .setResultWho(resultWho)
- .setRequestCode(requestCode)
- .setStartFlags(startFlags)
- .setActivityOptions(bOptions)
- .setUserId(userId)
- .setProfilerInfo(profilerInfo)
- .setWaitResult(res)
- .execute();
- }
+ enforceNotIsolatedCaller("startActivityAndWait");
+ userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
+ userId, "startActivityAndWait");
+ // TODO: Switch to user app stacks here.
+ getActivityStartController().obtainStarter(intent, "startActivityAndWait")
+ .setCaller(caller)
+ .setCallingPackage(callingPackage)
+ .setResolvedType(resolvedType)
+ .setResultTo(resultTo)
+ .setResultWho(resultWho)
+ .setRequestCode(requestCode)
+ .setStartFlags(startFlags)
+ .setActivityOptions(bOptions)
+ .setUserId(userId)
+ .setProfilerInfo(profilerInfo)
+ .setWaitResult(res)
+ .execute();
return res;
}
@@ -1254,24 +1251,22 @@
public final int startActivityWithConfig(IApplicationThread caller, String callingPackage,
Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
int startFlags, Configuration config, Bundle bOptions, int userId) {
- synchronized (mGlobalLock) {
- enforceNotIsolatedCaller("startActivityWithConfig");
- userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId,
- "startActivityWithConfig");
- // TODO: Switch to user app stacks here.
- return getActivityStartController().obtainStarter(intent, "startActivityWithConfig")
- .setCaller(caller)
- .setCallingPackage(callingPackage)
- .setResolvedType(resolvedType)
- .setResultTo(resultTo)
- .setResultWho(resultWho)
- .setRequestCode(requestCode)
- .setStartFlags(startFlags)
- .setGlobalConfiguration(config)
- .setActivityOptions(bOptions)
- .setUserId(userId)
- .execute();
- }
+ enforceNotIsolatedCaller("startActivityWithConfig");
+ userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId,
+ "startActivityWithConfig");
+ // TODO: Switch to user app stacks here.
+ return getActivityStartController().obtainStarter(intent, "startActivityWithConfig")
+ .setCaller(caller)
+ .setCallingPackage(callingPackage)
+ .setResolvedType(resolvedType)
+ .setResultTo(resultTo)
+ .setResultWho(resultWho)
+ .setRequestCode(requestCode)
+ .setStartFlags(startFlags)
+ .setGlobalConfiguration(config)
+ .setActivityOptions(bOptions)
+ .setUserId(userId)
+ .execute();
}
@@ -6099,15 +6094,6 @@
}
@Override
- public void notifyAppTransitionStarting(SparseIntArray reasons,
- long timestampNs) {
- synchronized (mGlobalLock) {
- mStackSupervisor.getActivityMetricsLogger().notifyTransitionStarting(
- reasons, timestampNs);
- }
- }
-
- @Override
public void notifySingleTaskDisplayDrawn(int displayId) {
mTaskChangeNotificationController.notifySingleTaskDisplayDrawn(displayId);
}
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index ff1b423..e9ad0d3 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -52,11 +52,10 @@
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-import android.os.SystemClock;
import android.os.Trace;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Slog;
-import android.util.SparseIntArray;
import android.view.Display;
import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationDefinition;
@@ -81,7 +80,7 @@
private final WallpaperController mWallpaperControllerLocked;
private RemoteAnimationDefinition mRemoteAnimationDefinition = null;
- private final SparseIntArray mTempTransitionReasons = new SparseIntArray();
+ private final ArrayMap<ActivityRecord, Integer> mTempTransitionReasons = new ArrayMap<>();
AppTransitionController(WindowManagerService service, DisplayContent displayContent) {
mService = service;
@@ -208,8 +207,8 @@
mDisplayContent.computeImeTarget(true /* updateImeTarget */);
- mService.mAtmInternal.notifyAppTransitionStarting(mTempTransitionReasons.clone(),
- SystemClock.elapsedRealtimeNanos());
+ mService.mAtmService.mStackSupervisor.getActivityMetricsLogger().notifyTransitionStarting(
+ mTempTransitionReasons);
if (transit == TRANSIT_SHOW_SINGLE_TASK_DISPLAY) {
mService.mAnimator.addAfterPrepareSurfacesRunnable(() -> {
@@ -442,7 +441,8 @@
}
}
- private boolean transitionGoodToGo(ArraySet<ActivityRecord> apps, SparseIntArray outReasons) {
+ private boolean transitionGoodToGo(ArraySet<ActivityRecord> apps,
+ ArrayMap<ActivityRecord, Integer> outReasons) {
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
"Checking %d opening apps (frozen=%b timeout=%b)...", apps.size(),
mService.mDisplayFrozen, mDisplayContent.mAppTransition.isTimeout());
@@ -478,11 +478,10 @@
if (!allDrawn && !activity.startingDisplayed && !activity.startingMoved) {
return false;
}
- final int windowingMode = activity.getWindowingMode();
if (allDrawn) {
- outReasons.put(windowingMode, APP_TRANSITION_WINDOWS_DRAWN);
+ outReasons.put(activity, APP_TRANSITION_WINDOWS_DRAWN);
} else {
- outReasons.put(windowingMode,
+ outReasons.put(activity,
activity.mStartingData instanceof SplashScreenStartingData
? APP_TRANSITION_SPLASH_SCREEN
: APP_TRANSITION_SNAPSHOT);
diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java
index f2e9505..f4c867c 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimation.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimation.java
@@ -45,6 +45,7 @@
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.internal.util.function.pooled.PooledPredicate;
import com.android.server.protolog.common.ProtoLog;
+import com.android.server.wm.ActivityMetricsLogger.LaunchingState;
import com.android.server.wm.RecentsAnimationController.RecentsAnimationCallbacks;
/**
@@ -196,7 +197,8 @@
true /* forceSend */, targetActivity);
}
- mStackSupervisor.getActivityMetricsLogger().notifyActivityLaunching(mTargetIntent);
+ final LaunchingState launchingState =
+ mStackSupervisor.getActivityMetricsLogger().notifyActivityLaunching(mTargetIntent);
if (mCaller != null) {
mCaller.setRunningRecentsAnimation(true);
@@ -255,8 +257,8 @@
// we fetch the visible tasks to be controlled by the animation
mService.mRootActivityContainer.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS);
- mStackSupervisor.getActivityMetricsLogger().notifyActivityLaunched(START_TASK_TO_FRONT,
- targetActivity);
+ mStackSupervisor.getActivityMetricsLogger().notifyActivityLaunched(launchingState,
+ START_TASK_TO_FRONT, targetActivity);
// Register for stack order changes
mDefaultDisplay.registerStackOrderChangedListener(this);
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index 4c5ca38..39d08a2 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -16,7 +16,6 @@
package com.android.server.wm;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.view.RemoteAnimationTarget.MODE_CLOSING;
@@ -42,10 +41,10 @@
import android.os.IBinder.DeathRecipient;
import android.os.RemoteException;
import android.os.SystemClock;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Slog;
import android.util.SparseBooleanArray;
-import android.util.SparseIntArray;
import android.util.proto.ProtoOutputStream;
import android.view.IRecentsAnimationController;
import android.view.IRecentsAnimationRunner;
@@ -502,10 +501,13 @@
} catch (RemoteException e) {
Slog.e(TAG, "Failed to start recents animation", e);
}
- final SparseIntArray reasons = new SparseIntArray();
- reasons.put(WINDOWING_MODE_FULLSCREEN, APP_TRANSITION_RECENTS_ANIM);
- mService.mAtmInternal
- .notifyAppTransitionStarting(reasons, SystemClock.elapsedRealtimeNanos());
+
+ if (mTargetActivityRecord != null) {
+ final ArrayMap<ActivityRecord, Integer> reasons = new ArrayMap<>(1);
+ reasons.put(mTargetActivityRecord, APP_TRANSITION_RECENTS_ANIM);
+ mService.mAtmService.mStackSupervisor.getActivityMetricsLogger()
+ .notifyTransitionStarting(reasons);
+ }
}
private RemoteAnimationTarget[] createAppAnimations() {
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 734761f..12074dc3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
@@ -18,6 +18,7 @@
import static android.app.ActivityManager.START_SUCCESS;
import static android.app.ActivityManager.START_TASK_TO_FRONT;
+import static android.content.ComponentName.createRelative;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
@@ -34,7 +35,7 @@
import android.content.Intent;
import android.os.SystemClock;
import android.platform.test.annotations.Presubmit;
-import android.util.SparseIntArray;
+import android.util.ArrayMap;
import androidx.test.filters.SmallTest;
@@ -58,11 +59,13 @@
@Presubmit
public class ActivityMetricsLaunchObserverTests extends ActivityTestsBase {
private ActivityMetricsLogger mActivityMetricsLogger;
+ private ActivityMetricsLogger.LaunchingState mLaunchingState;
private ActivityMetricsLaunchObserver mLaunchObserver;
private ActivityMetricsLaunchObserverRegistry mLaunchObserverRegistry;
private ActivityRecord mTrampolineActivity;
private ActivityRecord mTopActivity;
+ private boolean mLaunchTopByTrampoline;
@Before
public void setUpAMLO() {
@@ -76,9 +79,13 @@
// Sometimes we need an ActivityRecord for ActivityMetricsLogger to do anything useful.
// This seems to be the easiest way to create an ActivityRecord.
- mTrampolineActivity = new ActivityBuilder(mService).setCreateTask(true).build();
+ mTrampolineActivity = new ActivityBuilder(mService)
+ .setCreateTask(true)
+ .setComponent(createRelative(DEFAULT_COMPONENT_PACKAGE_NAME, "TrampolineActivity"))
+ .build();
mTopActivity = new ActivityBuilder(mService)
.setTask(mTrampolineActivity.getTask())
+ .setComponent(createRelative(DEFAULT_COMPONENT_PACKAGE_NAME, "TopActivity"))
.build();
}
@@ -114,42 +121,42 @@
return verify(mock, timeout(TimeUnit.SECONDS.toMillis(5)));
}
- private void onIntentStarted() {
- Intent intent = new Intent("action 1");
+ private void onIntentStarted(Intent intent) {
+ notifyActivityLaunching(intent);
- mActivityMetricsLogger.notifyActivityLaunching(intent);
-
- verifyAsync(mLaunchObserver).onIntentStarted(eq(intent), anyLong());
+ // If it is launching top activity from trampoline activity, the observer shouldn't receive
+ // onActivityLaunched because the activities should belong to the same transition.
+ if (!mLaunchTopByTrampoline) {
+ verifyAsync(mLaunchObserver).onIntentStarted(eq(intent), anyLong());
+ }
verifyNoMoreInteractions(mLaunchObserver);
}
@Test
public void testOnIntentFailed() {
- onIntentStarted();
+ onIntentStarted(new Intent("testOnIntentFailed"));
// Bringing an intent that's already running 'to front' is not considered
// as an ACTIVITY_LAUNCHED state transition.
- mActivityMetricsLogger.notifyActivityLaunched(START_TASK_TO_FRONT,
- null /* launchedActivity */);
+ notifyActivityLaunched(START_TASK_TO_FRONT, null /* launchedActivity */);
verifyAsync(mLaunchObserver).onIntentFailed();
verifyNoMoreInteractions(mLaunchObserver);
}
- private void onActivityLaunched() {
- onIntentStarted();
+ private void onActivityLaunched(ActivityRecord activity) {
+ onIntentStarted(activity.intent);
+ notifyActivityLaunched(START_SUCCESS, activity);
- mActivityMetricsLogger.notifyActivityLaunched(START_SUCCESS, mTopActivity);
-
- verifyAsync(mLaunchObserver).onActivityLaunched(eqProto(mTopActivity), anyInt());
+ verifyAsync(mLaunchObserver).onActivityLaunched(eqProto(activity), anyInt());
verifyNoMoreInteractions(mLaunchObserver);
}
@Test
public void testOnActivityLaunchFinished() {
- onActivityLaunched();
+ onActivityLaunched(mTopActivity);
- notifyTransitionStarting();
+ notifyTransitionStarting(mTopActivity);
notifyWindowsDrawn(mTopActivity);
verifyAsync(mLaunchObserver).onActivityLaunchFinished(eqProto(mTopActivity), anyLong());
@@ -158,12 +165,12 @@
@Test
public void testOnActivityLaunchCancelled_hasDrawn() {
- onActivityLaunched();
+ onActivityLaunched(mTopActivity);
mTopActivity.mVisibleRequested = mTopActivity.mDrawn = true;
// Cannot time already-visible activities.
- mActivityMetricsLogger.notifyActivityLaunched(START_TASK_TO_FRONT, mTopActivity);
+ notifyActivityLaunched(START_TASK_TO_FRONT, mTopActivity);
verifyAsync(mLaunchObserver).onActivityLaunchCancelled(eqProto(mTopActivity));
verifyNoMoreInteractions(mLaunchObserver);
@@ -182,8 +189,8 @@
.build();
mSupervisor.readyToResume();
- mActivityMetricsLogger.notifyActivityLaunching(noDrawnActivity.intent);
- mActivityMetricsLogger.notifyActivityLaunched(START_SUCCESS, noDrawnActivity);
+ notifyActivityLaunching(noDrawnActivity.intent);
+ notifyActivityLaunched(START_SUCCESS, noDrawnActivity);
noDrawnActivity.destroyIfPossible("test");
mActivityMetricsLogger.notifyVisibilityChanged(noDrawnActivity);
@@ -193,12 +200,12 @@
@Test
public void testOnReportFullyDrawn() {
- onActivityLaunched();
+ onActivityLaunched(mTopActivity);
// The activity reports fully drawn before windows drawn, then the fully drawn event will
// be pending (see {@link WindowingModeTransitionInfo#pendingFullyDrawn}).
mActivityMetricsLogger.logAppTransitionReportedDrawn(mTopActivity, false);
- notifyTransitionStarting();
+ notifyTransitionStarting(mTopActivity);
// The pending fully drawn event should send when the actual windows drawn event occurs.
notifyWindowsDrawn(mTopActivity);
@@ -208,35 +215,56 @@
}
private void onActivityLaunchedTrampoline() {
- onIntentStarted();
-
- mActivityMetricsLogger.notifyActivityLaunched(START_SUCCESS, mTrampolineActivity);
+ onIntentStarted(mTrampolineActivity.intent);
+ notifyActivityLaunched(START_SUCCESS, mTrampolineActivity);
verifyAsync(mLaunchObserver).onActivityLaunched(eqProto(mTrampolineActivity), anyInt());
// A second, distinct, activity launch is coalesced into the current app launch sequence.
- mActivityMetricsLogger.notifyActivityLaunched(START_SUCCESS, mTopActivity);
+ mLaunchTopByTrampoline = true;
+ onIntentStarted(mTopActivity.intent);
+ notifyActivityLaunched(START_SUCCESS, mTopActivity);
+ // The observer shouldn't receive onActivityLaunched for an existing transition.
verifyNoMoreInteractions(mLaunchObserver);
}
- private void notifyTransitionStarting() {
- mActivityMetricsLogger.notifyTransitionStarting(new SparseIntArray(),
- SystemClock.elapsedRealtimeNanos());
+ private void notifyActivityLaunching(Intent intent) {
+ final ActivityMetricsLogger.LaunchingState previousState = mLaunchingState;
+ mLaunchingState = mActivityMetricsLogger.notifyActivityLaunching(intent,
+ mLaunchTopByTrampoline ? mTrampolineActivity : null /* caller */);
+ if (mLaunchTopByTrampoline) {
+ // The transition of TrampolineActivity has not been completed, so when the next
+ // activity is starting from it, the same launching state should be returned.
+ assertWithMessage("Use existing launching state for a caller in active transition")
+ .that(previousState).isEqualTo(mLaunchingState);
+ }
+ }
+
+ private void notifyActivityLaunched(int resultCode, ActivityRecord activity) {
+ mActivityMetricsLogger.notifyActivityLaunched(mLaunchingState, resultCode, activity);
+ }
+
+ private void notifyTransitionStarting(ActivityRecord activity) {
+ final ArrayMap<ActivityRecord, Integer> reasons = new ArrayMap<>();
+ reasons.put(activity, ActivityTaskManagerInternal.APP_TRANSITION_SPLASH_SCREEN);
+ mActivityMetricsLogger.notifyTransitionStarting(reasons);
}
private void notifyWindowsDrawn(ActivityRecord r) {
- mActivityMetricsLogger.notifyWindowsDrawn(r.getWindowingMode(),
- SystemClock.elapsedRealtimeNanos());
+ mActivityMetricsLogger.notifyWindowsDrawn(r, SystemClock.elapsedRealtimeNanos());
}
@Test
public void testOnActivityLaunchFinishedTrampoline() {
onActivityLaunchedTrampoline();
- notifyTransitionStarting();
+ notifyTransitionStarting(mTopActivity);
notifyWindowsDrawn(mTrampolineActivity);
+ assertWithMessage("Trampoline activity is drawn but the top activity is not yet")
+ .that(mLaunchingState.allDrawn()).isFalse();
+
notifyWindowsDrawn(mTopActivity);
verifyAsync(mLaunchObserver).onActivityLaunchFinished(eqProto(mTopActivity), anyLong());
@@ -244,13 +272,23 @@
}
@Test
+ public void testDoNotCountInvisibleActivityToBeDrawn() {
+ onActivityLaunchedTrampoline();
+ mTrampolineActivity.setVisibility(false);
+ notifyWindowsDrawn(mTopActivity);
+
+ assertWithMessage("Trampoline activity is invisble so there should be no undrawn windows")
+ .that(mLaunchingState.allDrawn()).isTrue();
+ }
+
+ @Test
public void testOnActivityLaunchCancelledTrampoline() {
onActivityLaunchedTrampoline();
mTopActivity.mDrawn = true;
// Cannot time already-visible activities.
- mActivityMetricsLogger.notifyActivityLaunched(START_TASK_TO_FRONT, mTopActivity);
+ notifyActivityLaunched(START_TASK_TO_FRONT, mTopActivity);
verifyAsync(mLaunchObserver).onActivityLaunchCancelled(eqProto(mTopActivity));
verifyNoMoreInteractions(mLaunchObserver);
@@ -268,4 +306,32 @@
.that(activityRecordToProto(mTrampolineActivity).length)
.isAtMost(ActivityMetricsLogger.LAUNCH_OBSERVER_ACTIVITY_RECORD_PROTO_CHUNK_SIZE);
}
+
+ @Test
+ public void testConcurrentLaunches() {
+ onActivityLaunched(mTopActivity);
+ final ActivityMetricsLogger.LaunchingState previousState = mLaunchingState;
+
+ final ActivityRecord otherActivity = new ActivityBuilder(mService)
+ .setComponent(createRelative(DEFAULT_COMPONENT_PACKAGE_NAME, "OtherActivity"))
+ .setCreateTask(true)
+ .build();
+ // Assume the calling uid is different from the uid of TopActivity, so a new launching
+ // state should be created here.
+ onActivityLaunched(otherActivity);
+
+ assertWithMessage("Different callers should get 2 indepedent launching states")
+ .that(previousState).isNotEqualTo(mLaunchingState);
+
+ notifyTransitionStarting(otherActivity);
+ notifyWindowsDrawn(otherActivity);
+
+ verifyAsync(mLaunchObserver).onActivityLaunchFinished(eqProto(otherActivity), anyLong());
+
+ // The first transition should still be valid.
+ notifyTransitionStarting(mTopActivity);
+ notifyWindowsDrawn(mTopActivity);
+
+ verifyAsync(mLaunchObserver).onActivityLaunchFinished(eqProto(mTopActivity), anyLong());
+ }
}