1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.wm;
18 
19 import android.annotation.IntDef;
20 import android.annotation.NonNull;
21 import android.content.ComponentName;
22 import android.content.Intent;
23 
24 import java.lang.annotation.Retention;
25 import java.lang.annotation.RetentionPolicy;
26 
27 /**
28  * Observe activity launch sequences.
29  *
30  * Multiple calls to the callback methods can occur without first terminating the current launch
31  * sequence because activity can be launched concurrently. So the implementation should associate
32  * the corresponding event according to the timestamp from {@link #onIntentStarted} which is also
33  * used as the identifier to indicate which launch sequence it belongs to.
34  *
35  * When a new launch sequence is made, that sequence is in the {@code INTENT_STARTED} state which
36  * is communicated by the {@link #onIntentStarted} callback. This is a transient state.
37  *
38  * The intent can fail to launch the activity, in which case the sequence's state transitions to
39  * {@code INTENT_FAILED} via {@link #onIntentFailed}. This is a terminal state.
40  *
41  * If an activity is successfully started, the launch sequence's state will transition into
42  * {@code STARTED} via {@link #onActivityLaunched}. This is a transient state.
43  *
44  * It must then transition to either {@code CANCELLED} with {@link #onActivityLaunchCancelled},
45  * which is a terminal state or into {@code FINISHED} with {@link #onActivityLaunchFinished}.
46  *
47  * The {@code FINISHED} with {@link #onActivityLaunchFinished} then may transition to
48  * {@code FULLY_DRAWN} with {@link #onReportFullyDrawn}, which is a terminal state.
49  * Note this transition may not happen if the reportFullyDrawn event is not receivied,
50  * in which case {@code FINISHED} is terminal.
51  *
52  * Note that the {@code ComponentName} provided as a parameter to some state transitions isn't
53  * necessarily the same within a single launch sequence: it is only the top-most activity at the
54  * time (if any). Trampoline activities coalesce several activity starts into a single launch
55  * sequence.
56  *
57  * Upon reaching a terminal state, it is considered that there are no active launch sequences
58  * until a subsequent transition into {@code INTENT_STARTED} initiates a new launch sequence.
59  *
60  * <pre>
61  *        ┌⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯┐     ┌⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯┐     ┌--------------------------┐
62  *    ╴╴▶  INTENT_STARTED    ──▶      ACTIVITY_LAUNCHED        ──▶   ACTIVITY_LAUNCH_FINISHED
63  *        └⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯┘     └⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯┘     └--------------------------┘
64  *          :                      :                                 :
65  *          :                      :                                 :
66  *          ▼                      ▼                                 ▼
67  *        ╔════════════════╗     ╔═══════════════════════════╗     ╔═══════════════════════════╗
68  *        ║ INTENT_FAILED  ║     ║ ACTIVITY_LAUNCH_CANCELLED ║     ║    REPORT_FULLY_DRAWN     ║
69  *        ╚════════════════╝     ╚═══════════════════════════╝     ╚═══════════════════════════╝
70  * </pre>
71  */
72 public class ActivityMetricsLaunchObserver {
73     /**
74      * The 'temperature' at which a launch sequence had started.
75      *
76      * The lower the temperature the more work has to be done during start-up.
77      * A 'cold' temperature means that a new process has been started and likely
78      * nothing is cached.
79      *
80      * A hot temperature means the existing activity is brought to the foreground.
81      * It may need to regenerate some objects as a result of {@code onTrimMemory}.
82      *
83      * A warm temperature is in the middle; an existing process is used, but the activity
84      * has to be created from scratch with {@code #onCreate}.
85      *
86      * @see https://developer.android.com/topic/performance/vitals/launch-time
87      */
88     @Retention(RetentionPolicy.SOURCE)
89     @IntDef({
90             TEMPERATURE_COLD,
91             TEMPERATURE_WARM,
92             TEMPERATURE_HOT
93     })
94     @interface Temperature {}
95 
96     /** Cold launch sequence: a new process has started. */
97     public static final int TEMPERATURE_COLD = 1;
98     /** Warm launch sequence: process reused, but activity has to be created. */
99     public static final int TEMPERATURE_WARM = 2;
100     /** Hot launch sequence: process reused, activity brought-to-top. */
101     public static final int TEMPERATURE_HOT = 3;
102 
103     /**
104      * Notifies the observer that a new launch sequence has begun as a result of a new intent.
105      *
106      * Once a launch sequence begins, the resolved activity will either subsequently start with
107      * {@link #onActivityLaunched} or abort early (for example due to a resolution error or due to
108      * a security error) with {@link #onIntentFailed}.
109      *
110      * @param timestampNanos The timestamp when receiving the intent. It is also use as an
111      *                       identifier for other callback methods to known which launch sequence
112      *                       it is associated with.
113      */
onIntentStarted(@onNull Intent intent, long timestampNanos)114     public void onIntentStarted(@NonNull Intent intent, long timestampNanos) {
115     }
116 
117     /**
118      * Notifies the observer that the current launch sequence has failed to launch an activity.
119      *
120      * This function call terminates the current launch sequence.
121      *
122      * Examples of this happening:
123      *  - Failure to resolve to an activity
124      *  - Calling package did not have the security permissions to call the requested activity
125      *  - Resolved activity was already running and only needed to be brought to the top
126      */
onIntentFailed(long id)127     public void onIntentFailed(long id) {
128     }
129 
130     /**
131      * Notifies the observer that the current launch sequence had begun starting an activity.
132      *
133      * This is an intermediate state: once an activity begins starting, the entire launch sequence
134      * will later terminate by either finishing or cancelling.
135      *
136      * The initial activity is the first activity to be started as part of a launch sequence:
137      * it is represented by {@param activity} However, it isn't
138      * necessarily the activity which will be considered as displayed when the activity
139      * finishes launching (e.g. {@code activity} in {@link #onActivityLaunchFinished}).
140      *
141      * @param id The timestamp as an identifier from {@link #onIntentStarted}. It may be a new id
142      *           if the launching activity is started from an existing launch sequence (trampoline)
143      *           but cannot coalesce to the existing one, e.g. to a different display.
144      * @param name The launching activity name.
145      * @param temperature The temperature at which a launch sequence had started.
146      * @param userId The id of the user the activity is being launched for.
147      */
onActivityLaunched(long id, ComponentName name, @Temperature int temperature, int userId)148     public void onActivityLaunched(long id, ComponentName name, @Temperature int temperature,
149             int userId) {
150     }
151 
152     /**
153      * Notifies the observer that the current launch sequence has been aborted.
154      *
155      * This function call terminates the current launch sequence.
156      *
157      * This can happen for many reasons, for example the user switches away to another app
158      * prior to the launch sequence completing, or the application being killed.
159      *
160      * @param id The timestamp as an identifier from {@link #onIntentStarted}.
161      *
162      * @apiNote The aborting activity isn't necessarily the same as the starting activity;
163      *          in the case of a trampoline, multiple activities could've been started
164      *          and only the latest activity is reported here.
165      */
onActivityLaunchCancelled(long id)166     public void onActivityLaunchCancelled(long id) {
167     }
168 
169     /**
170      * Notifies the observer that the current launch sequence has been successfully finished.
171      *
172      * This function call terminates the current launch sequence.
173      *
174      * A launch sequence is considered to be successfully finished when a frame is fully
175      * drawn for the first time: the top-most activity at the time is what's reported here.
176      *
177      * @param id The timestamp as an identifier from {@link #onIntentStarted}.
178      * @param name The name of drawn activity. It can be different from {@link #onActivityLaunched}
179      *             if the transition contains multiple launching activities (e.g. trampoline).
180      * @param timestampNanos the timestamp of ActivityLaunchFinished event in nanoseconds.
181      *        To compute the TotalTime duration, deduct the timestamp {@link #onIntentStarted}
182      *        from {@code timestampNanos}.
183      * @param launchMode The activity launch mode.
184      *
185      * @apiNote The finishing activity isn't necessarily the same as the starting activity;
186      *          in the case of a trampoline, multiple activities could've been started
187      *          and only the latest activity that was top-most during first-frame drawn
188      *          is reported here.
189      */
onActivityLaunchFinished(long id, ComponentName name, long timestampNanos, int launchMode)190     public void onActivityLaunchFinished(long id, ComponentName name, long timestampNanos,
191             int launchMode) {
192     }
193 
194     /**
195      * Notifies the observer that the application self-reported itself as being fully drawn.
196      *
197      * @param id The timestamp as an identifier from {@link #onIntentStarted}.
198      * @param timestampNanos the timestamp of ReportFullyDrawn event in nanoseconds.
199      *        To compute the duration, deduct the deduct the timestamp {@link #onIntentStarted}
200      *        from {@code timestampNanos}.
201      *
202      * @apiNote The behavior of ReportFullyDrawn mostly depends on the app.
203      *          It is used as an accurate estimate of meanfully app startup time.
204      *          This event may be missing for many apps.
205      */
onReportFullyDrawn(long id, long timestampNanos)206     public void onReportFullyDrawn(long id, long timestampNanos) {
207     }
208 
209 }
210