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