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