• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package com.android.server.wm;
2 
3 import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
4 import static android.app.ActivityManager.START_SUCCESS;
5 import static android.app.ActivityManager.START_TASK_TO_FRONT;
6 import static android.app.ActivityManager.processStateAmToProto;
7 import static android.app.WaitResult.INVALID_DELAY;
8 import static android.app.WaitResult.LAUNCH_STATE_COLD;
9 import static android.app.WaitResult.LAUNCH_STATE_HOT;
10 import static android.app.WaitResult.LAUNCH_STATE_RELAUNCH;
11 import static android.app.WaitResult.LAUNCH_STATE_WARM;
12 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
13 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
14 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
15 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
16 
17 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_ACTIVITY_START;
18 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION;
19 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION_BIND_APPLICATION_DELAY_MS;
20 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION_CALLING_PACKAGE_NAME;
21 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION_CANCELLED;
22 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION_DELAY_MS;
23 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION_DEVICE_UPTIME_SECONDS;
24 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION_IS_EPHEMERAL;
25 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION_PROCESS_RUNNING;
26 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION_REPORTED_DRAWN;
27 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION_REPORTED_DRAWN_MS;
28 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION_STARTING_WINDOW_DELAY_MS;
29 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION_WINDOWS_DRAWN_DELAY_MS;
30 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_CALLING_PACKAGE_NAME;
31 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_CALLING_UID;
32 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_CALLING_UID_HAS_ANY_VISIBLE_WINDOW;
33 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_CALLING_UID_PROC_STATE;
34 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_CLASS_NAME;
35 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_COMING_FROM_PENDING_INTENT;
36 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_INSTANT_APP_LAUNCH_TOKEN;
37 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_INTENT_ACTION;
38 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_PROCESS_RECORD_CUR_PROC_STATE;
39 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_PROCESS_RECORD_HAS_CLIENT_ACTIVITIES;
40 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_PROCESS_RECORD_HAS_FOREGROUND_ACTIVITIES;
41 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_PROCESS_RECORD_HAS_FOREGROUND_SERVICES;
42 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_PROCESS_RECORD_HAS_OVERLAY_UI;
43 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_PROCESS_RECORD_HAS_TOP_UI;
44 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_PROCESS_RECORD_MILLIS_SINCE_FG_INTERACTION;
45 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_PROCESS_RECORD_MILLIS_SINCE_LAST_INTERACTION_EVENT;
46 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_PROCESS_RECORD_MILLIS_SINCE_UNIMPORTANT;
47 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_PROCESS_RECORD_PENDING_UI_CLEAN;
48 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_PROCESS_RECORD_PROCESS_NAME;
49 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_REAL_CALLING_UID;
50 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_REAL_CALLING_UID_HAS_ANY_VISIBLE_WINDOW;
51 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_REAL_CALLING_UID_PROC_STATE;
52 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TARGET_SHORT_COMPONENT_NAME;
53 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.PACKAGE_OPTIMIZATION_COMPILATION_FILTER;
54 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.PACKAGE_OPTIMIZATION_COMPILATION_REASON;
55 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_TRANSITION_COLD_LAUNCH;
56 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_TRANSITION_HOT_LAUNCH;
57 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_TRANSITION_REPORTED_DRAWN_NO_BUNDLE;
58 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_TRANSITION_REPORTED_DRAWN_WITH_BUNDLE;
59 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_TRANSITION_WARM_LAUNCH;
60 import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_WINDOW_TRANSITIONS;
61 import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__LETTERBOX_POSITION__NOT_LETTERBOXED_POSITION;
62 import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__STATE__LETTERBOXED_FOR_ASPECT_RATIO;
63 import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__STATE__LETTERBOXED_FOR_FIXED_ORIENTATION;
64 import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__STATE__LETTERBOXED_FOR_SIZE_COMPAT_MODE;
65 import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__STATE__NOT_LETTERBOXED;
66 import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__STATE__NOT_VISIBLE;
67 import static com.android.internal.util.FrameworkStatsLog.APP_START_OCCURRED__PACKAGE_STOPPED_STATE__PACKAGE_STATE_NORMAL;
68 import static com.android.internal.util.FrameworkStatsLog.APP_START_OCCURRED__PACKAGE_STOPPED_STATE__PACKAGE_STATE_STOPPED;
69 import static com.android.server.am.MemoryStatUtil.MemoryStat;
70 import static com.android.server.am.MemoryStatUtil.readMemoryStatFromFilesystem;
71 import static com.android.server.am.ProcessList.INVALID_ADJ;
72 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_METRICS;
73 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
74 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
75 import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_RECENTS_ANIM;
76 import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_TIMEOUT;
77 import static com.android.server.wm.EventLogTags.WM_ACTIVITY_LAUNCH_TIME;
78 
79 import android.annotation.NonNull;
80 import android.annotation.Nullable;
81 import android.app.ActivityOptions;
82 import android.app.ActivityOptions.SourceInfo;
83 import android.app.ApplicationStartInfo;
84 import android.app.WaitResult;
85 import android.app.WindowConfiguration.WindowingMode;
86 import android.content.ComponentName;
87 import android.content.Intent;
88 import android.content.pm.ApplicationInfo;
89 import android.content.pm.IncrementalStatesInfo;
90 import android.content.pm.dex.ArtManagerInternal;
91 import android.content.pm.dex.PackageOptimizationInfo;
92 import android.metrics.LogMaker;
93 import android.os.Handler;
94 import android.os.Looper;
95 import android.os.SystemClock;
96 import android.os.Trace;
97 import android.os.incremental.IncrementalManager;
98 import android.util.ArrayMap;
99 import android.util.EventLog;
100 import android.util.Log;
101 import android.util.Slog;
102 import android.util.SparseArray;
103 import android.util.TimeUtils;
104 
105 import com.android.internal.annotations.VisibleForTesting;
106 import com.android.internal.logging.MetricsLogger;
107 import com.android.internal.protolog.ProtoLog;
108 import com.android.internal.util.FrameworkStatsLog;
109 import com.android.internal.util.LatencyTracker;
110 import com.android.internal.util.function.pooled.PooledLambda;
111 import com.android.server.FgThread;
112 import com.android.server.LocalServices;
113 import com.android.server.apphibernation.AppHibernationManagerInternal;
114 import com.android.server.apphibernation.AppHibernationService;
115 
116 import java.util.ArrayList;
117 import java.util.concurrent.TimeUnit;
118 
119 /**
120  * Listens to activity launches, transitions, visibility changes and window drawn callbacks to
121  * determine app launch times and draw delays. Source of truth for activity metrics and provides
122  * data for Tron, logcat, event logs and {@link android.app.WaitResult}.
123  * <p>
124  * A typical sequence of a launch event could be:
125  * {@link #notifyActivityLaunching}, {@link #notifyActivityLaunched},
126  * {@link #notifyStartingWindowDrawn} (optional), {@link #notifyTransitionStarting}
127  * {@link #notifyWindowsDrawn}.
128  * <p>
129  * Tests:
130  * atest CtsWindowManagerDeviceTestCases:ActivityMetricsLoggerTests
131  */
132 class ActivityMetricsLogger {
133 
134     private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityMetricsLogger" : TAG_ATM;
135 
136     // Window modes we are interested in logging. If we ever introduce a new type, we need to add
137     // a value here and increase the {@link #TRON_WINDOW_STATE_VARZ_STRINGS} array.
138     private static final int WINDOW_STATE_STANDARD = 0;
139     private static final int WINDOW_STATE_SIDE_BY_SIDE = 1;
140     private static final int WINDOW_STATE_FREEFORM = 2;
141     private static final int WINDOW_STATE_ASSISTANT = 3;
142     private static final int WINDOW_STATE_MULTI_WINDOW = 4;
143     private static final int WINDOW_STATE_INVALID = -1;
144 
145     // These should match AppStartOccurred.MultiWindowLaunchType in the atoms.proto
146     private static final int MULTI_WINDOW_LAUNCH_TYPE_UNSPECIFIED = 0;
147     private static final int MULTI_WINDOW_LAUNCH_TYPE_APP_PAIR = 1;
148 
149     /**
150      * If a launching activity isn't visible within this duration when the device is sleeping, e.g.
151      * keyguard is locked, its transition info will be dropped.
152      */
153     private static final long UNKNOWN_VISIBILITY_CHECK_DELAY_MS = 3000;
154 
155     /**
156      * If the recents animation is finished before the delay since the window drawn, do not log the
157      * action because the duration is too small that may be just an accidentally touch.
158      */
159     private static final long LATENCY_TRACKER_RECENTS_DELAY_MS = 300;
160 
161     /**
162      * The flag for {@link #notifyActivityLaunching} to skip associating a new launch with an active
163      * transition, in the case the launch is standalone (e.g. from recents).
164      */
165     private static final int IGNORE_CALLER = -1;
166 
167     // Preallocated strings we are sending to tron, so we don't have to allocate a new one every
168     // time we log.
169     private static final String[] TRON_WINDOW_STATE_VARZ_STRINGS = {
170             "window_time_0", "window_time_1", "window_time_2", "window_time_3", "window_time_4"};
171 
172     private int mWindowState = WINDOW_STATE_STANDARD;
173     private long mLastLogTimeSecs;
174     private final ActivityTaskSupervisor mSupervisor;
175     private final MetricsLogger mMetricsLogger = new MetricsLogger();
176     private final Handler mLoggerHandler = FgThread.getHandler();
177 
178     /** All active transitions. */
179     private final ArrayList<TransitionInfo> mTransitionInfoList = new ArrayList<>();
180     /** Map : Last launched activity => {@link TransitionInfo} */
181     private final ArrayMap<ActivityRecord, TransitionInfo> mLastTransitionInfo = new ArrayMap<>();
182     /** SparseArray : Package UID => {@link PackageCompatStateInfo} */
183     private final SparseArray<PackageCompatStateInfo> mPackageUidToCompatStateInfo =
184             new SparseArray<>(0);
185 
186     private ArtManagerInternal mArtManagerInternal;
187     private final StringBuilder mStringBuilder = new StringBuilder();
188 
189     /**
190      * Due to the global single concurrent launch sequence, all calls to this observer must be made
191      * in-order on the same thread to fulfill the "happens-before" guarantee in LaunchObserver.
192      */
193     private final LaunchObserverRegistryImpl mLaunchObserver;
194     private final ArrayMap<String, Boolean> mLastHibernationStates = new ArrayMap<>();
195     private AppHibernationManagerInternal mAppHibernationManagerInternal;
196 
197     /**
198      * The information created when an intent is incoming but we do not yet know whether it will be
199      * launched successfully.
200      */
201     static final class LaunchingState {
202         /**
203          * The device uptime of {@link #notifyActivityLaunching}. It can be used as a key for
204          * observer to identify which callbacks belong to a launch event.
205          */
206         final long mStartUptimeNs = SystemClock.uptimeNanos();
207         /**
208          * The timestamp of {@link #notifyActivityLaunching}. It is used to provide the time
209          * relative to the wall-time.
210          */
211         final long mStartRealtimeNs = SystemClock.elapsedRealtimeNanos();
212         /** Non-null when a {@link TransitionInfo} is created for this state. */
213         private TransitionInfo mAssociatedTransitionInfo;
214         /** The sequence id for trace. It is used to map the traces before resolving intent. */
215         private static int sTraceSeqId;
216         /** The trace format is "launchingActivity#$seqId:$state(:$packageName)". */
217         String mTraceName;
218 
LaunchingState()219         LaunchingState() {
220             if (!Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
221                 return;
222             }
223             // Use an id because the launching app is not yet known before resolving intent.
224             sTraceSeqId++;
225             mTraceName = "launchingActivity#" + sTraceSeqId;
226             Trace.asyncTraceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, mTraceName, 0);
227         }
228 
stopTrace(boolean abort, TransitionInfo endInfo)229         void stopTrace(boolean abort, TransitionInfo endInfo) {
230             if (mTraceName == null) return;
231             if (!abort && endInfo != mAssociatedTransitionInfo) {
232                 // Multiple TransitionInfo can be associated with the same LaunchingState (e.g. a
233                 // launching activity launches another activity in a different windowing mode or
234                 // display). Only the original associated info can emit a "completed" trace.
235                 return;
236             }
237             Trace.asyncTraceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER, mTraceName, 0);
238             final String launchResult;
239             if (mAssociatedTransitionInfo == null) {
240                 launchResult = ":failed";
241             } else {
242                 final String status;
243                 if (abort) {
244                     status = ":canceled:";
245                 } else if (!mAssociatedTransitionInfo.mProcessSwitch) {
246                     status = ":completed-same-process:";
247                 } else {
248                     if (endInfo.mTransitionType == TYPE_TRANSITION_HOT_LAUNCH) {
249                         status = !endInfo.mRelaunched ? ":completed-hot:" : ":completed-relaunch:";
250                     } else if (endInfo.mTransitionType == TYPE_TRANSITION_WARM_LAUNCH) {
251                         status = ":completed-warm:";
252                     } else {
253                         status = ":completed-cold:";
254                     }
255                 }
256                 launchResult = status + mAssociatedTransitionInfo.mLastLaunchedActivity.packageName;
257             }
258             // Put a supplement trace as the description of the async trace with the same id.
259             Trace.instant(Trace.TRACE_TAG_ACTIVITY_MANAGER, mTraceName + launchResult);
260             mTraceName = null;
261         }
262 
263         @VisibleForTesting
allDrawn()264         boolean allDrawn() {
265             return mAssociatedTransitionInfo != null && mAssociatedTransitionInfo.mIsDrawn;
266         }
267 
hasActiveTransitionInfo()268         boolean hasActiveTransitionInfo() {
269             return mAssociatedTransitionInfo != null;
270         }
271 
contains(ActivityRecord r)272         boolean contains(ActivityRecord r) {
273             return mAssociatedTransitionInfo != null && mAssociatedTransitionInfo.contains(r);
274         }
275     }
276 
277     /** The information created when an activity is confirmed to be launched. */
278     private static final class TransitionInfo {
279         /**
280          * The field to lookup and update an existing transition efficiently between
281          * {@link #notifyActivityLaunching} and {@link #notifyActivityLaunched}.
282          *
283          * @see LaunchingState#mAssociatedTransitionInfo
284          */
285         final LaunchingState mLaunchingState;
286 
287         /** The type can be cold (new process), warm (new activity), or hot (bring to front). */
288         int mTransitionType;
289         /** Whether the process was already running when the transition started. */
290         boolean mProcessRunning;
291         /** whether the process of the launching activity didn't have any active activity. */
292         final boolean mProcessSwitch;
293         /** The process state of the launching activity prior to the launch */
294         final int mProcessState;
295         /** The oom adj score of the launching activity prior to the launch */
296         final int mProcessOomAdj;
297         /** Whether the activity is launched above a visible activity in the same task. */
298         final boolean mIsInTaskActivityStart;
299         /** Whether the last launched activity has reported drawn. */
300         boolean mIsDrawn;
301         /** The latest activity to have been launched. */
302         @NonNull ActivityRecord mLastLaunchedActivity;
303 
304         /** The type of the source that triggers the launch event. */
305         @SourceInfo.SourceType int mSourceType;
306         /** The time from the source event (e.g. touch) to {@link #notifyActivityLaunching}. */
307         int mSourceEventDelayMs = INVALID_DELAY;
308         /** The time from {@link #notifyActivityLaunching} to {@link #notifyTransitionStarting}. */
309         int mCurrentTransitionDelayMs;
310         /** The time from {@link #notifyActivityLaunching} to {@link #notifyStartingWindowDrawn}. */
311         int mStartingWindowDelayMs = INVALID_DELAY;
312         /** The time from {@link #notifyActivityLaunching} to {@link #notifyBindApplication}. */
313         int mBindApplicationDelayMs = INVALID_DELAY;
314         /** Elapsed time from when we launch an activity to when its windows are drawn. */
315         int mWindowsDrawnDelayMs;
316         /** The reason why the transition started (see ActivityManagerInternal.APP_TRANSITION_*). */
317         int mReason = APP_TRANSITION_TIMEOUT;
318         /** The flag ensures that {@link #mStartingWindowDelayMs} is only set once. */
319         boolean mLoggedStartingWindowDrawn;
320         /** If the any app transitions have been logged as starting. */
321         boolean mLoggedTransitionStarting;
322         /** Whether any activity belonging to this transition has relaunched. */
323         boolean mRelaunched;
324 
325         /** Non-null if the application has reported drawn but its window hasn't. */
326         @Nullable Runnable mPendingFullyDrawn;
327         /** Non-null if the trace is active. */
328         @Nullable String mLaunchTraceName;
329         /** Whether this transition info is for an activity that is a part of multi-window. */
330         int mMultiWindowLaunchType = MULTI_WINDOW_LAUNCH_TYPE_UNSPECIFIED;
331 
332         /** @return Non-null if there will be a window drawn event for the launch. */
333         @Nullable
create(@onNull ActivityRecord r, @NonNull LaunchingState launchingState, @Nullable ActivityOptions options, boolean processRunning, boolean processSwitch, int processState, int processOomAdj, boolean newActivityCreated, boolean isInTaskActivityStart, int startResult)334         static TransitionInfo create(@NonNull ActivityRecord r,
335                 @NonNull LaunchingState launchingState, @Nullable ActivityOptions options,
336                 boolean processRunning, boolean processSwitch, int processState, int processOomAdj,
337                 boolean newActivityCreated, boolean isInTaskActivityStart, int startResult) {
338             if (startResult != START_SUCCESS && startResult != START_TASK_TO_FRONT) {
339                 return null;
340             }
341             final int transitionType;
342             if (processRunning) {
343                 transitionType = !newActivityCreated && r.attachedToProcess()
344                         ? TYPE_TRANSITION_HOT_LAUNCH
345                         : TYPE_TRANSITION_WARM_LAUNCH;
346             } else {
347                 // Task may still exist when cold launching an activity and the start result will be
348                 // set to START_TASK_TO_FRONT. Treat this as a COLD launch.
349                 transitionType = TYPE_TRANSITION_COLD_LAUNCH;
350             }
351             return new TransitionInfo(r, launchingState, options, transitionType, processRunning,
352                     processSwitch, processState, processOomAdj, isInTaskActivityStart);
353         }
354 
355         /** Use {@link TransitionInfo#create} instead to ensure the transition type is valid. */
TransitionInfo(ActivityRecord r, LaunchingState launchingState, ActivityOptions options, int transitionType, boolean processRunning, boolean processSwitch, int processState, int processOomAdj, boolean isInTaskActivityStart)356         private TransitionInfo(ActivityRecord r, LaunchingState launchingState,
357                 ActivityOptions options, int transitionType, boolean processRunning,
358                 boolean processSwitch, int processState, int processOomAdj,
359                 boolean isInTaskActivityStart) {
360             mLaunchingState = launchingState;
361             mTransitionType = transitionType;
362             mProcessRunning = processRunning;
363             mProcessSwitch = processSwitch;
364             mProcessState = processState;
365             mProcessOomAdj = processOomAdj;
366             mIsInTaskActivityStart = isInTaskActivityStart;
367             setLatestLaunchedActivity(r);
368             // The launching state can be reused by consecutive launch. Its original association
369             // shouldn't be changed by a separated transition.
370             if (launchingState.mAssociatedTransitionInfo == null) {
371                 launchingState.mAssociatedTransitionInfo = this;
372             }
373             if (options != null) {
374                 final SourceInfo sourceInfo = options.getSourceInfo();
375                 if (sourceInfo != null) {
376                     mSourceType = sourceInfo.type;
377                     mSourceEventDelayMs = (int) (TimeUnit.NANOSECONDS.toMillis(
378                             launchingState.mStartUptimeNs) - sourceInfo.eventTimeMs);
379                 }
380             }
381         }
382 
383         /**
384          * Remembers the latest launched activity to represent the final transition. This also
385          * tracks the activities that should be drawn, so a consecutive launching sequence can be
386          * coalesced as one event.
387          */
setLatestLaunchedActivity(ActivityRecord r)388         void setLatestLaunchedActivity(ActivityRecord r) {
389             if (mLastLaunchedActivity == r) {
390                 return;
391             }
392             if (mLastLaunchedActivity != null) {
393                 if (mLastLaunchedActivity.mLaunchCookie != null) {
394                     ProtoLog.v(WM_DEBUG_WINDOW_TRANSITIONS,
395                             "Transferring launch cookie=%s from=%s(%d) to=%s(%d)",
396                             mLastLaunchedActivity.mLaunchCookie,
397                             mLastLaunchedActivity.packageName,
398                             System.identityHashCode(mLastLaunchedActivity), r.packageName,
399                             System.identityHashCode(r));
400                 }
401                 // Transfer the launch cookie and launch root task because it is a consecutive
402                 // launch event.
403                 r.mLaunchCookie = mLastLaunchedActivity.mLaunchCookie;
404                 mLastLaunchedActivity.mLaunchCookie = null;
405                 r.mLaunchRootTask = mLastLaunchedActivity.mLaunchRootTask;
406                 mLastLaunchedActivity.mLaunchRootTask = null;
407             }
408             mLastLaunchedActivity = r;
409             mIsDrawn = r.isReportedDrawn();
410         }
411 
412         /** Returns {@code true} if the incoming activity can belong to this transition. */
canCoalesce(ActivityRecord r)413         boolean canCoalesce(ActivityRecord r) {
414             if (mLastLaunchedActivity.mDisplayContent != r.mDisplayContent
415                     || mLastLaunchedActivity.getWindowingMode() != r.getWindowingMode()) {
416                 return false;
417             }
418             // The current task should be non-null because it is just launched. While the
419             // last task can be cleared when starting activity with FLAG_ACTIVITY_CLEAR_TASK.
420             final Task lastTask = mLastLaunchedActivity.getTask();
421             final Task currentTask = r.getTask();
422             if (lastTask != null && currentTask != null) {
423                 if (lastTask == currentTask) {
424                     return true;
425                 }
426                 return lastTask.getBounds().equals(currentTask.getBounds());
427             }
428             return mLastLaunchedActivity.isUid(r.launchedFromUid);
429         }
430 
431         /** @return {@code true} if the activity matches a launched activity in this transition. */
contains(ActivityRecord r)432         boolean contains(ActivityRecord r) {
433             return r == mLastLaunchedActivity;
434         }
435 
436         /**
437          * @return {@code true} if the transition info should be sent to MetricsLogger, StatsLog, or
438          *         LaunchObserver.
439          */
isInterestingToLoggerAndObserver()440         boolean isInterestingToLoggerAndObserver() {
441             return mProcessSwitch;
442         }
443 
calculateCurrentDelay()444         int calculateCurrentDelay() {
445             return calculateDelay(SystemClock.uptimeNanos());
446         }
447 
calculateDelay(long timestampNs)448         int calculateDelay(long timestampNs) {
449             // Shouldn't take more than 25 days to launch an app, so int is fine here.
450             return (int) TimeUnit.NANOSECONDS.toMillis(
451                     timestampNs - mLaunchingState.mStartUptimeNs);
452         }
453 
454         @Override
toString()455         public String toString() {
456             return "TransitionInfo{" + Integer.toHexString(System.identityHashCode(this))
457                     + " a=" + mLastLaunchedActivity + " d=" + mIsDrawn + "}";
458         }
459     }
460 
461     static final class TransitionInfoSnapshot {
462         final private ApplicationInfo applicationInfo;
463         final private WindowProcessController processRecord;
464         final String packageName;
465         final String launchedActivityName;
466         final private String launchedActivityLaunchedFromPackage;
467         final private String launchedActivityLaunchToken;
468         final private String launchedActivityAppRecordRequiredAbi;
469         final String launchedActivityShortComponentName;
470         final private String processName;
471         @VisibleForTesting final @SourceInfo.SourceType int sourceType;
472         @VisibleForTesting final int sourceEventDelayMs;
473         final private int reason;
474         final private int startingWindowDelayMs;
475         final private int bindApplicationDelayMs;
476         final int windowsDrawnDelayMs;
477         final int type;
478         final int userId;
479         /**
480          * Elapsed time from when we launch an activity to when the app reported it was
481          * fully drawn. If this is not reported then the value is set to INVALID_DELAY.
482          */
483         final int windowsFullyDrawnDelayMs;
484         final int activityRecordIdHashCode;
485         final boolean relaunched;
486         final long timestampNs;
487         final int multiWindowLaunchType;
488 
TransitionInfoSnapshot(TransitionInfo info)489         private TransitionInfoSnapshot(TransitionInfo info) {
490             this(info, info.mLastLaunchedActivity, INVALID_DELAY);
491         }
492 
TransitionInfoSnapshot(TransitionInfo info, ActivityRecord launchedActivity, int windowsFullyDrawnDelayMs)493         private TransitionInfoSnapshot(TransitionInfo info, ActivityRecord launchedActivity,
494                 int windowsFullyDrawnDelayMs) {
495             applicationInfo = launchedActivity.info.applicationInfo;
496             packageName = launchedActivity.packageName;
497             launchedActivityName = launchedActivity.info.name;
498             launchedActivityLaunchedFromPackage = launchedActivity.launchedFromPackage;
499             launchedActivityLaunchToken = launchedActivity.info.launchToken;
500             launchedActivityAppRecordRequiredAbi = launchedActivity.app == null
501                     ? null
502                     : launchedActivity.app.getRequiredAbi();
503             reason = info.mReason;
504             sourceEventDelayMs = info.mSourceEventDelayMs;
505             startingWindowDelayMs = info.mStartingWindowDelayMs;
506             bindApplicationDelayMs = info.mBindApplicationDelayMs;
507             windowsDrawnDelayMs = info.mWindowsDrawnDelayMs;
508             type = info.mTransitionType;
509             processRecord = launchedActivity.app;
510             processName = launchedActivity.processName;
511             sourceType = info.mSourceType;
512             userId = launchedActivity.mUserId;
513             launchedActivityShortComponentName = launchedActivity.shortComponentName;
514             activityRecordIdHashCode = System.identityHashCode(launchedActivity);
515             this.windowsFullyDrawnDelayMs = windowsFullyDrawnDelayMs;
516             relaunched = info.mRelaunched;
517             timestampNs = info.mLaunchingState.mStartRealtimeNs;
518             multiWindowLaunchType = info.mMultiWindowLaunchType;
519         }
520 
getLaunchState()521         @WaitResult.LaunchState int getLaunchState() {
522             switch (type) {
523                 case TYPE_TRANSITION_WARM_LAUNCH:
524                     return LAUNCH_STATE_WARM;
525                 case TYPE_TRANSITION_HOT_LAUNCH:
526                     return relaunched ? LAUNCH_STATE_RELAUNCH : LAUNCH_STATE_HOT;
527                 case TYPE_TRANSITION_COLD_LAUNCH:
528                     return LAUNCH_STATE_COLD;
529                 default:
530                     return -1;
531             }
532         }
533 
isInterestedToEventLog()534         boolean isInterestedToEventLog() {
535             return type == TYPE_TRANSITION_WARM_LAUNCH || type == TYPE_TRANSITION_COLD_LAUNCH;
536         }
537 
getPackageOptimizationInfo(ArtManagerInternal artManagerInternal)538         PackageOptimizationInfo getPackageOptimizationInfo(ArtManagerInternal artManagerInternal) {
539             return artManagerInternal == null || launchedActivityAppRecordRequiredAbi == null
540                     ? PackageOptimizationInfo.createWithNoInfo()
541                     : artManagerInternal.getPackageOptimizationInfo(applicationInfo,
542                             launchedActivityAppRecordRequiredAbi, launchedActivityName);
543         }
544     }
545 
546     /** Information about the App Compat state logging associated with a package UID . */
547     private static final class PackageCompatStateInfo {
548         /** All activities that have a visible state. */
549         final ArrayList<ActivityRecord> mVisibleActivities = new ArrayList<>();
550         /** The last logged state. */
551         int mLastLoggedState = APP_COMPAT_STATE_CHANGED__STATE__NOT_VISIBLE;
552         @Nullable ActivityRecord mLastLoggedActivity;
553     }
554 
ActivityMetricsLogger(ActivityTaskSupervisor supervisor, Looper looper)555     ActivityMetricsLogger(ActivityTaskSupervisor supervisor, Looper looper) {
556         mLastLogTimeSecs = SystemClock.elapsedRealtime() / 1000;
557         mSupervisor = supervisor;
558         mLaunchObserver = new LaunchObserverRegistryImpl(looper);
559     }
560 
logWindowState(String state, int durationSecs)561     private void logWindowState(String state, int durationSecs) {
562         mMetricsLogger.count(state, durationSecs);
563     }
564 
logWindowState()565     void logWindowState() {
566         final long now = SystemClock.elapsedRealtime() / 1000;
567         if (mWindowState != WINDOW_STATE_INVALID) {
568             // We log even if the window state hasn't changed, because the user might remain in
569             // home/fullscreen move forever and we would like to track this kind of behavior
570             // too.
571             mLoggerHandler.sendMessage(PooledLambda.obtainMessage(
572                     ActivityMetricsLogger::logWindowState, this,
573                     TRON_WINDOW_STATE_VARZ_STRINGS[mWindowState], (int) (now - mLastLogTimeSecs)));
574         }
575         mLastLogTimeSecs = now;
576 
577         mWindowState = WINDOW_STATE_INVALID;
578         final Task focusedTask = mSupervisor.mRootWindowContainer.getTopDisplayFocusedRootTask();
579         if (focusedTask == null)  return;
580         if (focusedTask.isActivityTypeAssistant()) {
581             mWindowState = WINDOW_STATE_ASSISTANT;
582             return;
583         }
584 
585         @WindowingMode final int windowingMode = focusedTask.getWindowingMode();
586         switch (windowingMode) {
587             case WINDOWING_MODE_FULLSCREEN:
588                 mWindowState = WINDOW_STATE_STANDARD;
589                 break;
590             case WINDOWING_MODE_FREEFORM:
591                 mWindowState = WINDOW_STATE_FREEFORM;
592                 break;
593             case WINDOWING_MODE_MULTI_WINDOW:
594                 mWindowState = WINDOW_STATE_MULTI_WINDOW;
595                 break;
596             default:
597                 if (windowingMode != WINDOWING_MODE_UNDEFINED) {
598                     Slog.wtf(TAG, "Unknown windowing mode for task=" + focusedTask
599                             + " windowingMode=" + windowingMode);
600                 }
601         }
602     }
603 
604     /** @return Non-null {@link TransitionInfo} if the activity is found in an active transition. */
605     @Nullable
getActiveTransitionInfo(ActivityRecord r)606     private TransitionInfo getActiveTransitionInfo(ActivityRecord r) {
607         for (int i = mTransitionInfoList.size() - 1; i >= 0; i--) {
608             final TransitionInfo info = mTransitionInfoList.get(i);
609             if (info.contains(r)) {
610                 return info;
611             }
612         }
613         return null;
614     }
615 
616     /**
617      * This method should be only used by starting recents and starting from recents, or internal
618      * tests. Because it doesn't lookup caller and always creates a new launching state.
619      *
620      * @see #notifyActivityLaunching(Intent, ActivityRecord, int)
621      */
notifyActivityLaunching(Intent intent)622     LaunchingState notifyActivityLaunching(Intent intent) {
623         return notifyActivityLaunching(intent, null /* caller */, IGNORE_CALLER);
624     }
625 
626     /**
627      * Notifies the tracker at the earliest possible point when we are starting to launch an
628      * activity. The caller must ensure that {@link #notifyActivityLaunched} will be called later
629      * with the returned {@link LaunchingState}. If the caller is found in an active transition,
630      * it will be considered as consecutive launch and coalesced into the active transition.
631      */
notifyActivityLaunching(Intent intent, @Nullable ActivityRecord caller, int callingUid)632     LaunchingState notifyActivityLaunching(Intent intent, @Nullable ActivityRecord caller,
633             int callingUid) {
634         TransitionInfo existingInfo = null;
635         if (callingUid != IGNORE_CALLER) {
636             // Associate the launching event to an active transition if the caller is found in its
637             // launched activities.
638             for (int i = mTransitionInfoList.size() - 1; i >= 0; i--) {
639                 final TransitionInfo info = mTransitionInfoList.get(i);
640                 if (caller != null && info.contains(caller)) {
641                     existingInfo = info;
642                     break;
643                 }
644                 if (existingInfo == null && callingUid == info.mLastLaunchedActivity.getUid()) {
645                     // Fallback to check the most recent matched uid for the case that the caller is
646                     // not an activity.
647                     existingInfo = info;
648                 }
649             }
650         }
651         if (DEBUG_METRICS) {
652             Slog.i(TAG, "notifyActivityLaunching intent=" + intent
653                     + " existingInfo=" + existingInfo);
654         }
655 
656         if (existingInfo == null) {
657             final LaunchingState launchingState = new LaunchingState();
658             // Only notify the observer for a new launching event.
659             launchObserverNotifyIntentStarted(intent, launchingState.mStartUptimeNs);
660             return launchingState;
661         }
662         return existingInfo.mLaunchingState;
663     }
664 
665     /**
666      * Notifies the tracker that the activity is actually launching.
667      *
668      * @param launchingState The launching state to track the new or active transition.
669      * @param resultCode One of the {@link android.app.ActivityManager}.START_* flags, indicating
670      *                   the result of the launch.
671      * @param launchedActivity The activity that is being launched
672      * @param newActivityCreated Whether a new activity instance is created.
673      * @param options The given options of the launching activity.
674      */
notifyActivityLaunched(@onNull LaunchingState launchingState, int resultCode, boolean newActivityCreated, @Nullable ActivityRecord launchedActivity, @Nullable ActivityOptions options)675     void notifyActivityLaunched(@NonNull LaunchingState launchingState, int resultCode,
676             boolean newActivityCreated, @Nullable ActivityRecord launchedActivity,
677             @Nullable ActivityOptions options) {
678         if (launchedActivity == null || launchedActivity.getTask() == null) {
679             // The launch is aborted, e.g. intent not resolved, class not found.
680             abort(launchingState, "nothing launched");
681             return;
682         }
683 
684         final WindowProcessController processRecord = launchedActivity.app != null
685                 ? launchedActivity.app
686                 : mSupervisor.mService.getProcessController(
687                         launchedActivity.processName, launchedActivity.info.applicationInfo.uid);
688         // Whether the process that will contains the activity is already running.
689         final boolean processRunning = processRecord != null;
690         // We consider this a "process switch" if the process of the activity that gets launched
691         // didn't have an activity that was in started state. In this case, we assume that lot
692         // of caches might be purged so the time until it produces the first frame is very
693         // interesting.
694         final boolean processSwitch = !processRunning
695                 || !processRecord.hasStartedActivity(launchedActivity);
696         final int processState;
697         final int processOomAdj;
698         if (processRunning) {
699             processState = processRecord.getCurrentProcState();
700             processOomAdj = processRecord.getCurrentAdj();
701         } else {
702             processState = PROCESS_STATE_NONEXISTENT;
703             processOomAdj = INVALID_ADJ;
704         }
705 
706         final TransitionInfo info = launchingState.mAssociatedTransitionInfo;
707         if (DEBUG_METRICS) {
708             Slog.i(TAG, "notifyActivityLaunched" + " resultCode=" + resultCode
709                     + " launchedActivity=" + launchedActivity + " processRunning=" + processRunning
710                     + " processSwitch=" + processSwitch
711                     + " processState=" + processState
712                     + " processOomAdj=" + processOomAdj
713                     + " newActivityCreated=" + newActivityCreated + " info=" + info);
714         }
715 
716         if (launchedActivity.isReportedDrawn() && launchedActivity.isVisible()) {
717             // Launched activity is already visible. We cannot measure windows drawn delay.
718             abort(launchingState, "launched activity already visible");
719             return;
720         }
721 
722         // If the launched activity is started from an existing active transition, it will be put
723         // into the transition info.
724         if (info != null && info.canCoalesce(launchedActivity)) {
725             if (DEBUG_METRICS) Slog.i(TAG, "notifyActivityLaunched consecutive launch");
726 
727             final boolean crossPackage =
728                     !info.mLastLaunchedActivity.packageName.equals(launchedActivity.packageName);
729             // The trace name uses package name so different packages should be separated.
730             if (crossPackage) {
731                 stopLaunchTrace(info);
732             }
733 
734             mLastTransitionInfo.remove(info.mLastLaunchedActivity);
735             // Coalesce multiple (trampoline) activities from a single sequence together.
736             info.setLatestLaunchedActivity(launchedActivity);
737             // Update the latest one so it can be found when reporting fully-drawn.
738             mLastTransitionInfo.put(launchedActivity, info);
739 
740             if (crossPackage) {
741                 startLaunchTrace(info);
742             }
743             scheduleCheckActivityToBeDrawnIfSleeping(launchedActivity);
744             return;
745         }
746 
747         final boolean isInTaskActivityStart = launchedActivity.getTask().isVisible();
748         final TransitionInfo newInfo = TransitionInfo.create(launchedActivity, launchingState,
749                 options, processRunning, processSwitch, processState, processOomAdj,
750                 newActivityCreated, isInTaskActivityStart, resultCode);
751         if (newInfo == null) {
752             abort(launchingState, "unrecognized launch");
753             return;
754         }
755 
756         // Look at all other transition infos and mark them as a split pair if they belong to
757         // adjacent tasks
758         updateSplitPairLaunches(newInfo);
759 
760         if (DEBUG_METRICS) Slog.i(TAG, "notifyActivityLaunched successful");
761         // A new launch sequence has begun. Start tracking it.
762         mTransitionInfoList.add(newInfo);
763         mLastTransitionInfo.put(launchedActivity, newInfo);
764         startLaunchTrace(newInfo);
765         if (newInfo.isInterestingToLoggerAndObserver()) {
766             launchObserverNotifyActivityLaunched(newInfo);
767         } else {
768             // As abort for no process switch.
769             launchObserverNotifyIntentFailed(newInfo.mLaunchingState.mStartUptimeNs);
770         }
771         if (Intent.ACTION_PROCESS_TEXT.equals(newInfo.mLastLaunchedActivity.intent.getAction())) {
772             mLoggerHandler.post(PooledLambda.obtainRunnable(FrameworkStatsLog::write,
773                     FrameworkStatsLog.PROCESS_TEXT_ACTION_LAUNCHED_REPORTED,
774                     launchedActivity.launchedFromUid, launchedActivity.getUid()));
775         }
776         scheduleCheckActivityToBeDrawnIfSleeping(launchedActivity);
777 
778         // If the previous transitions are no longer visible, abort them to avoid counting the
779         // launch time when resuming from back stack. E.g. launch 2 independent tasks in a short
780         // time, the transition info of the first task should not keep active until it becomes
781         // visible such as after the top task is finished.
782         for (int i = mTransitionInfoList.size() - 2; i >= 0; i--) {
783             final TransitionInfo prevInfo = mTransitionInfoList.get(i);
784             if (prevInfo.mIsDrawn || !prevInfo.mLastLaunchedActivity.isVisibleRequested()) {
785                 scheduleCheckActivityToBeDrawn(prevInfo.mLastLaunchedActivity, 0 /* delay */);
786             }
787         }
788     }
789 
790     /**
791      * Updates all transition infos including the given {@param info} if they are a part of a
792      * split pair launch.
793      */
updateSplitPairLaunches(@onNull TransitionInfo info)794     private void updateSplitPairLaunches(@NonNull TransitionInfo info) {
795         final Task launchedActivityTask = info.mLastLaunchedActivity.getTask();
796         final Task launchedSplitRootTask = launchedActivityTask.getTaskWithAdjacent();
797         if (launchedSplitRootTask == null) {
798             // Not a part of a split pair
799             return;
800         }
801 
802         for (int i = mTransitionInfoList.size() - 1; i >= 0; i--) {
803             final TransitionInfo otherInfo = mTransitionInfoList.get(i);
804             if (otherInfo == info) {
805                 continue;
806             }
807             final Task otherTask = otherInfo.mLastLaunchedActivity.getTask();
808             // The adjacent task is the split root in which activities are started
809             final boolean isDescendantOfAdjacent = launchedSplitRootTask.forOtherAdjacentTasks(
810                     otherTask::isDescendantOf);
811             if (isDescendantOfAdjacent) {
812                 if (DEBUG_METRICS) {
813                     Slog.i(TAG, "Found adjacent tasks t1=" + launchedActivityTask.mTaskId
814                             + " t2=" + otherTask.mTaskId);
815                 }
816                 // These tasks are adjacent, so mark them as such
817                 info.mMultiWindowLaunchType = MULTI_WINDOW_LAUNCH_TYPE_APP_PAIR;
818                 otherInfo.mMultiWindowLaunchType = MULTI_WINDOW_LAUNCH_TYPE_APP_PAIR;
819             }
820         }
821     }
822 
scheduleCheckActivityToBeDrawnIfSleeping(@onNull ActivityRecord r)823     private void scheduleCheckActivityToBeDrawnIfSleeping(@NonNull ActivityRecord r) {
824         if (r.mDisplayContent.isSleeping()) {
825             // It is unknown whether the activity can be drawn or not, e.g. it depends on the
826             // keyguard states and the attributes or flags set by the activity. If the activity
827             // keeps invisible in the grace period, the tracker will be cancelled so it won't get
828             // a very long launch time that takes unlocking as the end of launch.
829             scheduleCheckActivityToBeDrawn(r, UNKNOWN_VISIBILITY_CHECK_DELAY_MS);
830         }
831     }
832 
833     /**
834      * Notifies the tracker that all windows of the app have been drawn.
835      *
836      * @return Non-null info if the activity was pending to draw, otherwise it might have been set
837      *         to invisible (removed from active transition) or it was already drawn.
838      */
839     @Nullable
notifyWindowsDrawn(@onNull ActivityRecord r)840     TransitionInfoSnapshot notifyWindowsDrawn(@NonNull ActivityRecord r) {
841         if (DEBUG_METRICS) Slog.i(TAG, "notifyWindowsDrawn " + r);
842 
843         final long timestampNs = SystemClock.uptimeNanos();
844         final TransitionInfo info = getActiveTransitionInfo(r);
845         if (info == null || info.mIsDrawn) {
846             if (DEBUG_METRICS) Slog.i(TAG, "notifyWindowsDrawn not pending drawn " + info);
847             return null;
848         }
849         // Always calculate the delay because the caller may need to know the individual drawn time.
850         info.mWindowsDrawnDelayMs = info.calculateDelay(timestampNs);
851         info.mIsDrawn = true;
852         final TransitionInfoSnapshot infoSnapshot = new TransitionInfoSnapshot(info);
853         if (info.mLoggedTransitionStarting || !r.mTransitionController.isCollecting(r)) {
854             done(false /* abort */, info, "notifyWindowsDrawn", timestampNs);
855         }
856 
857         if (android.app.Flags.appStartInfoTimestamps()) {
858             final int pid = r.getPid();
859             // Log here to match StatsD for time to first frame.
860             mLoggerHandler.post(
861                     () -> mSupervisor.mService.mWindowManager.mAmInternal.addStartInfoTimestamp(
862                             ApplicationStartInfo.START_TIMESTAMP_FIRST_FRAME,
863                             timestampNs, infoSnapshot.applicationInfo.uid, pid,
864                             infoSnapshot.userId));
865         }
866 
867         return infoSnapshot;
868     }
869 
870     /**
871      * Notifies the tracker that the starting window was drawn.
872      */
notifyStartingWindowDrawn(@onNull ActivityRecord r)873     void notifyStartingWindowDrawn(@NonNull ActivityRecord r) {
874         final TransitionInfo info = getActiveTransitionInfo(r);
875         if (info == null || info.mLoggedStartingWindowDrawn) {
876             return;
877         }
878         if (DEBUG_METRICS) Slog.i(TAG, "notifyStartingWindowDrawn " + r);
879         info.mLoggedStartingWindowDrawn = true;
880         info.mStartingWindowDelayMs = info.calculateCurrentDelay();
881     }
882 
883     /**
884      * Notifies the tracker that the app transition is starting.
885      *
886      * @param activityToReason A map from activity to a reason integer, which must be on of
887      *                         ActivityTaskManagerInternal.APP_TRANSITION_* reasons.
888      */
notifyTransitionStarting(ArrayMap<WindowContainer, Integer> activityToReason)889     void notifyTransitionStarting(ArrayMap<WindowContainer, Integer> activityToReason) {
890         if (DEBUG_METRICS) Slog.i(TAG, "notifyTransitionStarting " + activityToReason);
891 
892         final long timestampNs = SystemClock.uptimeNanos();
893         for (int index = activityToReason.size() - 1; index >= 0; index--) {
894             final WindowContainer<?> wc = activityToReason.keyAt(index);
895             final ActivityRecord activity = wc.asActivityRecord();
896             final ActivityRecord r = activity != null ? activity
897                     : wc.getTopActivity(false /* includeFinishing */, true /* includeOverlays */);
898             final TransitionInfo info = getActiveTransitionInfo(r);
899             if (info == null || info.mLoggedTransitionStarting) {
900                 // Ignore any subsequent notifyTransitionStarting.
901                 continue;
902             }
903             if (DEBUG_METRICS) {
904                 Slog.i(TAG, "notifyTransitionStarting activity=" + wc + " info=" + info);
905             }
906 
907             info.mCurrentTransitionDelayMs = info.calculateDelay(timestampNs);
908             info.mReason = activityToReason.valueAt(index);
909             info.mLoggedTransitionStarting = true;
910             if (info.mIsDrawn) {
911                 done(false /* abort */, info, "notifyTransitionStarting drawn", timestampNs);
912             }
913         }
914     }
915 
notifyActivityRelaunched(ActivityRecord r)916     void notifyActivityRelaunched(ActivityRecord r) {
917         final TransitionInfo info = getActiveTransitionInfo(r);
918         if (info != null) {
919             info.mRelaunched = true;
920         }
921     }
922 
923     /** Makes sure that the reference to the removed activity is cleared. */
notifyActivityRemoved(@onNull ActivityRecord r)924     void notifyActivityRemoved(@NonNull ActivityRecord r) {
925         mLastTransitionInfo.remove(r);
926         final TransitionInfo info = getActiveTransitionInfo(r);
927         if (info != null) {
928             abort(info, "removed");
929         }
930 
931         final int packageUid = r.info.applicationInfo.uid;
932         final PackageCompatStateInfo compatStateInfo = mPackageUidToCompatStateInfo.get(packageUid);
933         if (compatStateInfo == null) {
934             return;
935         }
936 
937         compatStateInfo.mVisibleActivities.remove(r);
938         if (compatStateInfo.mLastLoggedActivity == r) {
939             compatStateInfo.mLastLoggedActivity = null;
940         }
941     }
942 
943     /**
944      * Notifies the tracker that the visibility of an app is changing.
945      *
946      * @param r the app that is changing its visibility
947      */
notifyVisibilityChanged(@onNull ActivityRecord r)948     void notifyVisibilityChanged(@NonNull ActivityRecord r) {
949         final TransitionInfo info = getActiveTransitionInfo(r);
950         if (info == null) {
951             return;
952         }
953         if (DEBUG_METRICS) {
954             Slog.i(TAG, "notifyVisibilityChanged " + r + " visible=" + r.isVisibleRequested()
955                     + " state=" + r.getState() + " finishing=" + r.finishing);
956         }
957         if (r.isState(ActivityRecord.State.RESUMED) && r.mDisplayContent.isSleeping()) {
958             // The activity may be launching while keyguard is locked. The keyguard may be dismissed
959             // after the activity finished relayout, so skip the visibility check to avoid aborting
960             // the tracking of launch event.
961             return;
962         }
963         if (!r.isVisibleRequested() || r.finishing) {
964             // Check if the tracker can be cancelled because the last launched activity may be
965             // no longer visible.
966             scheduleCheckActivityToBeDrawn(r, 0 /* delay */);
967         }
968     }
969 
scheduleCheckActivityToBeDrawn(@onNull ActivityRecord r, long delay)970     private void scheduleCheckActivityToBeDrawn(@NonNull ActivityRecord r, long delay) {
971         // The activity and its task are passed separately because it is possible that the activity
972         // is removed from the task later.
973         r.mAtmService.mH.sendMessageDelayed(PooledLambda.obtainMessage(
974                 ActivityMetricsLogger::checkActivityToBeDrawn, this, r.getTask(), r), delay);
975     }
976 
977     /** Cancels the tracking of launch if there won't be an activity to be drawn. */
checkActivityToBeDrawn(Task t, ActivityRecord r)978     private void checkActivityToBeDrawn(Task t, ActivityRecord r) {
979         synchronized (mSupervisor.mService.mGlobalLock) {
980             final TransitionInfo info = getActiveTransitionInfo(r);
981 
982             // If we have an active transition that's waiting on a certain activity that will be
983             // invisible now, we'll never get onWindowsDrawn, so abort the transition if necessary.
984 
985             // We have no active transitions. Or the notified activity whose visibility changed is
986             // no longer the launched activity, then we can still wait to get onWindowsDrawn.
987             if (info == null) {
988                 return;
989             }
990 
991             // If the task of the launched activity contains any activity to be drawn, then the
992             // window drawn event should report later to complete the transition. Otherwise all
993             // activities in this task may be finished, invisible or drawn, so the transition event
994             // should be cancelled.
995             if (t != null && t.forAllActivities(
996                     a -> a.isVisibleRequested() && !a.isReportedDrawn() && !a.finishing)) {
997                 return;
998             }
999 
1000             if (DEBUG_METRICS) Slog.i(TAG, "checkActivityToBeDrawn cancels activity=" + r);
1001             logAppTransitionCancel(info);
1002             abort(info, "checkActivityToBeDrawn (invisible or drawn already)");
1003         }
1004     }
1005 
1006     @Nullable
getAppHibernationManagerInternal()1007     private AppHibernationManagerInternal getAppHibernationManagerInternal() {
1008         if (!AppHibernationService.isAppHibernationEnabled()) return null;
1009         if (mAppHibernationManagerInternal == null) {
1010             mAppHibernationManagerInternal =
1011                     LocalServices.getService(AppHibernationManagerInternal.class);
1012         }
1013         return mAppHibernationManagerInternal;
1014     }
1015 
1016     /**
1017      * Notifies the tracker before the package is unstopped because of launching activity.
1018      * @param packageName The package to be unstopped.
1019      */
notifyBeforePackageUnstopped(@onNull String packageName)1020     void notifyBeforePackageUnstopped(@NonNull String packageName) {
1021         final AppHibernationManagerInternal ahmInternal = getAppHibernationManagerInternal();
1022         if (ahmInternal != null) {
1023             mLastHibernationStates.put(packageName, ahmInternal.isHibernatingGlobally(packageName));
1024         }
1025     }
1026 
1027     /**
1028      * Notifies the tracker that we called immediately before we call bindApplication on the client.
1029      *
1030      * @param appInfo The client into which we'll call bindApplication.
1031      */
notifyBindApplication(ApplicationInfo appInfo)1032     void notifyBindApplication(ApplicationInfo appInfo) {
1033         for (int i = mTransitionInfoList.size() - 1; i >= 0; i--) {
1034             final TransitionInfo info = mTransitionInfoList.get(i);
1035 
1036             // App isn't attached to record yet, so match with info.
1037             if (info.mLastLaunchedActivity.info.applicationInfo == appInfo) {
1038                 info.mBindApplicationDelayMs = info.calculateCurrentDelay();
1039                 if (info.mProcessRunning) {
1040                     // It was HOT/WARM launch, but the process was died somehow right after the
1041                     // launch request.
1042                     info.mProcessRunning = false;
1043                     info.mTransitionType = TYPE_TRANSITION_COLD_LAUNCH;
1044                     final String msg = "Process " + info.mLastLaunchedActivity.info.processName
1045                             + " restarted";
1046                     Slog.i(TAG, msg);
1047                     if (info.mLaunchingState.mTraceName != null) {
1048                         Trace.instant(Trace.TRACE_TAG_ACTIVITY_MANAGER, msg + "#"
1049                                 + LaunchingState.sTraceSeqId);
1050                     }
1051                 }
1052             }
1053         }
1054     }
1055 
abort(@onNull LaunchingState state, String cause)1056     private void abort(@NonNull LaunchingState state, String cause) {
1057         if (state.mAssociatedTransitionInfo != null) {
1058             abort(state.mAssociatedTransitionInfo, cause);
1059             return;
1060         }
1061         if (DEBUG_METRICS) Slog.i(TAG, "abort launch cause=" + cause);
1062         state.stopTrace(true /* abort */, null /* endInfo */);
1063         launchObserverNotifyIntentFailed(state.mStartUptimeNs);
1064     }
1065 
1066     /** Aborts tracking of current launch metrics. */
abort(@onNull TransitionInfo info, String cause)1067     private void abort(@NonNull TransitionInfo info, String cause) {
1068         done(true /* abort */, info, cause, 0L /* timestampNs */);
1069     }
1070 
1071     /** Called when the given transition (info) is no longer active. */
done(boolean abort, @NonNull TransitionInfo info, String cause, long timestampNs)1072     private void done(boolean abort, @NonNull TransitionInfo info, String cause,
1073             long timestampNs) {
1074         if (DEBUG_METRICS) {
1075             Slog.i(TAG, "done abort=" + abort + " cause=" + cause + " timestamp=" + timestampNs
1076                     + " info=" + info);
1077         }
1078         info.mLaunchingState.stopTrace(abort, info);
1079         stopLaunchTrace(info);
1080         final Boolean isHibernating =
1081                 mLastHibernationStates.remove(info.mLastLaunchedActivity.packageName);
1082         if (abort) {
1083             mLastTransitionInfo.remove(info.mLastLaunchedActivity);
1084             mSupervisor.stopWaitingForActivityVisible(info.mLastLaunchedActivity);
1085             launchObserverNotifyActivityLaunchCancelled(info);
1086         } else {
1087             if (info.isInterestingToLoggerAndObserver()) {
1088                 launchObserverNotifyActivityLaunchFinished(info, timestampNs);
1089             }
1090             logAppTransitionFinished(info, isHibernating != null ? isHibernating : false);
1091             if (info.mReason == APP_TRANSITION_RECENTS_ANIM) {
1092                 logRecentsAnimationLatency(info);
1093             }
1094         }
1095         mTransitionInfoList.remove(info);
1096     }
1097 
logAppTransitionCancel(TransitionInfo info)1098     private void logAppTransitionCancel(TransitionInfo info) {
1099         final int type = info.mTransitionType;
1100         final ActivityRecord activity = info.mLastLaunchedActivity;
1101         final LogMaker builder = new LogMaker(APP_TRANSITION_CANCELLED);
1102         builder.setPackageName(activity.packageName);
1103         builder.setType(type);
1104         builder.addTaggedData(FIELD_CLASS_NAME, activity.info.name);
1105         mMetricsLogger.write(builder);
1106         FrameworkStatsLog.write(
1107                 FrameworkStatsLog.APP_START_CANCELED,
1108                 activity.info.applicationInfo.uid,
1109                 activity.packageName,
1110                 getAppStartTransitionType(type, info.mRelaunched),
1111                 activity.info.name);
1112         if (DEBUG_METRICS) {
1113             Slog.i(TAG, String.format("APP_START_CANCELED(%s, %s, %s, %s)",
1114                     activity.info.applicationInfo.uid,
1115                     activity.packageName,
1116                     getAppStartTransitionType(type, info.mRelaunched),
1117                     activity.info.name));
1118         }
1119     }
1120 
logAppTransitionFinished(@onNull TransitionInfo info, boolean isHibernating)1121     private void logAppTransitionFinished(@NonNull TransitionInfo info, boolean isHibernating) {
1122         if (DEBUG_METRICS) Slog.i(TAG, "logging finished transition " + info);
1123 
1124         // Take a snapshot of the transition info before sending it to the handler for logging.
1125         // This will avoid any races with other operations that modify the ActivityRecord.
1126         final TransitionInfoSnapshot infoSnapshot = new TransitionInfoSnapshot(info);
1127         final boolean isOpaque = info.mLastLaunchedActivity.mStyleFillsParent;
1128         final long uptimeNs = info.mLaunchingState.mStartUptimeNs;
1129         final int transitionDelay = info.mCurrentTransitionDelayMs;
1130         final int processState = info.mProcessState;
1131         final int processOomAdj = info.mProcessOomAdj;
1132         mLoggerHandler.post(() -> {
1133             if (info.isInterestingToLoggerAndObserver()) {
1134                 logAppTransition(uptimeNs, transitionDelay, infoSnapshot, isHibernating,
1135                         processState, processOomAdj);
1136             }
1137             if (info.mIsInTaskActivityStart) {
1138                 logInTaskActivityStart(infoSnapshot, isOpaque, transitionDelay);
1139             }
1140             if (infoSnapshot.isInterestedToEventLog()) {
1141                 logAppDisplayed(infoSnapshot);
1142             }
1143         });
1144         if (info.mPendingFullyDrawn != null) {
1145             info.mPendingFullyDrawn.run();
1146         }
1147 
1148         info.mLastLaunchedActivity.info.launchToken = null;
1149     }
1150 
1151     // This gets called on another thread without holding the activity manager lock.
logAppTransition(long transitionDeviceUptimeNs, int currentTransitionDelayMs, TransitionInfoSnapshot info, boolean isHibernating, int processState, int processOomAdj)1152     private void logAppTransition(long transitionDeviceUptimeNs,
1153             int currentTransitionDelayMs, TransitionInfoSnapshot info, boolean isHibernating,
1154             int processState, int processOomAdj) {
1155         final LogMaker builder = new LogMaker(APP_TRANSITION);
1156         builder.setPackageName(info.packageName);
1157         builder.setType(info.type);
1158         builder.addTaggedData(FIELD_CLASS_NAME, info.launchedActivityName);
1159         final boolean isInstantApp = info.applicationInfo.isInstantApp();
1160         if (info.launchedActivityLaunchedFromPackage != null) {
1161             builder.addTaggedData(APP_TRANSITION_CALLING_PACKAGE_NAME,
1162                     info.launchedActivityLaunchedFromPackage);
1163         }
1164         String launchToken = info.launchedActivityLaunchToken;
1165         if (launchToken != null) {
1166             builder.addTaggedData(FIELD_INSTANT_APP_LAUNCH_TOKEN, launchToken);
1167         }
1168         builder.addTaggedData(APP_TRANSITION_IS_EPHEMERAL, isInstantApp ? 1 : 0);
1169         builder.addTaggedData(APP_TRANSITION_DEVICE_UPTIME_SECONDS,
1170                 TimeUnit.NANOSECONDS.toSeconds(transitionDeviceUptimeNs));
1171         builder.addTaggedData(APP_TRANSITION_DELAY_MS, currentTransitionDelayMs);
1172         builder.setSubtype(info.reason);
1173         if (info.startingWindowDelayMs != INVALID_DELAY) {
1174             builder.addTaggedData(APP_TRANSITION_STARTING_WINDOW_DELAY_MS,
1175                     info.startingWindowDelayMs);
1176         }
1177         if (info.bindApplicationDelayMs != INVALID_DELAY) {
1178             builder.addTaggedData(APP_TRANSITION_BIND_APPLICATION_DELAY_MS,
1179                     info.bindApplicationDelayMs);
1180         }
1181         builder.addTaggedData(APP_TRANSITION_WINDOWS_DRAWN_DELAY_MS, info.windowsDrawnDelayMs);
1182         final PackageOptimizationInfo packageOptimizationInfo =
1183                 info.getPackageOptimizationInfo(getArtManagerInternal());
1184         builder.addTaggedData(PACKAGE_OPTIMIZATION_COMPILATION_REASON,
1185                 packageOptimizationInfo.getCompilationReason());
1186         builder.addTaggedData(PACKAGE_OPTIMIZATION_COMPILATION_FILTER,
1187                 packageOptimizationInfo.getCompilationFilter());
1188         mMetricsLogger.write(builder);
1189 
1190         // Incremental info
1191         boolean isIncremental = false, isLoading = false;
1192         final String codePath = info.applicationInfo.getCodePath();
1193         if (codePath != null && IncrementalManager.isIncrementalPath(codePath)) {
1194             isIncremental = true;
1195             isLoading = isIncrementalLoading(info.packageName, info.userId);
1196         }
1197         final boolean stopped = wasStoppedNeedsLogging(info);
1198         final int packageState = stopped
1199                 ? APP_START_OCCURRED__PACKAGE_STOPPED_STATE__PACKAGE_STATE_STOPPED
1200                 : APP_START_OCCURRED__PACKAGE_STOPPED_STATE__PACKAGE_STATE_NORMAL;
1201 
1202         final boolean firstLaunch = wasFirstLaunch(info);
1203         FrameworkStatsLog.write(
1204                 FrameworkStatsLog.APP_START_OCCURRED,
1205                 info.applicationInfo.uid,
1206                 info.packageName,
1207                 getAppStartTransitionType(info.type, info.relaunched),
1208                 info.launchedActivityName,
1209                 info.launchedActivityLaunchedFromPackage,
1210                 isInstantApp,
1211                 0 /* deprecated transitionDeviceUptimeMs */,
1212                 info.reason,
1213                 currentTransitionDelayMs,
1214                 info.startingWindowDelayMs,
1215                 info.bindApplicationDelayMs,
1216                 info.windowsDrawnDelayMs,
1217                 launchToken,
1218                 packageOptimizationInfo.getCompilationReason(),
1219                 packageOptimizationInfo.getCompilationFilter(),
1220                 info.sourceType,
1221                 info.sourceEventDelayMs,
1222                 isHibernating,
1223                 isIncremental,
1224                 isLoading,
1225                 info.launchedActivityName.hashCode(),
1226                 TimeUnit.NANOSECONDS.toMillis(info.timestampNs),
1227                 processState,
1228                 processOomAdj,
1229                 packageState,
1230                 false, // is_xr_activity
1231                 firstLaunch,
1232                 0L /* TODO: stoppedDuration */,
1233                 info.multiWindowLaunchType);
1234         // Reset the stopped state to avoid reporting stopped again
1235         if (info.processRecord != null) {
1236             info.processRecord.setWasStoppedLogged(true);
1237         }
1238 
1239         if (DEBUG_METRICS) {
1240             Slog.i(TAG, String.format(
1241                     "APP_START_OCCURRED(%s, %s, %s, %s, %s, wasStopped=%b, firstLaunch=%b)",
1242                     info.applicationInfo.uid,
1243                     info.packageName,
1244                     getAppStartTransitionType(info.type, info.relaunched),
1245                     info.launchedActivityName,
1246                     info.launchedActivityLaunchedFromPackage,
1247                     stopped, firstLaunch));
1248         }
1249 
1250         logAppStartMemoryStateCapture(info);
1251     }
1252 
isIncrementalLoading(String packageName, int userId)1253     private boolean isIncrementalLoading(String packageName, int userId) {
1254         final IncrementalStatesInfo info = mSupervisor.mService.getPackageManagerInternalLocked()
1255                 .getIncrementalStatesInfo(packageName, 0 /* filterCallingUid */, userId);
1256         return info != null && info.isLoading();
1257     }
1258 
1259     @VisibleForTesting
logInTaskActivityStart(TransitionInfoSnapshot info, boolean isOpaque, int transitionDelayMs)1260     void logInTaskActivityStart(TransitionInfoSnapshot info, boolean isOpaque,
1261             int transitionDelayMs) {
1262         if (DEBUG_METRICS) {
1263             Slog.i(TAG, "IN_TASK_ACTIVITY_STARTED " + info.launchedActivityName
1264                     + " transitionDelayMs=" + transitionDelayMs + "ms");
1265         }
1266         FrameworkStatsLog.write(FrameworkStatsLog.IN_TASK_ACTIVITY_STARTED,
1267                 info.applicationInfo.uid,
1268                 getAppStartTransitionType(info.type, info.relaunched),
1269                 isOpaque,
1270                 transitionDelayMs,
1271                 info.windowsDrawnDelayMs,
1272                 TimeUnit.NANOSECONDS.toMillis(info.timestampNs));
1273     }
1274 
logAppDisplayed(TransitionInfoSnapshot info)1275     private void logAppDisplayed(TransitionInfoSnapshot info) {
1276         EventLog.writeEvent(WM_ACTIVITY_LAUNCH_TIME,
1277                 info.userId, info.activityRecordIdHashCode, info.launchedActivityShortComponentName,
1278                 info.windowsDrawnDelayMs);
1279 
1280         StringBuilder sb = mStringBuilder;
1281         sb.setLength(0);
1282         sb.append("Displayed ");
1283         sb.append(info.launchedActivityShortComponentName);
1284         sb.append(" for user ");
1285         sb.append(info.userId);
1286         sb.append(": ");
1287         TimeUtils.formatDuration(info.windowsDrawnDelayMs, sb);
1288         Log.i(TAG, sb.toString());
1289     }
1290 
logRecentsAnimationLatency(TransitionInfo info)1291     private void logRecentsAnimationLatency(TransitionInfo info) {
1292         final int duration = info.mSourceEventDelayMs + info.mWindowsDrawnDelayMs;
1293         final ActivityRecord r = info.mLastLaunchedActivity;
1294         final long lastTopLossTime = r.topResumedStateLossTime;
1295         final WindowManagerService wm = mSupervisor.mService.mWindowManager;
1296         mLoggerHandler.postDelayed(() -> {
1297             if (lastTopLossTime != r.topResumedStateLossTime) {
1298                 // Skip if the animation was finished in a short time.
1299                 return;
1300             }
1301             wm.mLatencyTracker.logAction(LatencyTracker.ACTION_START_RECENTS_ANIMATION, duration);
1302         }, LATENCY_TRACKER_RECENTS_DELAY_MS);
1303     }
1304 
getAppStartTransitionType(int tronType, boolean relaunched)1305     private static int getAppStartTransitionType(int tronType, boolean relaunched) {
1306         if (tronType == TYPE_TRANSITION_COLD_LAUNCH) {
1307             return FrameworkStatsLog.APP_START_OCCURRED__TYPE__COLD;
1308         }
1309         if (tronType == TYPE_TRANSITION_WARM_LAUNCH) {
1310             return FrameworkStatsLog.APP_START_OCCURRED__TYPE__WARM;
1311         }
1312         if (tronType == TYPE_TRANSITION_HOT_LAUNCH) {
1313             return relaunched
1314                     ? FrameworkStatsLog.APP_START_OCCURRED__TYPE__RELAUNCH
1315                     : FrameworkStatsLog.APP_START_OCCURRED__TYPE__HOT;
1316         }
1317         return FrameworkStatsLog.APP_START_OCCURRED__TYPE__UNKNOWN;
1318     }
1319 
1320     /** @see android.app.Activity#reportFullyDrawn */
notifyFullyDrawn(ActivityRecord r, boolean restoredFromBundle)1321     TransitionInfoSnapshot notifyFullyDrawn(ActivityRecord r, boolean restoredFromBundle) {
1322         final TransitionInfo info = mLastTransitionInfo.get(r);
1323         if (info == null) {
1324             return null;
1325         }
1326         if (!info.mIsDrawn && info.mPendingFullyDrawn == null) {
1327             // There are still undrawn activities, postpone reporting fully drawn until all of its
1328             // windows are drawn. So that is closer to an usable state.
1329             info.mPendingFullyDrawn = () -> {
1330                 notifyFullyDrawn(r, restoredFromBundle);
1331                 info.mPendingFullyDrawn = null;
1332             };
1333             return null;
1334         }
1335 
1336         final long currentTimestampNs = SystemClock.uptimeNanos();
1337         final long startupTimeMs = info.mPendingFullyDrawn != null
1338                 ? info.mWindowsDrawnDelayMs
1339                 : TimeUnit.NANOSECONDS.toMillis(
1340                         currentTimestampNs - info.mLaunchingState.mStartUptimeNs);
1341         final TransitionInfoSnapshot infoSnapshot =
1342                 new TransitionInfoSnapshot(info, r, (int) startupTimeMs);
1343         if (infoSnapshot.isInterestedToEventLog()) {
1344             mLoggerHandler.post(() -> logAppFullyDrawn(infoSnapshot));
1345         }
1346         mLastTransitionInfo.remove(r);
1347 
1348         if (!info.isInterestingToLoggerAndObserver()) {
1349             return infoSnapshot;
1350         }
1351 
1352         // Record the handling of the reportFullyDrawn callback in the trace system. This is not
1353         // actually used to trace this function, but instead the logical task that this function
1354         // fullfils (handling reportFullyDrawn() callbacks).
1355         Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
1356                 "ActivityManager:ReportingFullyDrawn " + info.mLastLaunchedActivity.packageName);
1357         mLoggerHandler.post(() -> logAppFullyDrawnMetrics(infoSnapshot, restoredFromBundle,
1358                 info.mProcessRunning));
1359         // Ends the trace started at the beginning of this function. This is located here to allow
1360         // the trace slice to have a noticable duration.
1361         Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
1362 
1363         // Notify reportFullyDrawn event.
1364         launchObserverNotifyReportFullyDrawn(info, currentTimestampNs);
1365 
1366         return infoSnapshot;
1367     }
1368 
logAppFullyDrawnMetrics(TransitionInfoSnapshot info, boolean restoredFromBundle, boolean processRunning)1369     private void logAppFullyDrawnMetrics(TransitionInfoSnapshot info, boolean restoredFromBundle,
1370             boolean processRunning) {
1371         final LogMaker builder = new LogMaker(APP_TRANSITION_REPORTED_DRAWN);
1372         builder.setPackageName(info.packageName);
1373         builder.addTaggedData(FIELD_CLASS_NAME, info.launchedActivityName);
1374         builder.addTaggedData(APP_TRANSITION_REPORTED_DRAWN_MS,
1375                 (long) info.windowsFullyDrawnDelayMs);
1376         builder.setType(restoredFromBundle
1377                 ? TYPE_TRANSITION_REPORTED_DRAWN_WITH_BUNDLE
1378                 : TYPE_TRANSITION_REPORTED_DRAWN_NO_BUNDLE);
1379         builder.addTaggedData(APP_TRANSITION_PROCESS_RUNNING, processRunning ? 1 : 0);
1380         mMetricsLogger.write(builder);
1381         final PackageOptimizationInfo packageOptimizationInfo =
1382                 info.getPackageOptimizationInfo(getArtManagerInternal());
1383         // Incremental info
1384         boolean isIncremental = false, isLoading = false;
1385         final String codePath = info.applicationInfo.getCodePath();
1386         if (codePath != null && IncrementalManager.isIncrementalPath(codePath)) {
1387             isIncremental = true;
1388             isLoading = isIncrementalLoading(info.packageName, info.userId);
1389         }
1390         FrameworkStatsLog.write(
1391                 FrameworkStatsLog.APP_START_FULLY_DRAWN,
1392                 info.applicationInfo.uid,
1393                 info.packageName,
1394                 restoredFromBundle
1395                         ? FrameworkStatsLog.APP_START_FULLY_DRAWN__TYPE__WITH_BUNDLE
1396                         : FrameworkStatsLog.APP_START_FULLY_DRAWN__TYPE__WITHOUT_BUNDLE,
1397                 info.launchedActivityName,
1398                 processRunning,
1399                 info.windowsFullyDrawnDelayMs,
1400                 packageOptimizationInfo.getCompilationReason(),
1401                 packageOptimizationInfo.getCompilationFilter(),
1402                 info.sourceType,
1403                 info.sourceEventDelayMs,
1404                 isIncremental,
1405                 isLoading,
1406                 info.launchedActivityName.hashCode(),
1407                 TimeUnit.NANOSECONDS.toMillis(info.timestampNs));
1408     }
1409 
logAppFullyDrawn(TransitionInfoSnapshot info)1410     private void logAppFullyDrawn(TransitionInfoSnapshot info) {
1411         StringBuilder sb = mStringBuilder;
1412         sb.setLength(0);
1413         sb.append("Fully drawn ");
1414         sb.append(info.launchedActivityShortComponentName);
1415         sb.append(": ");
1416         TimeUtils.formatDuration(info.windowsFullyDrawnDelayMs, sb);
1417         Log.i(TAG, sb.toString());
1418     }
1419 
logAbortedBgActivityStart(Intent intent, WindowProcessController callerApp, int callingUid, String callingPackage, int callingUidProcState, boolean callingUidHasAnyVisibleWindow, int realCallingUid, int realCallingUidProcState, boolean realCallingUidHasAnyVisibleWindow, boolean comingFromPendingIntent)1420     void logAbortedBgActivityStart(Intent intent, WindowProcessController callerApp,
1421             int callingUid, String callingPackage, int callingUidProcState,
1422             boolean callingUidHasAnyVisibleWindow,
1423             int realCallingUid, int realCallingUidProcState,
1424             boolean realCallingUidHasAnyVisibleWindow,
1425             boolean comingFromPendingIntent) {
1426 
1427         final long nowElapsed = SystemClock.elapsedRealtime();
1428         final long nowUptime = SystemClock.uptimeMillis();
1429         final LogMaker builder = new LogMaker(ACTION_ACTIVITY_START);
1430         builder.setTimestamp(System.currentTimeMillis());
1431         builder.addTaggedData(FIELD_CALLING_UID, callingUid);
1432         builder.addTaggedData(FIELD_CALLING_PACKAGE_NAME, callingPackage);
1433         builder.addTaggedData(FIELD_CALLING_UID_PROC_STATE,
1434                 processStateAmToProto(callingUidProcState));
1435         builder.addTaggedData(FIELD_CALLING_UID_HAS_ANY_VISIBLE_WINDOW,
1436                 callingUidHasAnyVisibleWindow ? 1 : 0);
1437         builder.addTaggedData(FIELD_REAL_CALLING_UID, realCallingUid);
1438         builder.addTaggedData(FIELD_REAL_CALLING_UID_PROC_STATE,
1439                 processStateAmToProto(realCallingUidProcState));
1440         builder.addTaggedData(FIELD_REAL_CALLING_UID_HAS_ANY_VISIBLE_WINDOW,
1441                 realCallingUidHasAnyVisibleWindow ? 1 : 0);
1442         builder.addTaggedData(FIELD_COMING_FROM_PENDING_INTENT, comingFromPendingIntent ? 1 : 0);
1443         if (intent != null) {
1444             builder.addTaggedData(FIELD_INTENT_ACTION, intent.getAction());
1445             ComponentName component = intent.getComponent();
1446             if (component != null) {
1447                 builder.addTaggedData(FIELD_TARGET_SHORT_COMPONENT_NAME,
1448                         component.flattenToShortString());
1449             }
1450         }
1451         if (callerApp != null) {
1452             builder.addTaggedData(FIELD_PROCESS_RECORD_PROCESS_NAME, callerApp.mName);
1453             builder.addTaggedData(FIELD_PROCESS_RECORD_CUR_PROC_STATE,
1454                     processStateAmToProto(callerApp.getCurrentProcState()));
1455             builder.addTaggedData(FIELD_PROCESS_RECORD_HAS_CLIENT_ACTIVITIES,
1456                     callerApp.hasClientActivities() ? 1 : 0);
1457             builder.addTaggedData(FIELD_PROCESS_RECORD_HAS_FOREGROUND_SERVICES,
1458                     callerApp.hasForegroundServices() ? 1 : 0);
1459             builder.addTaggedData(FIELD_PROCESS_RECORD_HAS_FOREGROUND_ACTIVITIES,
1460                     callerApp.hasForegroundActivities() ? 1 : 0);
1461             builder.addTaggedData(FIELD_PROCESS_RECORD_HAS_TOP_UI, callerApp.hasTopUi() ? 1 : 0);
1462             builder.addTaggedData(FIELD_PROCESS_RECORD_HAS_OVERLAY_UI,
1463                     callerApp.hasOverlayUi() ? 1 : 0);
1464             builder.addTaggedData(FIELD_PROCESS_RECORD_PENDING_UI_CLEAN,
1465                     callerApp.hasPendingUiClean() ? 1 : 0);
1466             if (callerApp.getInteractionEventTime() != 0) {
1467                 builder.addTaggedData(FIELD_PROCESS_RECORD_MILLIS_SINCE_LAST_INTERACTION_EVENT,
1468                         (nowElapsed - callerApp.getInteractionEventTime()));
1469             }
1470             if (callerApp.getFgInteractionTime() != 0) {
1471                 builder.addTaggedData(FIELD_PROCESS_RECORD_MILLIS_SINCE_FG_INTERACTION,
1472                         (nowElapsed - callerApp.getFgInteractionTime()));
1473             }
1474             if (callerApp.getWhenUnimportant() != 0) {
1475                 builder.addTaggedData(FIELD_PROCESS_RECORD_MILLIS_SINCE_UNIMPORTANT,
1476                         (nowUptime - callerApp.getWhenUnimportant()));
1477             }
1478         }
1479         mMetricsLogger.write(builder);
1480     }
1481 
logAppStartMemoryStateCapture(TransitionInfoSnapshot info)1482     private void logAppStartMemoryStateCapture(TransitionInfoSnapshot info) {
1483         if (info.processRecord == null) {
1484             if (DEBUG_METRICS) Slog.i(TAG, "logAppStartMemoryStateCapture processRecord null");
1485             return;
1486         }
1487 
1488         final int pid = info.processRecord.getPid();
1489         final int uid = info.applicationInfo.uid;
1490         final MemoryStat memoryStat = readMemoryStatFromFilesystem(uid, pid);
1491         if (memoryStat == null) {
1492             if (DEBUG_METRICS) Slog.i(TAG, "logAppStartMemoryStateCapture memoryStat null");
1493             return;
1494         }
1495 
1496         FrameworkStatsLog.write(
1497                 FrameworkStatsLog.APP_START_MEMORY_STATE_CAPTURED,
1498                 uid,
1499                 info.processName,
1500                 info.launchedActivityName,
1501                 memoryStat.pgfault,
1502                 memoryStat.pgmajfault,
1503                 memoryStat.rssInBytes,
1504                 memoryStat.cacheInBytes,
1505                 memoryStat.swapInBytes);
1506     }
1507 
1508     /**
1509      * Logs the current App Compat state of the given {@link ActivityRecord} with its package
1510      * UID, if all of the following hold:
1511      * <ul>
1512      *   <li>The current state is different than the last logged state for the package UID of the
1513      *   activity.
1514      *   <li>If the current state is NOT_VISIBLE, there is a previously logged state for the
1515      *   package UID and there are no other visible activities with the same package UID.
1516      *   <li>The last logged activity with the same package UID is either {@code activity} (or an
1517      *   activity that has been removed) or the last logged state is NOT_VISIBLE or NOT_LETTERBOXED.
1518      * </ul>
1519      *
1520      * <p>If the current state is NOT_VISIBLE and the previous state which was logged by {@code
1521      * activity} (or an activity that has been removed) wasn't, looks for the first visible activity
1522      * with the same package UID that has a letterboxed state, or a non-letterboxed state if
1523      * there isn't one, and logs that state.
1524      *
1525      * <p>This method assumes that the caller is wrapping the call with a synchronized block so
1526      * that there won't be a race condition between two activities with the same package.
1527      */
logAppCompatState(@onNull ActivityRecord activity)1528     void logAppCompatState(@NonNull ActivityRecord activity) {
1529         final int packageUid = activity.info.applicationInfo.uid;
1530         final int state = activity.getAppCompatState();
1531 
1532         if (!mPackageUidToCompatStateInfo.contains(packageUid)) {
1533             mPackageUidToCompatStateInfo.put(packageUid, new PackageCompatStateInfo());
1534         }
1535         final PackageCompatStateInfo compatStateInfo = mPackageUidToCompatStateInfo.get(packageUid);
1536         final int lastLoggedState = compatStateInfo.mLastLoggedState;
1537         final ActivityRecord lastLoggedActivity = compatStateInfo.mLastLoggedActivity;
1538 
1539         final boolean isVisible = state != APP_COMPAT_STATE_CHANGED__STATE__NOT_VISIBLE;
1540         final ArrayList<ActivityRecord> visibleActivities = compatStateInfo.mVisibleActivities;
1541         if (isVisible && !visibleActivities.contains(activity)) {
1542             visibleActivities.add(activity);
1543         } else if (!isVisible) {
1544             visibleActivities.remove(activity);
1545             if (visibleActivities.isEmpty()) {
1546                 // No need to keep the entry if there are no visible activities.
1547                 mPackageUidToCompatStateInfo.remove(packageUid);
1548             }
1549         }
1550 
1551         if (state == lastLoggedState) {
1552             // We don’t want to log the same state twice or log DEFAULT_NOT_VISIBLE before any
1553             // visible state was logged.
1554             return;
1555         }
1556 
1557         if (!isVisible && !visibleActivities.isEmpty()) {
1558             // There is another visible activity for this package UID.
1559             if (lastLoggedActivity == null || activity == lastLoggedActivity) {
1560                 // Make sure a new visible state is logged if needed.
1561                 findAppCompatStateToLog(compatStateInfo, packageUid);
1562             }
1563             return;
1564         }
1565 
1566         if (lastLoggedActivity != null && activity != lastLoggedActivity
1567                 && lastLoggedState != APP_COMPAT_STATE_CHANGED__STATE__NOT_VISIBLE
1568                 && lastLoggedState != APP_COMPAT_STATE_CHANGED__STATE__NOT_LETTERBOXED) {
1569             // Another visible activity for this package UID has logged a letterboxed state.
1570             return;
1571         }
1572 
1573         logAppCompatStateInternal(activity, state, compatStateInfo);
1574     }
1575 
1576     /**
1577      * Looks for the first visible activity in {@code compatStateInfo} that has a letterboxed
1578      * state, or a non-letterboxed state if there isn't one, and logs that state for the given
1579      * {@code packageUid}.
1580      *
1581      * <p>If there is a visible activity in {@code compatStateInfo} with the same state as the
1582      * last logged state for the given {@code packageUid}, changes the last logged activity to
1583      * reference the first such activity without actually logging the same state twice.
1584      */
findAppCompatStateToLog(PackageCompatStateInfo compatStateInfo, int packageUid)1585     private void findAppCompatStateToLog(PackageCompatStateInfo compatStateInfo, int packageUid) {
1586         final ArrayList<ActivityRecord> visibleActivities = compatStateInfo.mVisibleActivities;
1587         final int lastLoggedState = compatStateInfo.mLastLoggedState;
1588 
1589         ActivityRecord activityToLog = null;
1590         int stateToLog = APP_COMPAT_STATE_CHANGED__STATE__NOT_VISIBLE;
1591         for (int i = 0; i < visibleActivities.size(); i++) {
1592             ActivityRecord activity = visibleActivities.get(i);
1593             int state = activity.getAppCompatState();
1594             if (state == lastLoggedState) {
1595                 // Change last logged activity without logging the same state twice.
1596                 compatStateInfo.mLastLoggedActivity = activity;
1597                 return;
1598             }
1599             if (state == APP_COMPAT_STATE_CHANGED__STATE__NOT_VISIBLE) {
1600                 // This shouldn't happen.
1601                 Slog.w(TAG, "Visible activity with NOT_VISIBLE App Compat state for package UID: "
1602                         + packageUid);
1603                 continue;
1604             }
1605             if (stateToLog == APP_COMPAT_STATE_CHANGED__STATE__NOT_VISIBLE || (
1606                     stateToLog == APP_COMPAT_STATE_CHANGED__STATE__NOT_LETTERBOXED
1607                             && state != APP_COMPAT_STATE_CHANGED__STATE__NOT_LETTERBOXED)) {
1608                 activityToLog = activity;
1609                 stateToLog = state;
1610             }
1611         }
1612         if (activityToLog != null && stateToLog != APP_COMPAT_STATE_CHANGED__STATE__NOT_VISIBLE) {
1613             logAppCompatStateInternal(activityToLog, stateToLog, compatStateInfo);
1614         }
1615     }
1616 
isAppCompateStateChangedToLetterboxed(int state)1617     private static boolean isAppCompateStateChangedToLetterboxed(int state) {
1618         return state == APP_COMPAT_STATE_CHANGED__STATE__LETTERBOXED_FOR_ASPECT_RATIO
1619                 || state == APP_COMPAT_STATE_CHANGED__STATE__LETTERBOXED_FOR_FIXED_ORIENTATION
1620                 || state == APP_COMPAT_STATE_CHANGED__STATE__LETTERBOXED_FOR_SIZE_COMPAT_MODE;
1621     }
1622 
logAppCompatStateInternal(@onNull ActivityRecord activity, int state, PackageCompatStateInfo compatStateInfo)1623     private void logAppCompatStateInternal(@NonNull ActivityRecord activity, int state,
1624              PackageCompatStateInfo compatStateInfo) {
1625         compatStateInfo.mLastLoggedState = state;
1626         compatStateInfo.mLastLoggedActivity = activity;
1627         int packageUid = activity.info.applicationInfo.uid;
1628 
1629         int positionToLog = APP_COMPAT_STATE_CHANGED__LETTERBOX_POSITION__NOT_LETTERBOXED_POSITION;
1630         if (isAppCompateStateChangedToLetterboxed(state)) {
1631             positionToLog = activity.mAppCompatController.getReachabilityOverrides()
1632                     .getLetterboxPositionForLogging();
1633         }
1634         FrameworkStatsLog.write(FrameworkStatsLog.APP_COMPAT_STATE_CHANGED,
1635                 packageUid, state, positionToLog);
1636 
1637         if (DEBUG_METRICS) {
1638             Slog.i(TAG, String.format("APP_COMPAT_STATE_CHANGED(%s, %s, %s)",
1639                     packageUid, state, positionToLog));
1640         }
1641     }
1642 
1643     /**
1644      * Logs the changing of the letterbox position along with its package UID
1645      */
logLetterboxPositionChange(@onNull ActivityRecord activity, int position)1646     void logLetterboxPositionChange(@NonNull ActivityRecord activity, int position) {
1647         int packageUid = activity.info.applicationInfo.uid;
1648         FrameworkStatsLog.write(FrameworkStatsLog.LETTERBOX_POSITION_CHANGED, packageUid, position);
1649 
1650         if (!mPackageUidToCompatStateInfo.contains(packageUid)) {
1651             // There is no last logged activity for this packageUid so we should not log the
1652             // position change as we can only log the position change for the current activity
1653             return;
1654         }
1655         final PackageCompatStateInfo compatStateInfo = mPackageUidToCompatStateInfo.get(packageUid);
1656         final ActivityRecord lastLoggedActivity = compatStateInfo.mLastLoggedActivity;
1657         if (activity != lastLoggedActivity) {
1658             // Only log the position change for the current activity to be consistent with
1659             // findAppCompatStateToLog and ensure that metrics for the state changes are computed
1660             // correctly
1661             return;
1662         }
1663         int state = activity.getAppCompatState();
1664         logAppCompatStateInternal(activity, state, compatStateInfo);
1665 
1666         if (DEBUG_METRICS) {
1667             Slog.i(TAG, String.format("LETTERBOX_POSITION_CHANGED(%s, %s)",
1668                     packageUid, position));
1669         }
1670     }
1671 
getArtManagerInternal()1672     private ArtManagerInternal getArtManagerInternal() {
1673         if (mArtManagerInternal == null) {
1674             // Note that this may be null.
1675             // ArtManagerInternal is registered during PackageManagerService
1676             // initialization which happens after ActivityManagerService.
1677             mArtManagerInternal = LocalServices.getService(ArtManagerInternal.class);
1678         }
1679         return mArtManagerInternal;
1680     }
1681 
1682     /** Starts trace for an activity is actually launching. */
startLaunchTrace(@onNull TransitionInfo info)1683     private void startLaunchTrace(@NonNull TransitionInfo info) {
1684         if (DEBUG_METRICS) Slog.i(TAG, "startLaunchTrace " + info);
1685         if (info.mLaunchingState.mTraceName == null) {
1686             return;
1687         }
1688         info.mLaunchTraceName = "launching: " + info.mLastLaunchedActivity.packageName;
1689         Trace.asyncTraceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, info.mLaunchTraceName,
1690                 (int) info.mLaunchingState.mStartRealtimeNs /* cookie */);
1691     }
1692 
1693     /** Stops trace for the launch is completed or cancelled. */
stopLaunchTrace(@onNull TransitionInfo info)1694     private void stopLaunchTrace(@NonNull TransitionInfo info) {
1695         if (DEBUG_METRICS) Slog.i(TAG, "stopLaunchTrace " + info);
1696         if (info.mLaunchTraceName == null) {
1697             return;
1698         }
1699         Trace.asyncTraceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER, info.mLaunchTraceName,
1700                 (int) info.mLaunchingState.mStartRealtimeNs /* cookie */);
1701         info.mLaunchTraceName = null;
1702     }
1703 
getLaunchObserverRegistry()1704     public ActivityMetricsLaunchObserverRegistry getLaunchObserverRegistry() {
1705         return mLaunchObserver;
1706     }
1707 
1708     /** Notify the {@link ActivityMetricsLaunchObserver} that a new launch sequence has begun. */
launchObserverNotifyIntentStarted(Intent intent, long timestampNs)1709     private void launchObserverNotifyIntentStarted(Intent intent, long timestampNs) {
1710         Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
1711                 "MetricsLogger:launchObserverNotifyIntentStarted");
1712 
1713         // Beginning a launch is timing sensitive and so should be observed as soon as possible.
1714         mLaunchObserver.onIntentStarted(intent, timestampNs);
1715 
1716         Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
1717     }
1718 
1719     /**
1720      * Notify the {@link ActivityMetricsLaunchObserver} that the previous launch sequence has
1721      * aborted due to intent failure (e.g. intent resolve failed or security error, etc) or
1722      * intent being delivered to the top running activity.
1723      */
launchObserverNotifyIntentFailed(long id)1724     private void launchObserverNotifyIntentFailed(long id) {
1725        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
1726                 "MetricsLogger:launchObserverNotifyIntentFailed");
1727 
1728         mLaunchObserver.onIntentFailed(id);
1729 
1730         Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
1731     }
1732 
1733     /**
1734      * Notify the {@link ActivityMetricsLaunchObserver} that the current launch sequence's activity
1735      * has started.
1736      */
launchObserverNotifyActivityLaunched(TransitionInfo info)1737     private void launchObserverNotifyActivityLaunched(TransitionInfo info) {
1738         Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
1739                 "MetricsLogger:launchObserverNotifyActivityLaunched");
1740 
1741         @ActivityMetricsLaunchObserver.Temperature int temperature =
1742                 convertTransitionTypeToLaunchObserverTemperature(info.mTransitionType);
1743 
1744         // Beginning a launch is timing sensitive and so should be observed as soon as possible.
1745         mLaunchObserver.onActivityLaunched(info.mLaunchingState.mStartUptimeNs,
1746                 info.mLastLaunchedActivity.mActivityComponent, temperature,
1747                 info.mLastLaunchedActivity.mUserId);
1748 
1749         Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
1750     }
1751 
1752     /**
1753      * Notifies the {@link ActivityMetricsLaunchObserver} the reportFullDrawn event.
1754      */
launchObserverNotifyReportFullyDrawn(TransitionInfo info, long timestampNs)1755     private void launchObserverNotifyReportFullyDrawn(TransitionInfo info, long timestampNs) {
1756         Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
1757             "MetricsLogger:launchObserverNotifyReportFullyDrawn");
1758         mLaunchObserver.onReportFullyDrawn(info.mLaunchingState.mStartUptimeNs, timestampNs);
1759         Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
1760     }
1761 
1762     /**
1763      * Notify the {@link ActivityMetricsLaunchObserver} that the current launch sequence is
1764      * cancelled.
1765      */
launchObserverNotifyActivityLaunchCancelled(TransitionInfo info)1766     private void launchObserverNotifyActivityLaunchCancelled(TransitionInfo info) {
1767         Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
1768                 "MetricsLogger:launchObserverNotifyActivityLaunchCancelled");
1769 
1770         mLaunchObserver.onActivityLaunchCancelled(info.mLaunchingState.mStartUptimeNs);
1771 
1772         Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
1773     }
1774 
1775     /**
1776      * Notify the {@link ActivityMetricsLaunchObserver} that the current launch sequence's activity
1777      * has fully finished (successfully).
1778      */
launchObserverNotifyActivityLaunchFinished(TransitionInfo info, long timestampNs)1779     private void launchObserverNotifyActivityLaunchFinished(TransitionInfo info, long timestampNs) {
1780         Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
1781                 "MetricsLogger:launchObserverNotifyActivityLaunchFinished");
1782 
1783         mLaunchObserver.onActivityLaunchFinished(info.mLaunchingState.mStartUptimeNs,
1784                 info.mLastLaunchedActivity.mActivityComponent, timestampNs,
1785                 info.mLastLaunchedActivity.launchMode);
1786 
1787         Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
1788     }
1789 
1790     private static @ActivityMetricsLaunchObserver.Temperature int
convertTransitionTypeToLaunchObserverTemperature(int transitionType)1791             convertTransitionTypeToLaunchObserverTemperature(int transitionType) {
1792         switch (transitionType) {
1793             case TYPE_TRANSITION_WARM_LAUNCH:
1794                 return ActivityMetricsLaunchObserver.TEMPERATURE_WARM;
1795             case TYPE_TRANSITION_HOT_LAUNCH:
1796                 return ActivityMetricsLaunchObserver.TEMPERATURE_HOT;
1797             case TYPE_TRANSITION_COLD_LAUNCH:
1798                 return ActivityMetricsLaunchObserver.TEMPERATURE_COLD;
1799             default:
1800                 return -1;
1801         }
1802     }
1803 
wasStoppedNeedsLogging(TransitionInfoSnapshot info)1804     private boolean wasStoppedNeedsLogging(TransitionInfoSnapshot info) {
1805         if (info.processRecord != null) {
1806             return (info.processRecord.wasForceStopped()
1807                         || info.processRecord.wasFirstLaunch())
1808                     && !info.processRecord.getWasStoppedLogged();
1809         } else {
1810             return (info.applicationInfo.flags & ApplicationInfo.FLAG_STOPPED) != 0;
1811         }
1812     }
1813 
wasFirstLaunch(TransitionInfoSnapshot info)1814     private boolean wasFirstLaunch(TransitionInfoSnapshot info) {
1815         if (info.processRecord != null) {
1816             return info.processRecord.wasFirstLaunch()
1817                     && !info.processRecord.getWasStoppedLogged();
1818         }
1819         try {
1820             return !mSupervisor.mService.getPackageManagerInternalLocked()
1821                     .wasPackageEverLaunched(info.packageName, info.userId);
1822         } catch (Exception e) {
1823             // Couldn't find the state record, so must be a newly installed app
1824             return true;
1825         }
1826     }
1827 }
1828