ActivityMetricsLogger.java revision f970410afef518003c84eef022194848b2a4f606
1package com.android.server.am; 2 3import static android.app.ActivityManager.StackId.DOCKED_STACK_ID; 4import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID; 5import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID; 6import static android.app.ActivityManager.StackId.HOME_STACK_ID; 7import static android.app.ActivityManager.StackId.PINNED_STACK_ID; 8import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; 9import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME; 10import static com.android.server.am.ActivityStack.STACK_INVISIBLE; 11 12import android.annotation.Nullable; 13import android.app.ActivityManager.StackId; 14import android.content.Context; 15import android.os.SystemClock; 16import android.util.Slog; 17 18import com.android.internal.logging.MetricsLogger; 19import com.android.internal.logging.MetricsProto.MetricsEvent; 20 21/** 22 * Handles logging into Tron. 23 */ 24class ActivityMetricsLogger { 25 26 private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityMetricsLogger" : TAG_AM; 27 28 // Window modes we are interested in logging. If we ever introduce a new type, we need to add 29 // a value here and increase the {@link #TRON_WINDOW_STATE_VARZ_STRINGS} array. 30 private static final int WINDOW_STATE_STANDARD = 0; 31 private static final int WINDOW_STATE_SIDE_BY_SIDE = 1; 32 private static final int WINDOW_STATE_FREEFORM = 2; 33 private static final int WINDOW_STATE_INVALID = -1; 34 35 private static final long INVALID_START_TIME = -1; 36 37 // Preallocated strings we are sending to tron, so we don't have to allocate a new one every 38 // time we log. 39 private static final String[] TRON_WINDOW_STATE_VARZ_STRINGS = { 40 "window_time_0", "window_time_1", "window_time_2"}; 41 42 private int mWindowState = WINDOW_STATE_STANDARD; 43 private long mLastLogTimeSecs; 44 private final ActivityStackSupervisor mSupervisor; 45 private final Context mContext; 46 47 private long mCurrentTransitionStartTime = INVALID_START_TIME; 48 private boolean mLoggedWindowsDrawn; 49 private boolean mLoggedStartingWindowDrawn; 50 private boolean mLoggedTransitionStarting; 51 52 ActivityMetricsLogger(ActivityStackSupervisor supervisor, Context context) { 53 mLastLogTimeSecs = SystemClock.elapsedRealtime() / 1000; 54 mSupervisor = supervisor; 55 mContext = context; 56 } 57 58 void logWindowState() { 59 final long now = SystemClock.elapsedRealtime() / 1000; 60 if (mWindowState != WINDOW_STATE_INVALID) { 61 // We log even if the window state hasn't changed, because the user might remain in 62 // home/fullscreen move forever and we would like to track this kind of behavior 63 // too. 64 MetricsLogger.count(mContext, TRON_WINDOW_STATE_VARZ_STRINGS[mWindowState], 65 (int) (now - mLastLogTimeSecs)); 66 } 67 mLastLogTimeSecs = now; 68 69 ActivityStack stack = mSupervisor.getStack(DOCKED_STACK_ID); 70 if (stack != null && stack.getStackVisibilityLocked(null) != STACK_INVISIBLE) { 71 mWindowState = WINDOW_STATE_SIDE_BY_SIDE; 72 return; 73 } 74 mWindowState = WINDOW_STATE_INVALID; 75 stack = mSupervisor.getFocusedStack(); 76 if (stack.mStackId == PINNED_STACK_ID) { 77 stack = mSupervisor.findStackBehind(stack); 78 } 79 if (stack.mStackId == HOME_STACK_ID 80 || stack.mStackId == FULLSCREEN_WORKSPACE_STACK_ID) { 81 mWindowState = WINDOW_STATE_STANDARD; 82 } else if (stack.mStackId == DOCKED_STACK_ID) { 83 Slog.wtf(TAG, "Docked stack shouldn't be the focused stack, because it reported not" 84 + " being visible."); 85 mWindowState = WINDOW_STATE_INVALID; 86 } else if (stack.mStackId == FREEFORM_WORKSPACE_STACK_ID) { 87 mWindowState = WINDOW_STATE_FREEFORM; 88 } else if (StackId.isStaticStack(stack.mStackId)) { 89 throw new IllegalStateException("Unknown stack=" + stack); 90 } 91 } 92 93 /** 94 * Notifies the tracker at the earliest possible point when we are starting to launch an 95 * activity. 96 */ 97 void notifyActivityLaunching() { 98 mCurrentTransitionStartTime = System.currentTimeMillis(); 99 } 100 101 /** 102 * Notifies the tracker the the activity is actually launching. 103 * 104 * @param resultCode one of the ActivityManager.START_* flags, indicating the result of the 105 * launch 106 * @param componentName the component name of the activity being launched 107 * @param processRunning whether the process that will contains the activity is already running 108 * @param processSwitch whether the process that will contain the activity didn't have any 109 * activity that was stopped, i.e. the started activity is "switching" 110 * processes 111 */ 112 void notifyActivityLaunched(int resultCode, @Nullable String componentName, 113 boolean processRunning, boolean processSwitch) { 114 115 if (resultCode < 0 || componentName == null || !processSwitch) { 116 117 // Failed to launch or it was not a process switch, so we don't care about the timing. 118 reset(); 119 return; 120 } 121 122 MetricsLogger.action(mContext, MetricsEvent.APP_TRANSITION_COMPONENT_NAME, 123 componentName); 124 MetricsLogger.action(mContext, MetricsEvent.APP_TRANSITION_PROCESS_RUNNING, 125 processRunning); 126 MetricsLogger.action(mContext, MetricsEvent.APP_TRANSITION_DEVICE_UPTIME_SECONDS, 127 (int) (SystemClock.uptimeMillis() / 1000)); 128 } 129 130 /** 131 * Notifies the tracker that all windows of the app have been drawn. 132 */ 133 void notifyWindowsDrawn() { 134 if (!isTransitionActive() || mLoggedWindowsDrawn) { 135 return; 136 } 137 MetricsLogger.action(mContext, MetricsEvent.APP_TRANSITION_WINDOWS_DRAWN_DELAY_MS, 138 calculateCurrentDelay()); 139 mLoggedWindowsDrawn = true; 140 if (mLoggedTransitionStarting) { 141 reset(); 142 } 143 } 144 145 /** 146 * Notifies the tracker that the starting window was drawn. 147 */ 148 void notifyStartingWindowDrawn() { 149 if (!isTransitionActive() || mLoggedStartingWindowDrawn) { 150 return; 151 } 152 mLoggedStartingWindowDrawn = true; 153 MetricsLogger.action(mContext, MetricsEvent.APP_TRANSITION_STARTING_WINDOW_DELAY_MS, 154 calculateCurrentDelay()); 155 } 156 157 /** 158 * Notifies the tracker that the app transition is starting. 159 * 160 * @param reason The reason why we started it. Must be on of 161 * ActivityManagerInternal.APP_TRANSITION_* reasons. 162 */ 163 void notifyTransitionStarting(int reason) { 164 if (!isTransitionActive() || mLoggedTransitionStarting) { 165 return; 166 } 167 MetricsLogger.action(mContext, MetricsEvent.APP_TRANSITION_REASON, reason); 168 MetricsLogger.action(mContext, MetricsEvent.APP_TRANSITION_DELAY_MS, 169 calculateCurrentDelay()); 170 mLoggedTransitionStarting = true; 171 if (mLoggedWindowsDrawn) { 172 reset(); 173 } 174 } 175 176 private boolean isTransitionActive() { 177 return mCurrentTransitionStartTime != INVALID_START_TIME; 178 } 179 180 private void reset() { 181 mCurrentTransitionStartTime = INVALID_START_TIME; 182 mLoggedWindowsDrawn = false; 183 mLoggedTransitionStarting = false; 184 mLoggedStartingWindowDrawn = false; 185 } 186 187 private int calculateCurrentDelay() { 188 189 // Shouldn't take more than 25 days to launch an app, so int is fine here. 190 return (int) (System.currentTimeMillis() - mCurrentTransitionStartTime); 191 } 192} 193