• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
5  * except in compliance with the License. You may obtain a copy of the License at
6  *
7  *      http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software distributed under the
10  * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
11  * KIND, either express or implied. See the License for the specific language governing
12  * permissions and limitations under the License.
13  */
14 
15 package com.android.internal.util;
16 
17 import static android.os.Trace.TRACE_TAG_APP;
18 
19 import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_CHECK_CREDENTIAL;
20 import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_CHECK_CREDENTIAL_UNLOCKED;
21 import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_EXPAND_PANEL;
22 import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_FACE_WAKE_AND_UNLOCK;
23 import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_FINGERPRINT_WAKE_AND_UNLOCK;
24 import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_FOLD_TO_AOD;
25 import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_LOAD_SHARE_SHEET;
26 import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_LOCKSCREEN_UNLOCK;
27 import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_REQUEST_IME_HIDDEN;
28 import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_REQUEST_IME_SHOWN;
29 import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_ROTATE_SCREEN;
30 import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_ROTATE_SCREEN_CAMERA_CHECK;
31 import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_ROTATE_SCREEN_SENSOR;
32 import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_SHOW_BACK_ARROW;
33 import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_SHOW_SELECTION_TOOLBAR;
34 import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_SHOW_VOICE_INTERACTION;
35 import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_SMARTSPACE_DOORBELL;
36 import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_START_RECENTS_ANIMATION;
37 import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_SWITCH_DISPLAY_UNFOLD;
38 import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_TOGGLE_RECENTS;
39 import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_TURN_ON_SCREEN;
40 import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_UDFPS_ILLUMINATE;
41 import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_USER_SWITCH;
42 import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__UNKNOWN_ACTION;
43 import static com.android.internal.util.LatencyTracker.ActionProperties.ENABLE_SUFFIX;
44 import static com.android.internal.util.LatencyTracker.ActionProperties.LEGACY_TRACE_THRESHOLD_SUFFIX;
45 import static com.android.internal.util.LatencyTracker.ActionProperties.SAMPLE_INTERVAL_SUFFIX;
46 import static com.android.internal.util.LatencyTracker.ActionProperties.TRACE_THRESHOLD_SUFFIX;
47 
48 import android.annotation.IntDef;
49 import android.annotation.NonNull;
50 import android.annotation.Nullable;
51 import android.content.Context;
52 import android.os.Build;
53 import android.os.ConditionVariable;
54 import android.os.SystemClock;
55 import android.os.Trace;
56 import android.provider.DeviceConfig;
57 import android.text.TextUtils;
58 import android.util.EventLog;
59 import android.util.Log;
60 import android.util.SparseArray;
61 
62 import com.android.internal.annotations.GuardedBy;
63 import com.android.internal.annotations.VisibleForTesting;
64 import com.android.internal.logging.EventLogTags;
65 import com.android.internal.os.BackgroundThread;
66 
67 import java.lang.annotation.Retention;
68 import java.lang.annotation.RetentionPolicy;
69 import java.util.Locale;
70 import java.util.concurrent.ThreadLocalRandom;
71 import java.util.concurrent.TimeUnit;
72 
73 /**
74  * Class to track various latencies in SystemUI. It then writes the latency to statsd and also
75  * outputs it to logcat so these latencies can be captured by tests and then used for dashboards.
76  * <p>
77  * This is currently only in Keyguard so it can be shared between SystemUI and Keyguard, but
78  * eventually we'd want to merge these two packages together so Keyguard can use common classes
79  * that are shared with SystemUI.
80  */
81 public class LatencyTracker {
82     private static final String TAG = "LatencyTracker";
83     public static final String SETTINGS_ENABLED_KEY = "enabled";
84     private static final String SETTINGS_SAMPLING_INTERVAL_KEY = "sampling_interval";
85     private static final boolean DEBUG = false;
86     /** Default to being enabled on debug builds. */
87     private static final boolean DEFAULT_ENABLED = Build.IS_DEBUGGABLE;
88     /** Default to collecting data for 1/5 of all actions (randomly sampled). */
89     private static final int DEFAULT_SAMPLING_INTERVAL = 5;
90 
91     /**
92      * Time it takes until the first frame of the notification panel to be displayed while expanding
93      */
94     public static final int ACTION_EXPAND_PANEL = 0;
95 
96     /**
97      * Time it takes until the first frame of recents is drawn after invoking it with the button.
98      */
99     public static final int ACTION_TOGGLE_RECENTS = 1;
100 
101     /**
102      * Time between we get a fingerprint acquired signal until we start with the unlock animation
103      */
104     public static final int ACTION_FINGERPRINT_WAKE_AND_UNLOCK = 2;
105 
106     /**
107      * Time it takes to check PIN/Pattern/Password.
108      */
109     public static final int ACTION_CHECK_CREDENTIAL = 3;
110 
111     /**
112      * Time it takes to check fully PIN/Pattern/Password, i.e. that's the time spent including the
113      * actions to unlock a user.
114      */
115     public static final int ACTION_CHECK_CREDENTIAL_UNLOCKED = 4;
116 
117     /**
118      * Time it takes to turn on the screen.
119      */
120     public static final int ACTION_TURN_ON_SCREEN = 5;
121 
122     /**
123      * Time it takes to rotate the screen.
124      */
125     public static final int ACTION_ROTATE_SCREEN = 6;
126 
127     /*
128      * Time between we get a face acquired signal until we start with the unlock animation
129      */
130     public static final int ACTION_FACE_WAKE_AND_UNLOCK = 7;
131 
132     /**
133      * Time between the swipe-up gesture and window drawn of recents activity.
134      */
135     public static final int ACTION_START_RECENTS_ANIMATION = 8;
136 
137     /**
138      * Time it takes to for the camera based algorithm to rotate the screen.
139      */
140     public static final int ACTION_ROTATE_SCREEN_CAMERA_CHECK = 9;
141 
142     /**
143      * Time it takes the sensor to detect rotation.
144      */
145     public static final int ACTION_ROTATE_SCREEN_SENSOR = 10;
146 
147     /**
148      * Time it takes to start unlock animation .
149      */
150     public static final int ACTION_LOCKSCREEN_UNLOCK = 11;
151 
152     /**
153      * Time it takes to switch users.
154      */
155     public static final int ACTION_USER_SWITCH = 12;
156 
157     /**
158      * Time it takes to turn on the inner screen for a foldable device.
159      */
160     public static final int ACTION_SWITCH_DISPLAY_UNFOLD = 13;
161 
162     /**
163      * Time it takes for a UDFPS sensor to appear ready after it is touched.
164      */
165     public static final int ACTION_UDFPS_ILLUMINATE = 14;
166 
167     /**
168      * Time it takes for the gesture back affordance arrow to show up.
169      */
170     public static final int ACTION_SHOW_BACK_ARROW = 15;
171 
172     /**
173      * Time it takes for loading share sheet.
174      */
175     public static final int ACTION_LOAD_SHARE_SHEET = 16;
176 
177     /**
178      * Time it takes for showing the selection toolbar.
179      */
180     public static final int ACTION_SHOW_SELECTION_TOOLBAR = 17;
181 
182     /**
183      * Time it takes to show AOD display after folding the device.
184      */
185     public static final int ACTION_FOLD_TO_AOD = 18;
186 
187     /**
188      * Time it takes to show the {@link android.service.voice.VoiceInteractionSession} system UI
189      * after a {@link android.hardware.soundtrigger3.ISoundTriggerHw} voice trigger.
190      */
191     public static final int ACTION_SHOW_VOICE_INTERACTION = 19;
192 
193     /**
194      * Time it takes to request IME shown animation.
195      */
196     public static final int ACTION_REQUEST_IME_SHOWN = 20;
197 
198     /**
199      * Time it takes to request IME hidden animation.
200      */
201     public static final int ACTION_REQUEST_IME_HIDDEN = 21;
202 
203     /**
204      * Time it takes to load the animation frames in smart space doorbell card.
205      * It measures the duration from the images uris are passed into the view
206      * to all the frames are loaded.
207      * <p/>
208      * A long latency makes the doorbell animation looks janky until all the frames are loaded.
209      */
210     public static final int ACTION_SMARTSPACE_DOORBELL = 22;
211 
212     private static final int[] ACTIONS_ALL = {
213         ACTION_EXPAND_PANEL,
214         ACTION_TOGGLE_RECENTS,
215         ACTION_FINGERPRINT_WAKE_AND_UNLOCK,
216         ACTION_CHECK_CREDENTIAL,
217         ACTION_CHECK_CREDENTIAL_UNLOCKED,
218         ACTION_TURN_ON_SCREEN,
219         ACTION_ROTATE_SCREEN,
220         ACTION_FACE_WAKE_AND_UNLOCK,
221         ACTION_START_RECENTS_ANIMATION,
222         ACTION_ROTATE_SCREEN_CAMERA_CHECK,
223         ACTION_ROTATE_SCREEN_SENSOR,
224         ACTION_LOCKSCREEN_UNLOCK,
225         ACTION_USER_SWITCH,
226         ACTION_SWITCH_DISPLAY_UNFOLD,
227         ACTION_UDFPS_ILLUMINATE,
228         ACTION_SHOW_BACK_ARROW,
229         ACTION_LOAD_SHARE_SHEET,
230         ACTION_SHOW_SELECTION_TOOLBAR,
231         ACTION_FOLD_TO_AOD,
232         ACTION_SHOW_VOICE_INTERACTION,
233         ACTION_REQUEST_IME_SHOWN,
234         ACTION_REQUEST_IME_HIDDEN,
235         ACTION_SMARTSPACE_DOORBELL,
236     };
237 
238     /** @hide */
239     @IntDef({
240         ACTION_EXPAND_PANEL,
241         ACTION_TOGGLE_RECENTS,
242         ACTION_FINGERPRINT_WAKE_AND_UNLOCK,
243         ACTION_CHECK_CREDENTIAL,
244         ACTION_CHECK_CREDENTIAL_UNLOCKED,
245         ACTION_TURN_ON_SCREEN,
246         ACTION_ROTATE_SCREEN,
247         ACTION_FACE_WAKE_AND_UNLOCK,
248         ACTION_START_RECENTS_ANIMATION,
249         ACTION_ROTATE_SCREEN_CAMERA_CHECK,
250         ACTION_ROTATE_SCREEN_SENSOR,
251         ACTION_LOCKSCREEN_UNLOCK,
252         ACTION_USER_SWITCH,
253         ACTION_SWITCH_DISPLAY_UNFOLD,
254         ACTION_UDFPS_ILLUMINATE,
255         ACTION_SHOW_BACK_ARROW,
256         ACTION_LOAD_SHARE_SHEET,
257         ACTION_SHOW_SELECTION_TOOLBAR,
258         ACTION_FOLD_TO_AOD,
259         ACTION_SHOW_VOICE_INTERACTION,
260         ACTION_REQUEST_IME_SHOWN,
261         ACTION_REQUEST_IME_HIDDEN,
262         ACTION_SMARTSPACE_DOORBELL,
263     })
264     @Retention(RetentionPolicy.SOURCE)
265     public @interface Action {
266     }
267 
268     @VisibleForTesting
269     public static final int[] STATSD_ACTION = new int[] {
270             UIACTION_LATENCY_REPORTED__ACTION__ACTION_EXPAND_PANEL,
271             UIACTION_LATENCY_REPORTED__ACTION__ACTION_TOGGLE_RECENTS,
272             UIACTION_LATENCY_REPORTED__ACTION__ACTION_FINGERPRINT_WAKE_AND_UNLOCK,
273             UIACTION_LATENCY_REPORTED__ACTION__ACTION_CHECK_CREDENTIAL,
274             UIACTION_LATENCY_REPORTED__ACTION__ACTION_CHECK_CREDENTIAL_UNLOCKED,
275             UIACTION_LATENCY_REPORTED__ACTION__ACTION_TURN_ON_SCREEN,
276             UIACTION_LATENCY_REPORTED__ACTION__ACTION_ROTATE_SCREEN,
277             UIACTION_LATENCY_REPORTED__ACTION__ACTION_FACE_WAKE_AND_UNLOCK,
278             UIACTION_LATENCY_REPORTED__ACTION__ACTION_START_RECENTS_ANIMATION,
279             UIACTION_LATENCY_REPORTED__ACTION__ACTION_ROTATE_SCREEN_CAMERA_CHECK,
280             UIACTION_LATENCY_REPORTED__ACTION__ACTION_ROTATE_SCREEN_SENSOR,
281             UIACTION_LATENCY_REPORTED__ACTION__ACTION_LOCKSCREEN_UNLOCK,
282             UIACTION_LATENCY_REPORTED__ACTION__ACTION_USER_SWITCH,
283             UIACTION_LATENCY_REPORTED__ACTION__ACTION_SWITCH_DISPLAY_UNFOLD,
284             UIACTION_LATENCY_REPORTED__ACTION__ACTION_UDFPS_ILLUMINATE,
285             UIACTION_LATENCY_REPORTED__ACTION__ACTION_SHOW_BACK_ARROW,
286             UIACTION_LATENCY_REPORTED__ACTION__ACTION_LOAD_SHARE_SHEET,
287             UIACTION_LATENCY_REPORTED__ACTION__ACTION_SHOW_SELECTION_TOOLBAR,
288             UIACTION_LATENCY_REPORTED__ACTION__ACTION_FOLD_TO_AOD,
289             UIACTION_LATENCY_REPORTED__ACTION__ACTION_SHOW_VOICE_INTERACTION,
290             UIACTION_LATENCY_REPORTED__ACTION__ACTION_REQUEST_IME_SHOWN,
291             UIACTION_LATENCY_REPORTED__ACTION__ACTION_REQUEST_IME_HIDDEN,
292             UIACTION_LATENCY_REPORTED__ACTION__ACTION_SMARTSPACE_DOORBELL,
293     };
294 
295     private static LatencyTracker sLatencyTracker;
296 
297     private final Object mLock = new Object();
298     @GuardedBy("mLock")
299     private final SparseArray<Session> mSessions = new SparseArray<>();
300     @GuardedBy("mLock")
301     private final SparseArray<ActionProperties> mActionPropertiesMap = new SparseArray<>();
302     @GuardedBy("mLock")
303     private boolean mEnabled;
304     @VisibleForTesting
305     public final ConditionVariable mDeviceConfigPropertiesUpdated = new ConditionVariable();
306 
getInstance(Context context)307     public static LatencyTracker getInstance(Context context) {
308         if (sLatencyTracker == null) {
309             synchronized (LatencyTracker.class) {
310                 if (sLatencyTracker == null) {
311                     sLatencyTracker = new LatencyTracker();
312                 }
313             }
314         }
315         return sLatencyTracker;
316     }
317 
318     @VisibleForTesting
LatencyTracker()319     public LatencyTracker() {
320         mEnabled = DEFAULT_ENABLED;
321 
322         // Post initialization to the background in case we're running on the main thread.
323         BackgroundThread.getHandler().post(() -> this.updateProperties(
324                 DeviceConfig.getProperties(DeviceConfig.NAMESPACE_LATENCY_TRACKER)));
325         DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_LATENCY_TRACKER,
326                 BackgroundThread.getExecutor(), this::updateProperties);
327     }
328 
updateProperties(DeviceConfig.Properties properties)329     private void updateProperties(DeviceConfig.Properties properties) {
330         synchronized (mLock) {
331             int samplingInterval = properties.getInt(SETTINGS_SAMPLING_INTERVAL_KEY,
332                     DEFAULT_SAMPLING_INTERVAL);
333             mEnabled = properties.getBoolean(SETTINGS_ENABLED_KEY, DEFAULT_ENABLED);
334             for (int action : ACTIONS_ALL) {
335                 String actionName = getNameOfAction(STATSD_ACTION[action]).toLowerCase(Locale.ROOT);
336                 int legacyActionTraceThreshold = properties.getInt(
337                         actionName + LEGACY_TRACE_THRESHOLD_SUFFIX, -1);
338                 mActionPropertiesMap.put(action, new ActionProperties(action,
339                         properties.getBoolean(actionName + ENABLE_SUFFIX, mEnabled),
340                         properties.getInt(actionName + SAMPLE_INTERVAL_SUFFIX, samplingInterval),
341                         properties.getInt(actionName + TRACE_THRESHOLD_SUFFIX,
342                                 legacyActionTraceThreshold)));
343             }
344             if (DEBUG) {
345                 Log.d(TAG, "updated action properties: " + mActionPropertiesMap);
346             }
347         }
348         mDeviceConfigPropertiesUpdated.open();
349     }
350 
351     /**
352      * A helper method to translate action type to name.
353      *
354      * @param atomsProtoAction the action type defined in AtomsProto.java
355      * @return the name of the action
356      */
getNameOfAction(int atomsProtoAction)357     public static String getNameOfAction(int atomsProtoAction) {
358         // Defined in AtomsProto.java
359         switch (atomsProtoAction) {
360             case UIACTION_LATENCY_REPORTED__ACTION__UNKNOWN_ACTION:
361                 return "UNKNOWN";
362             case UIACTION_LATENCY_REPORTED__ACTION__ACTION_EXPAND_PANEL:
363                 return "ACTION_EXPAND_PANEL";
364             case UIACTION_LATENCY_REPORTED__ACTION__ACTION_TOGGLE_RECENTS:
365                 return "ACTION_TOGGLE_RECENTS";
366             case UIACTION_LATENCY_REPORTED__ACTION__ACTION_FINGERPRINT_WAKE_AND_UNLOCK:
367                 return "ACTION_FINGERPRINT_WAKE_AND_UNLOCK";
368             case UIACTION_LATENCY_REPORTED__ACTION__ACTION_CHECK_CREDENTIAL:
369                 return "ACTION_CHECK_CREDENTIAL";
370             case UIACTION_LATENCY_REPORTED__ACTION__ACTION_CHECK_CREDENTIAL_UNLOCKED:
371                 return "ACTION_CHECK_CREDENTIAL_UNLOCKED";
372             case UIACTION_LATENCY_REPORTED__ACTION__ACTION_TURN_ON_SCREEN:
373                 return "ACTION_TURN_ON_SCREEN";
374             case UIACTION_LATENCY_REPORTED__ACTION__ACTION_ROTATE_SCREEN:
375                 return "ACTION_ROTATE_SCREEN";
376             case UIACTION_LATENCY_REPORTED__ACTION__ACTION_FACE_WAKE_AND_UNLOCK:
377                 return "ACTION_FACE_WAKE_AND_UNLOCK";
378             case UIACTION_LATENCY_REPORTED__ACTION__ACTION_START_RECENTS_ANIMATION:
379                 return "ACTION_START_RECENTS_ANIMATION";
380             case UIACTION_LATENCY_REPORTED__ACTION__ACTION_ROTATE_SCREEN_CAMERA_CHECK:
381                 return "ACTION_ROTATE_SCREEN_CAMERA_CHECK";
382             case UIACTION_LATENCY_REPORTED__ACTION__ACTION_ROTATE_SCREEN_SENSOR:
383                 return "ACTION_ROTATE_SCREEN_SENSOR";
384             case UIACTION_LATENCY_REPORTED__ACTION__ACTION_LOCKSCREEN_UNLOCK:
385                 return "ACTION_LOCKSCREEN_UNLOCK";
386             case UIACTION_LATENCY_REPORTED__ACTION__ACTION_USER_SWITCH:
387                 return "ACTION_USER_SWITCH";
388             case UIACTION_LATENCY_REPORTED__ACTION__ACTION_SWITCH_DISPLAY_UNFOLD:
389                 return "ACTION_SWITCH_DISPLAY_UNFOLD";
390             case UIACTION_LATENCY_REPORTED__ACTION__ACTION_UDFPS_ILLUMINATE:
391                 return "ACTION_UDFPS_ILLUMINATE";
392             case UIACTION_LATENCY_REPORTED__ACTION__ACTION_SHOW_BACK_ARROW:
393                 return "ACTION_SHOW_BACK_ARROW";
394             case UIACTION_LATENCY_REPORTED__ACTION__ACTION_LOAD_SHARE_SHEET:
395                 return "ACTION_LOAD_SHARE_SHEET";
396             case UIACTION_LATENCY_REPORTED__ACTION__ACTION_SHOW_SELECTION_TOOLBAR:
397                 return "ACTION_SHOW_SELECTION_TOOLBAR";
398             case UIACTION_LATENCY_REPORTED__ACTION__ACTION_FOLD_TO_AOD:
399                 return "ACTION_FOLD_TO_AOD";
400             case UIACTION_LATENCY_REPORTED__ACTION__ACTION_SHOW_VOICE_INTERACTION:
401                 return "ACTION_SHOW_VOICE_INTERACTION";
402             case UIACTION_LATENCY_REPORTED__ACTION__ACTION_REQUEST_IME_SHOWN:
403                 return "ACTION_REQUEST_IME_SHOWN";
404             case UIACTION_LATENCY_REPORTED__ACTION__ACTION_REQUEST_IME_HIDDEN:
405                 return "ACTION_REQUEST_IME_HIDDEN";
406             case UIACTION_LATENCY_REPORTED__ACTION__ACTION_SMARTSPACE_DOORBELL:
407                 return "ACTION_SMARTSPACE_DOORBELL";
408             default:
409                 throw new IllegalArgumentException("Invalid action");
410         }
411     }
412 
getTraceNameOfAction(@ction int action, String tag)413     private static String getTraceNameOfAction(@Action int action, String tag) {
414         if (TextUtils.isEmpty(tag)) {
415             return "L<" + getNameOfAction(STATSD_ACTION[action]) + ">";
416         } else {
417             return "L<" + getNameOfAction(STATSD_ACTION[action]) + "::" + tag + ">";
418         }
419     }
420 
getTraceTriggerNameForAction(@ction int action)421     private static String getTraceTriggerNameForAction(@Action int action) {
422         return "com.android.telemetry.latency-tracker-" + getNameOfAction(STATSD_ACTION[action]);
423     }
424 
425     /**
426      * @deprecated Use {@link #isEnabled(Context, int)}
427      */
428     @Deprecated
isEnabled(Context ctx)429     public static boolean isEnabled(Context ctx) {
430         return getInstance(ctx).isEnabled();
431     }
432 
433     /**
434      * @deprecated Used {@link #isEnabled(int)}
435      */
436     @Deprecated
isEnabled()437     public boolean isEnabled() {
438         synchronized (mLock) {
439             return mEnabled;
440         }
441     }
442 
isEnabled(Context ctx, int action)443     public static boolean isEnabled(Context ctx, int action) {
444         return getInstance(ctx).isEnabled(action);
445     }
446 
isEnabled(int action)447     public boolean isEnabled(int action) {
448         synchronized (mLock) {
449             ActionProperties actionProperties = mActionPropertiesMap.get(action);
450             if (actionProperties != null) {
451                 return actionProperties.isEnabled();
452             }
453             return false;
454         }
455     }
456 
457     /**
458      * Notifies that an action is starting. <s>This needs to be called from the main thread.</s>
459      *
460      * @param action The action to start. One of the ACTION_* values.
461      */
onActionStart(@ction int action)462     public void onActionStart(@Action int action) {
463         onActionStart(action, null);
464     }
465 
466     /**
467      * Notifies that an action is starting. <s>This needs to be called from the main thread.</s>
468      *
469      * @param action The action to start. One of the ACTION_* values.
470      * @param tag The brief description of the action.
471      */
onActionStart(@ction int action, String tag)472     public void onActionStart(@Action int action, String tag) {
473         synchronized (mLock) {
474             if (!isEnabled(action)) {
475                 return;
476             }
477             // skip if the action is already instrumenting.
478             if (mSessions.get(action) != null) {
479                 return;
480             }
481             Session session = new Session(action, tag);
482             session.begin(() -> onActionCancel(action));
483             mSessions.put(action, session);
484 
485             if (DEBUG) {
486                 Log.d(TAG, "onActionStart: " + session.name() + ", start=" + session.mStartRtc);
487             }
488         }
489     }
490 
491     /**
492      * Notifies that an action has ended. <s>This needs to be called from the main thread.</s>
493      *
494      * @param action The action to end. One of the ACTION_* values.
495      */
onActionEnd(@ction int action)496     public void onActionEnd(@Action int action) {
497         synchronized (mLock) {
498             if (!isEnabled(action)) {
499                 return;
500             }
501             Session session = mSessions.get(action);
502             if (session == null) {
503                 return;
504             }
505             session.end();
506             mSessions.delete(action);
507             logAction(action, session.duration());
508 
509             if (DEBUG) {
510                 Log.d(TAG, "onActionEnd:" + session.name() + ", duration=" + session.duration());
511             }
512         }
513     }
514 
515     /**
516      * Notifies that an action has canceled. <s>This needs to be called from the main thread.</s>
517      *
518      * @param action The action to cancel. One of the ACTION_* values.
519      * @hide
520      */
onActionCancel(@ction int action)521     public void onActionCancel(@Action int action) {
522         synchronized (mLock) {
523             Session session = mSessions.get(action);
524             if (session == null) {
525                 return;
526             }
527             session.cancel();
528             mSessions.delete(action);
529 
530             if (DEBUG) {
531                 Log.d(TAG, "onActionCancel: " + session.name());
532             }
533         }
534     }
535 
536     /**
537      * Logs an action that has started and ended. This needs to be called from the main thread.
538      *
539      * @param action   The action to end. One of the ACTION_* values.
540      * @param duration The duration of the action in ms.
541      */
logAction(@ction int action, int duration)542     public void logAction(@Action int action, int duration) {
543         boolean shouldSample;
544         int traceThreshold;
545         synchronized (mLock) {
546             ActionProperties actionProperties = mActionPropertiesMap.get(action);
547             if (actionProperties == null) {
548                 return;
549             }
550             int nextRandNum = ThreadLocalRandom.current().nextInt(
551                     actionProperties.getSamplingInterval());
552             shouldSample = nextRandNum == 0;
553             traceThreshold = actionProperties.getTraceThreshold();
554         }
555 
556         if (traceThreshold > 0 && duration >= traceThreshold) {
557             PerfettoTrigger.trigger(getTraceTriggerNameForAction(action));
558         }
559 
560         logActionDeprecated(action, duration, shouldSample);
561     }
562 
563     /**
564      * Logs an action that has started and ended. This needs to be called from the main thread.
565      *
566      * @param action The action to end. One of the ACTION_* values.
567      * @param duration The duration of the action in ms.
568      * @param writeToStatsLog Whether to write the measured latency to FrameworkStatsLog.
569      */
logActionDeprecated( @ction int action, int duration, boolean writeToStatsLog)570     public static void logActionDeprecated(
571             @Action int action, int duration, boolean writeToStatsLog) {
572         Log.i(TAG, getNameOfAction(STATSD_ACTION[action]) + " latency=" + duration);
573         EventLog.writeEvent(EventLogTags.SYSUI_LATENCY, action, duration);
574 
575         if (writeToStatsLog) {
576             FrameworkStatsLog.write(
577                     FrameworkStatsLog.UI_ACTION_LATENCY_REPORTED, STATSD_ACTION[action], duration);
578         }
579     }
580 
581     static class Session {
582         @Action
583         private final int mAction;
584         private final String mTag;
585         private final String mName;
586         private Runnable mTimeoutRunnable;
587         private long mStartRtc = -1;
588         private long mEndRtc = -1;
589 
Session(@ction int action, @Nullable String tag)590         Session(@Action int action, @Nullable String tag) {
591             mAction = action;
592             mTag = tag;
593             mName = TextUtils.isEmpty(mTag)
594                     ? getNameOfAction(STATSD_ACTION[mAction])
595                     : getNameOfAction(STATSD_ACTION[mAction]) + "::" + mTag;
596         }
597 
name()598         String name() {
599             return mName;
600         }
601 
traceName()602         String traceName() {
603             return getTraceNameOfAction(mAction, mTag);
604         }
605 
begin(@onNull Runnable timeoutAction)606         void begin(@NonNull Runnable timeoutAction) {
607             mStartRtc = SystemClock.elapsedRealtime();
608             Trace.asyncTraceForTrackBegin(TRACE_TAG_APP, traceName(), traceName(), 0);
609 
610             // start counting timeout.
611             mTimeoutRunnable = () -> {
612                 Trace.instantForTrack(TRACE_TAG_APP, traceName(), "timeout");
613                 timeoutAction.run();
614             };
615             BackgroundThread.getHandler()
616                     .postDelayed(mTimeoutRunnable, TimeUnit.SECONDS.toMillis(15));
617         }
618 
end()619         void end() {
620             mEndRtc = SystemClock.elapsedRealtime();
621             Trace.asyncTraceForTrackEnd(TRACE_TAG_APP, traceName(), "end", 0);
622             BackgroundThread.getHandler().removeCallbacks(mTimeoutRunnable);
623             mTimeoutRunnable = null;
624         }
625 
cancel()626         void cancel() {
627             Trace.instantForTrack(TRACE_TAG_APP, traceName(), "cancel");
628             Trace.asyncTraceForTrackEnd(TRACE_TAG_APP, traceName(), "cancel", 0);
629             BackgroundThread.getHandler().removeCallbacks(mTimeoutRunnable);
630             mTimeoutRunnable = null;
631         }
632 
duration()633         int duration() {
634             return (int) (mEndRtc - mStartRtc);
635         }
636     }
637 
638     @VisibleForTesting
639     static class ActionProperties {
640         static final String ENABLE_SUFFIX = "_enable";
641         static final String SAMPLE_INTERVAL_SUFFIX = "_sample_interval";
642         // TODO: migrate all usages of the legacy trace theshold property
643         static final String LEGACY_TRACE_THRESHOLD_SUFFIX = "";
644         static final String TRACE_THRESHOLD_SUFFIX = "_trace_threshold";
645 
646         @Action
647         private final int mAction;
648         private final boolean mEnabled;
649         private final int mSamplingInterval;
650         private final int mTraceThreshold;
651 
ActionProperties( @ction int action, boolean enabled, int samplingInterval, int traceThreshold)652         ActionProperties(
653                 @Action int action,
654                 boolean enabled,
655                 int samplingInterval,
656                 int traceThreshold) {
657             this.mAction = action;
658             com.android.internal.util.AnnotationValidations.validate(
659                     Action.class, null, mAction);
660             this.mEnabled = enabled;
661             this.mSamplingInterval = samplingInterval;
662             this.mTraceThreshold = traceThreshold;
663         }
664 
665         @Action
getAction()666         int getAction() {
667             return mAction;
668         }
669 
isEnabled()670         boolean isEnabled() {
671             return mEnabled;
672         }
673 
getSamplingInterval()674         int getSamplingInterval() {
675             return mSamplingInterval;
676         }
677 
getTraceThreshold()678         int getTraceThreshold() {
679             return mTraceThreshold;
680         }
681 
682         @Override
toString()683         public String toString() {
684             return "ActionProperties{"
685                     + " mAction=" + mAction
686                     + ", mEnabled=" + mEnabled
687                     + ", mSamplingInterval=" + mSamplingInterval
688                     + ", mTraceThreshold=" + mTraceThreshold
689                     + "}";
690         }
691     }
692 }
693