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