• 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.Manifest.permission.READ_DEVICE_CONFIG;
18 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
19 import static android.os.Trace.TRACE_TAG_APP;
20 import static android.provider.DeviceConfig.NAMESPACE_LATENCY_TRACKER;
21 
22 import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_BACK_SYSTEM_ANIMATION;
23 import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_CHECK_CREDENTIAL;
24 import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_CHECK_CREDENTIAL_UNLOCKED;
25 import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG;
26 import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_DESKTOP_MODE_ENTER_APP_HANDLE_MENU;
27 import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_DESKTOP_MODE_EXIT_MODE;
28 import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_EXPAND_PANEL;
29 import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_FACE_WAKE_AND_UNLOCK;
30 import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_FINGERPRINT_WAKE_AND_UNLOCK;
31 import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_FOLD_TO_AOD;
32 import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_NOTIFICATIONS_HIDDEN_FOR_MEASURE;
33 import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_NOTIFICATIONS_HIDDEN_FOR_MEASURE_WITH_SHADE_OPEN;
34 import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_KEYGUARD_FACE_UNLOCK_TO_HOME;
35 import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_KEYGUARD_FPS_UNLOCK_TO_HOME;
36 import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_LOAD_SHARE_SHEET;
37 import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_LOCKSCREEN_UNLOCK;
38 import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_NOTIFICATION_BIG_PICTURE_LOADED;
39 import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_REQUEST_IME_HIDDEN;
40 import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_REQUEST_IME_SHOWN;
41 import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_ROTATE_SCREEN;
42 import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_ROTATE_SCREEN_CAMERA_CHECK;
43 import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_ROTATE_SCREEN_SENSOR;
44 import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_SHADE_WINDOW_DISPLAY_CHANGE;
45 import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_SHOW_BACK_ARROW;
46 import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_SHOW_SELECTION_TOOLBAR;
47 import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_SHOW_VOICE_INTERACTION;
48 import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_SMARTSPACE_DOORBELL;
49 import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_START_RECENTS_ANIMATION;
50 import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_SWITCH_DISPLAY_UNFOLD;
51 import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_TOGGLE_RECENTS;
52 import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_TURN_ON_SCREEN;
53 import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_UDFPS_ILLUMINATE;
54 import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_USER_SWITCH;
55 import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__UNKNOWN_ACTION;
56 import static com.android.internal.util.LatencyTracker.ActionProperties.ENABLE_SUFFIX;
57 import static com.android.internal.util.LatencyTracker.ActionProperties.LEGACY_TRACE_THRESHOLD_SUFFIX;
58 import static com.android.internal.util.LatencyTracker.ActionProperties.SAMPLE_INTERVAL_SUFFIX;
59 import static com.android.internal.util.LatencyTracker.ActionProperties.TRACE_THRESHOLD_SUFFIX;
60 
61 import android.Manifest;
62 import android.annotation.ElapsedRealtimeLong;
63 import android.annotation.IntDef;
64 import android.annotation.NonNull;
65 import android.annotation.Nullable;
66 import android.annotation.RequiresPermission;
67 import android.app.ActivityThread;
68 import android.content.Context;
69 import android.os.Build;
70 import android.os.SystemClock;
71 import android.os.Trace;
72 import android.provider.DeviceConfig;
73 import android.text.TextUtils;
74 import android.util.EventLog;
75 import android.util.Log;
76 import android.util.SparseArray;
77 
78 import com.android.internal.annotations.GuardedBy;
79 import com.android.internal.annotations.VisibleForTesting;
80 import com.android.internal.logging.EventLogTags;
81 import com.android.internal.os.BackgroundThread;
82 
83 import java.lang.annotation.Retention;
84 import java.lang.annotation.RetentionPolicy;
85 import java.util.Locale;
86 import java.util.concurrent.ThreadLocalRandom;
87 import java.util.concurrent.TimeUnit;
88 
89 /**
90  * Class to track various latencies in SystemUI. It then writes the latency to statsd and also
91  * outputs it to logcat so these latencies can be captured by tests and then used for dashboards.
92  * <p>
93  * This is currently only in Keyguard. It can be shared between SystemUI and Keyguard, but
94  * eventually we'd want to merge these two packages together so Keyguard can use common classes
95  * that are shared with SystemUI.
96  */
97 public class LatencyTracker {
98     private static final String TAG = "LatencyTracker";
99     public static final String SETTINGS_ENABLED_KEY = "enabled";
100     private static final String SETTINGS_SAMPLING_INTERVAL_KEY = "sampling_interval";
101     private static final boolean DEBUG = false;
102     /** Default to being enabled on debug builds. */
103     private static final boolean DEFAULT_ENABLED = Build.IS_DEBUGGABLE;
104     /** Default to collecting data for 1/5 of all actions (randomly sampled). */
105     private static final int DEFAULT_SAMPLING_INTERVAL = 5;
106 
107     /**
108      * Time it takes until the first frame of the notification panel to be displayed while expanding
109      */
110     public static final int ACTION_EXPAND_PANEL = 0;
111 
112     /**
113      * Time it takes until the first frame of recents is drawn after invoking it with the button.
114      */
115     public static final int ACTION_TOGGLE_RECENTS = 1;
116 
117     /**
118      * Time between we get a fingerprint acquired signal until we start with the unlock animation
119      */
120     public static final int ACTION_FINGERPRINT_WAKE_AND_UNLOCK = 2;
121 
122     /**
123      * Time it takes to check PIN/Pattern/Password.
124      */
125     public static final int ACTION_CHECK_CREDENTIAL = 3;
126 
127     /**
128      * Time it takes to check fully PIN/Pattern/Password, i.e. that's the time spent including the
129      * actions to unlock a user.
130      */
131     public static final int ACTION_CHECK_CREDENTIAL_UNLOCKED = 4;
132 
133     /**
134      * Time it takes to turn on the screen.
135      */
136     public static final int ACTION_TURN_ON_SCREEN = 5;
137 
138     /**
139      * Time it takes to rotate the screen.
140      */
141     public static final int ACTION_ROTATE_SCREEN = 6;
142 
143     /*
144      * Time between we get a face acquired signal until we start with the unlock animation
145      */
146     public static final int ACTION_FACE_WAKE_AND_UNLOCK = 7;
147 
148     /**
149      * Time between the swipe-up gesture and window drawn of recents activity.
150      */
151     public static final int ACTION_START_RECENTS_ANIMATION = 8;
152 
153     /**
154      * Time it takes to for the camera based algorithm to rotate the screen.
155      */
156     public static final int ACTION_ROTATE_SCREEN_CAMERA_CHECK = 9;
157 
158     /**
159      * Time it takes the sensor to detect rotation.
160      */
161     public static final int ACTION_ROTATE_SCREEN_SENSOR = 10;
162 
163     /**
164      * Time it takes to start unlock animation .
165      */
166     public static final int ACTION_LOCKSCREEN_UNLOCK = 11;
167 
168     /**
169      * Time it takes to switch users.
170      */
171     public static final int ACTION_USER_SWITCH = 12;
172 
173     /**
174      * Time it takes to turn on the inner screen for a foldable device.
175      */
176     public static final int ACTION_SWITCH_DISPLAY_UNFOLD = 13;
177 
178     /**
179      * Time it takes for a UDFPS sensor to appear ready after it is touched.
180      */
181     public static final int ACTION_UDFPS_ILLUMINATE = 14;
182 
183     /**
184      * Time it takes for the gesture back affordance arrow to show up.
185      */
186     public static final int ACTION_SHOW_BACK_ARROW = 15;
187 
188     /**
189      * Time it takes for loading share sheet.
190      */
191     public static final int ACTION_LOAD_SHARE_SHEET = 16;
192 
193     /**
194      * Time it takes for showing the selection toolbar.
195      */
196     public static final int ACTION_SHOW_SELECTION_TOOLBAR = 17;
197 
198     /**
199      * Time it takes to show AOD display after folding the device.
200      */
201     public static final int ACTION_FOLD_TO_AOD = 18;
202 
203     /**
204      * Time it takes to show the {@link android.service.voice.VoiceInteractionSession} system UI
205      * after a {@link android.hardware.soundtrigger3.ISoundTriggerHw} voice trigger.
206      */
207     public static final int ACTION_SHOW_VOICE_INTERACTION = 19;
208 
209     /**
210      * Time it takes to request IME shown animation.
211      */
212     public static final int ACTION_REQUEST_IME_SHOWN = 20;
213 
214     /**
215      * Time it takes to request IME hidden animation.
216      */
217     public static final int ACTION_REQUEST_IME_HIDDEN = 21;
218 
219     /**
220      * Time it takes to load the animation frames in smart space doorbell card.
221      * It measures the duration from the images uris are passed into the view
222      * to all the frames are loaded.
223      * <p/>
224      * A long latency makes the doorbell animation looks janky until all the frames are loaded.
225      */
226     public static final int ACTION_SMARTSPACE_DOORBELL = 22;
227 
228     /**
229      * Time it takes to lazy-load the image of a {@link android.app.Notification.BigPictureStyle}
230      * notification.
231      */
232     public static final int ACTION_NOTIFICATION_BIG_PICTURE_LOADED = 23;
233 
234     /**
235      * Time it takes to unlock the device via fps,
236      * until either the launcher or the foreground app appears.
237      */
238     public static final int ACTION_KEYGUARD_FPS_UNLOCK_TO_HOME = 24;
239 
240     /**
241      * Time it takes to start back preview surface animation after a back gesture starts.
242      */
243     public static final int ACTION_BACK_SYSTEM_ANIMATION = 25;
244 
245     /**
246      * Time notifications spent in hidden state for performance reasons. We might temporary
247      * hide notifications after display size changes (e.g. fold/unfold of a foldable device)
248      * and measure them while they are hidden to unblock rendering of the rest of the UI.
249      */
250     public static final int ACTION_NOTIFICATIONS_HIDDEN_FOR_MEASURE = 26;
251 
252     /**
253      * The same as {@link ACTION_NOTIFICATIONS_HIDDEN_FOR_MEASURE} but tracks time only
254      * when the notifications are hidden and when the shade is open or keyguard is visible.
255      */
256     public static final int ACTION_NOTIFICATIONS_HIDDEN_FOR_MEASURE_WITH_SHADE_OPEN = 27;
257 
258     /**
259      * Time it takes to unlock the device via face,
260      * until either the launcher or the foreground app appears.
261      */
262     public static final int ACTION_KEYGUARD_FACE_UNLOCK_TO_HOME = 28;
263 
264     /**
265      * Time it takes for the shade window to move display after a user interaction.
266      * <p>
267      * This starts when the user does an interaction that triggers the window reparenting, and
268      * finishes after the first doFrame done with the new display configuration.
269      */
270     public static final int ACTION_SHADE_WINDOW_DISPLAY_CHANGE = 29;
271 
272     /**
273      * Time it takes for the "enter desktop" mode animation to begin when initiated by dragging the
274      * app's handle into the desktop drop zone.
275      * <p>
276      * This measure the time from when the user releases their finger in the drop zone to when the
277      * animation for entering desktop mode visually begins. During this period, the home task and
278      * app headers for each window are initialized. Both have historically been expensive. See
279      * b/381396057 and b/360452034 respectively.
280      */
281     public static final int ACTION_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG = 30;
282 
283     /**
284      * Time it takes for the "enter desktop" mode animation to begin when initiated via the app
285      * handle's menu.
286      * <p>
287      * This measures the time from when the menu option is clicked/tapped to when the animation for
288      * entering desktop mode visually begins. During this period, the home task and app headers for
289      * each window are initialized. Both have historically been expensive. See b/381396057 and
290      * b/360452034 respectively.
291      */
292     public static final int ACTION_DESKTOP_MODE_ENTER_APP_HANDLE_MENU = 31;
293 
294     /**
295      * Time it takes for the "exit desktop" mode animation to begin after the user provides input.
296      * <p>
297      * Starts when the user provides input to exit desktop mode and enter full screen mode for an
298      * app. This including selecting the full screen button in an app handle's menu, dragging an
299      * app's window handle to the top of the screen, and using the appropriate keyboard shortcut.
300      * Ends when the animation to exit desktop mode begins.
301      */
302     public static final int ACTION_DESKTOP_MODE_EXIT_MODE = 32;
303 
304     private static final int[] ACTIONS_ALL = {
305         ACTION_EXPAND_PANEL,
306         ACTION_TOGGLE_RECENTS,
307         ACTION_FINGERPRINT_WAKE_AND_UNLOCK,
308         ACTION_CHECK_CREDENTIAL,
309         ACTION_CHECK_CREDENTIAL_UNLOCKED,
310         ACTION_TURN_ON_SCREEN,
311         ACTION_ROTATE_SCREEN,
312         ACTION_FACE_WAKE_AND_UNLOCK,
313         ACTION_START_RECENTS_ANIMATION,
314         ACTION_ROTATE_SCREEN_CAMERA_CHECK,
315         ACTION_ROTATE_SCREEN_SENSOR,
316         ACTION_LOCKSCREEN_UNLOCK,
317         ACTION_USER_SWITCH,
318         ACTION_SWITCH_DISPLAY_UNFOLD,
319         ACTION_UDFPS_ILLUMINATE,
320         ACTION_SHOW_BACK_ARROW,
321         ACTION_LOAD_SHARE_SHEET,
322         ACTION_SHOW_SELECTION_TOOLBAR,
323         ACTION_FOLD_TO_AOD,
324         ACTION_SHOW_VOICE_INTERACTION,
325         ACTION_REQUEST_IME_SHOWN,
326         ACTION_REQUEST_IME_HIDDEN,
327         ACTION_SMARTSPACE_DOORBELL,
328         ACTION_NOTIFICATION_BIG_PICTURE_LOADED,
329         ACTION_KEYGUARD_FPS_UNLOCK_TO_HOME,
330         ACTION_BACK_SYSTEM_ANIMATION,
331         ACTION_NOTIFICATIONS_HIDDEN_FOR_MEASURE,
332         ACTION_NOTIFICATIONS_HIDDEN_FOR_MEASURE_WITH_SHADE_OPEN,
333         ACTION_KEYGUARD_FACE_UNLOCK_TO_HOME,
334         ACTION_SHADE_WINDOW_DISPLAY_CHANGE,
335         ACTION_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG,
336         ACTION_DESKTOP_MODE_ENTER_APP_HANDLE_MENU,
337         ACTION_DESKTOP_MODE_EXIT_MODE,
338     };
339 
340     /** @hide */
341     @IntDef({
342         ACTION_EXPAND_PANEL,
343         ACTION_TOGGLE_RECENTS,
344         ACTION_FINGERPRINT_WAKE_AND_UNLOCK,
345         ACTION_CHECK_CREDENTIAL,
346         ACTION_CHECK_CREDENTIAL_UNLOCKED,
347         ACTION_TURN_ON_SCREEN,
348         ACTION_ROTATE_SCREEN,
349         ACTION_FACE_WAKE_AND_UNLOCK,
350         ACTION_START_RECENTS_ANIMATION,
351         ACTION_ROTATE_SCREEN_CAMERA_CHECK,
352         ACTION_ROTATE_SCREEN_SENSOR,
353         ACTION_LOCKSCREEN_UNLOCK,
354         ACTION_USER_SWITCH,
355         ACTION_SWITCH_DISPLAY_UNFOLD,
356         ACTION_UDFPS_ILLUMINATE,
357         ACTION_SHOW_BACK_ARROW,
358         ACTION_LOAD_SHARE_SHEET,
359         ACTION_SHOW_SELECTION_TOOLBAR,
360         ACTION_FOLD_TO_AOD,
361         ACTION_SHOW_VOICE_INTERACTION,
362         ACTION_REQUEST_IME_SHOWN,
363         ACTION_REQUEST_IME_HIDDEN,
364         ACTION_SMARTSPACE_DOORBELL,
365         ACTION_NOTIFICATION_BIG_PICTURE_LOADED,
366         ACTION_KEYGUARD_FPS_UNLOCK_TO_HOME,
367         ACTION_BACK_SYSTEM_ANIMATION,
368         ACTION_NOTIFICATIONS_HIDDEN_FOR_MEASURE,
369         ACTION_NOTIFICATIONS_HIDDEN_FOR_MEASURE_WITH_SHADE_OPEN,
370         ACTION_KEYGUARD_FACE_UNLOCK_TO_HOME,
371         ACTION_SHADE_WINDOW_DISPLAY_CHANGE,
372         ACTION_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG,
373         ACTION_DESKTOP_MODE_ENTER_APP_HANDLE_MENU,
374         ACTION_DESKTOP_MODE_EXIT_MODE,
375     })
376     @Retention(RetentionPolicy.SOURCE)
377     public @interface Action {}
378 
379     @VisibleForTesting
380     public static final int[] STATSD_ACTION = new int[] {
381             UIACTION_LATENCY_REPORTED__ACTION__ACTION_EXPAND_PANEL,
382             UIACTION_LATENCY_REPORTED__ACTION__ACTION_TOGGLE_RECENTS,
383             UIACTION_LATENCY_REPORTED__ACTION__ACTION_FINGERPRINT_WAKE_AND_UNLOCK,
384             UIACTION_LATENCY_REPORTED__ACTION__ACTION_CHECK_CREDENTIAL,
385             UIACTION_LATENCY_REPORTED__ACTION__ACTION_CHECK_CREDENTIAL_UNLOCKED,
386             UIACTION_LATENCY_REPORTED__ACTION__ACTION_TURN_ON_SCREEN,
387             UIACTION_LATENCY_REPORTED__ACTION__ACTION_ROTATE_SCREEN,
388             UIACTION_LATENCY_REPORTED__ACTION__ACTION_FACE_WAKE_AND_UNLOCK,
389             UIACTION_LATENCY_REPORTED__ACTION__ACTION_START_RECENTS_ANIMATION,
390             UIACTION_LATENCY_REPORTED__ACTION__ACTION_ROTATE_SCREEN_CAMERA_CHECK,
391             UIACTION_LATENCY_REPORTED__ACTION__ACTION_ROTATE_SCREEN_SENSOR,
392             UIACTION_LATENCY_REPORTED__ACTION__ACTION_LOCKSCREEN_UNLOCK,
393             UIACTION_LATENCY_REPORTED__ACTION__ACTION_USER_SWITCH,
394             UIACTION_LATENCY_REPORTED__ACTION__ACTION_SWITCH_DISPLAY_UNFOLD,
395             UIACTION_LATENCY_REPORTED__ACTION__ACTION_UDFPS_ILLUMINATE,
396             UIACTION_LATENCY_REPORTED__ACTION__ACTION_SHOW_BACK_ARROW,
397             UIACTION_LATENCY_REPORTED__ACTION__ACTION_LOAD_SHARE_SHEET,
398             UIACTION_LATENCY_REPORTED__ACTION__ACTION_SHOW_SELECTION_TOOLBAR,
399             UIACTION_LATENCY_REPORTED__ACTION__ACTION_FOLD_TO_AOD,
400             UIACTION_LATENCY_REPORTED__ACTION__ACTION_SHOW_VOICE_INTERACTION,
401             UIACTION_LATENCY_REPORTED__ACTION__ACTION_REQUEST_IME_SHOWN,
402             UIACTION_LATENCY_REPORTED__ACTION__ACTION_REQUEST_IME_HIDDEN,
403             UIACTION_LATENCY_REPORTED__ACTION__ACTION_SMARTSPACE_DOORBELL,
404             UIACTION_LATENCY_REPORTED__ACTION__ACTION_NOTIFICATION_BIG_PICTURE_LOADED,
405             UIACTION_LATENCY_REPORTED__ACTION__ACTION_KEYGUARD_FPS_UNLOCK_TO_HOME,
406             UIACTION_LATENCY_REPORTED__ACTION__ACTION_BACK_SYSTEM_ANIMATION,
407             UIACTION_LATENCY_REPORTED__ACTION__ACTION_NOTIFICATIONS_HIDDEN_FOR_MEASURE,
408             UIACTION_LATENCY_REPORTED__ACTION__ACTION_NOTIFICATIONS_HIDDEN_FOR_MEASURE_WITH_SHADE_OPEN,
409             UIACTION_LATENCY_REPORTED__ACTION__ACTION_KEYGUARD_FACE_UNLOCK_TO_HOME,
410             UIACTION_LATENCY_REPORTED__ACTION__ACTION_SHADE_WINDOW_DISPLAY_CHANGE,
411             UIACTION_LATENCY_REPORTED__ACTION__ACTION_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG,
412             UIACTION_LATENCY_REPORTED__ACTION__ACTION_DESKTOP_MODE_ENTER_APP_HANDLE_MENU,
413             UIACTION_LATENCY_REPORTED__ACTION__ACTION_DESKTOP_MODE_EXIT_MODE,
414     };
415 
416     private final Object mLock = new Object();
417     @GuardedBy("mLock")
418     private final SparseArray<Session> mSessions = new SparseArray<>();
419     @GuardedBy("mLock")
420     private final SparseArray<ActionProperties> mActionPropertiesMap = new SparseArray<>();
421     @GuardedBy("mLock")
422     private boolean mEnabled;
423     private final DeviceConfig.OnPropertiesChangedListener mOnPropertiesChangedListener =
424             this::updateProperties;
425 
426     // Wrapping this in a holder class achieves lazy loading behavior
427     private static final class SLatencyTrackerHolder {
428         private static final LatencyTracker sLatencyTracker;
429 
430         static {
431             sLatencyTracker = new LatencyTracker();
sLatencyTracker.startListeningForLatencyTrackerConfigChanges()432             sLatencyTracker.startListeningForLatencyTrackerConfigChanges();
433         }
434     }
435 
getInstance(Context context)436     public static LatencyTracker getInstance(Context context) {
437         return SLatencyTrackerHolder.sLatencyTracker;
438     }
439 
440     /**
441      * Constructor for LatencyTracker
442      *
443      * <p>This constructor is only visible for test classes to inject their own consumer callbacks
444      *
445      * @param startListeningForPropertyChanges If set, constructor will register for device config
446      *                      property updates prior to returning. If not set,
447      *                      {@link #startListeningForLatencyTrackerConfigChanges} must be called
448      *                      to start listening.
449      */
450     @RequiresPermission(Manifest.permission.READ_DEVICE_CONFIG)
451     @VisibleForTesting
LatencyTracker()452     public LatencyTracker() {
453         mEnabled = DEFAULT_ENABLED;
454     }
455 
updateProperties(DeviceConfig.Properties properties)456     private void updateProperties(DeviceConfig.Properties properties) {
457         synchronized (mLock) {
458             int samplingInterval = properties.getInt(SETTINGS_SAMPLING_INTERVAL_KEY,
459                     DEFAULT_SAMPLING_INTERVAL);
460             boolean wasEnabled = mEnabled;
461             mEnabled = properties.getBoolean(SETTINGS_ENABLED_KEY, DEFAULT_ENABLED);
462             if (wasEnabled != mEnabled) {
463                 Log.d(TAG, "Latency tracker " + (mEnabled ? "enabled" : "disabled") + ".");
464             }
465             for (int action : ACTIONS_ALL) {
466                 String actionName = getNameOfAction(STATSD_ACTION[action]).toLowerCase(Locale.ROOT);
467                 int legacyActionTraceThreshold = properties.getInt(
468                         actionName + LEGACY_TRACE_THRESHOLD_SUFFIX, -1);
469                 mActionPropertiesMap.put(action, new ActionProperties(action,
470                         properties.getBoolean(actionName + ENABLE_SUFFIX, mEnabled),
471                         properties.getInt(actionName + SAMPLE_INTERVAL_SUFFIX, samplingInterval),
472                         properties.getInt(actionName + TRACE_THRESHOLD_SUFFIX,
473                                 legacyActionTraceThreshold)));
474             }
475             onDeviceConfigPropertiesUpdated(mActionPropertiesMap);
476         }
477     }
478 
479     /**
480      * Test method to start listening to {@link DeviceConfig} properties changes.
481      *
482      * <p>During testing, a {@link LatencyTracker} it is desired to stop and start listening for
483      * config updates.
484      *
485      * <p>This is not used for production usages of this class outside of testing as we are
486      * using a single static object.
487      */
488     @VisibleForTesting
489     @RequiresPermission(Manifest.permission.READ_DEVICE_CONFIG)
startListeningForLatencyTrackerConfigChanges()490     public void startListeningForLatencyTrackerConfigChanges() {
491         final Context context = ActivityThread.currentApplication();
492         if (context == null) {
493             Log.e(
494                     TAG,
495                     String.format(
496                             "No application for package: %s. Latency Tracker Disabled",
497                             ActivityThread.currentPackageName()));
498             return;
499         }
500         if (context.checkCallingOrSelfPermission(READ_DEVICE_CONFIG) != PERMISSION_GRANTED) {
501             if (DEBUG) {
502                 synchronized (mLock) {
503                     Log.d(TAG, "Initialized the LatencyTracker."
504                             + " (No READ_DEVICE_CONFIG permission to change configs)"
505                             + " enabled=" + mEnabled + ", package=" + context.getPackageName());
506                 }
507             }
508             return;
509         }
510 
511         // Post initialization to the background in case we're running on the main thread.
512         BackgroundThread.getHandler().post(() -> {
513             try {
514                 this.updateProperties(
515                         DeviceConfig.getProperties(NAMESPACE_LATENCY_TRACKER));
516                 DeviceConfig.addOnPropertiesChangedListener(NAMESPACE_LATENCY_TRACKER,
517                         BackgroundThread.getExecutor(), mOnPropertiesChangedListener);
518             } catch (SecurityException ex) {
519                 // In case of running tests that the main thread passes the check,
520                 // but the background thread doesn't have necessary permissions.
521                 // Swallow it since it's ok to ignore device config changes in the tests.
522                 Log.d(TAG, "Can't get properties: READ_DEVICE_CONFIG granted="
523                         + context.checkCallingOrSelfPermission(READ_DEVICE_CONFIG)
524                         + ", package=" + context.getPackageName());
525             }
526         });
527     }
528 
529     /**
530      * Test method to stop listening to {@link DeviceConfig} properties changes.
531      *
532      * <p>During testing, a {@link LatencyTracker} it is desired to stop and start listening for
533      * config updates.
534      *
535      * <p>This is not used for production usages of this class outside of testing as we are
536      * using a single static object.
537      */
538     @VisibleForTesting
stopListeningForLatencyTrackerConfigChanges()539     public void stopListeningForLatencyTrackerConfigChanges() {
540         DeviceConfig.removeOnPropertiesChangedListener(mOnPropertiesChangedListener);
541     }
542 
543     /**
544      * A helper method to translate action type to name.
545      *
546      * @param atomsProtoAction the action type defined in AtomsProto.java
547      * @return the name of the action
548      */
getNameOfAction(int atomsProtoAction)549     public static String getNameOfAction(int atomsProtoAction) {
550         // Defined in AtomsProto.java
551         switch (atomsProtoAction) {
552             case UIACTION_LATENCY_REPORTED__ACTION__UNKNOWN_ACTION:
553                 return "UNKNOWN";
554             case UIACTION_LATENCY_REPORTED__ACTION__ACTION_EXPAND_PANEL:
555                 return "ACTION_EXPAND_PANEL";
556             case UIACTION_LATENCY_REPORTED__ACTION__ACTION_TOGGLE_RECENTS:
557                 return "ACTION_TOGGLE_RECENTS";
558             case UIACTION_LATENCY_REPORTED__ACTION__ACTION_FINGERPRINT_WAKE_AND_UNLOCK:
559                 return "ACTION_FINGERPRINT_WAKE_AND_UNLOCK";
560             case UIACTION_LATENCY_REPORTED__ACTION__ACTION_CHECK_CREDENTIAL:
561                 return "ACTION_CHECK_CREDENTIAL";
562             case UIACTION_LATENCY_REPORTED__ACTION__ACTION_CHECK_CREDENTIAL_UNLOCKED:
563                 return "ACTION_CHECK_CREDENTIAL_UNLOCKED";
564             case UIACTION_LATENCY_REPORTED__ACTION__ACTION_TURN_ON_SCREEN:
565                 return "ACTION_TURN_ON_SCREEN";
566             case UIACTION_LATENCY_REPORTED__ACTION__ACTION_ROTATE_SCREEN:
567                 return "ACTION_ROTATE_SCREEN";
568             case UIACTION_LATENCY_REPORTED__ACTION__ACTION_FACE_WAKE_AND_UNLOCK:
569                 return "ACTION_FACE_WAKE_AND_UNLOCK";
570             case UIACTION_LATENCY_REPORTED__ACTION__ACTION_START_RECENTS_ANIMATION:
571                 return "ACTION_START_RECENTS_ANIMATION";
572             case UIACTION_LATENCY_REPORTED__ACTION__ACTION_ROTATE_SCREEN_CAMERA_CHECK:
573                 return "ACTION_ROTATE_SCREEN_CAMERA_CHECK";
574             case UIACTION_LATENCY_REPORTED__ACTION__ACTION_ROTATE_SCREEN_SENSOR:
575                 return "ACTION_ROTATE_SCREEN_SENSOR";
576             case UIACTION_LATENCY_REPORTED__ACTION__ACTION_LOCKSCREEN_UNLOCK:
577                 return "ACTION_LOCKSCREEN_UNLOCK";
578             case UIACTION_LATENCY_REPORTED__ACTION__ACTION_USER_SWITCH:
579                 return "ACTION_USER_SWITCH";
580             case UIACTION_LATENCY_REPORTED__ACTION__ACTION_SWITCH_DISPLAY_UNFOLD:
581                 return "ACTION_SWITCH_DISPLAY_UNFOLD";
582             case UIACTION_LATENCY_REPORTED__ACTION__ACTION_UDFPS_ILLUMINATE:
583                 return "ACTION_UDFPS_ILLUMINATE";
584             case UIACTION_LATENCY_REPORTED__ACTION__ACTION_SHOW_BACK_ARROW:
585                 return "ACTION_SHOW_BACK_ARROW";
586             case UIACTION_LATENCY_REPORTED__ACTION__ACTION_LOAD_SHARE_SHEET:
587                 return "ACTION_LOAD_SHARE_SHEET";
588             case UIACTION_LATENCY_REPORTED__ACTION__ACTION_SHOW_SELECTION_TOOLBAR:
589                 return "ACTION_SHOW_SELECTION_TOOLBAR";
590             case UIACTION_LATENCY_REPORTED__ACTION__ACTION_FOLD_TO_AOD:
591                 return "ACTION_FOLD_TO_AOD";
592             case UIACTION_LATENCY_REPORTED__ACTION__ACTION_SHOW_VOICE_INTERACTION:
593                 return "ACTION_SHOW_VOICE_INTERACTION";
594             case UIACTION_LATENCY_REPORTED__ACTION__ACTION_REQUEST_IME_SHOWN:
595                 return "ACTION_REQUEST_IME_SHOWN";
596             case UIACTION_LATENCY_REPORTED__ACTION__ACTION_REQUEST_IME_HIDDEN:
597                 return "ACTION_REQUEST_IME_HIDDEN";
598             case UIACTION_LATENCY_REPORTED__ACTION__ACTION_SMARTSPACE_DOORBELL:
599                 return "ACTION_SMARTSPACE_DOORBELL";
600             case UIACTION_LATENCY_REPORTED__ACTION__ACTION_NOTIFICATION_BIG_PICTURE_LOADED:
601                 return "ACTION_NOTIFICATION_BIG_PICTURE_LOADED";
602             case UIACTION_LATENCY_REPORTED__ACTION__ACTION_KEYGUARD_FPS_UNLOCK_TO_HOME:
603                 return "ACTION_KEYGUARD_FPS_UNLOCK_TO_HOME";
604             case UIACTION_LATENCY_REPORTED__ACTION__ACTION_BACK_SYSTEM_ANIMATION:
605                 return "ACTION_BACK_SYSTEM_ANIMATION";
606             case UIACTION_LATENCY_REPORTED__ACTION__ACTION_NOTIFICATIONS_HIDDEN_FOR_MEASURE:
607                 return "ACTION_NOTIFICATIONS_HIDDEN_FOR_MEASURE";
608             case UIACTION_LATENCY_REPORTED__ACTION__ACTION_NOTIFICATIONS_HIDDEN_FOR_MEASURE_WITH_SHADE_OPEN:
609                 return "ACTION_NOTIFICATIONS_HIDDEN_FOR_MEASURE_WITH_SHADE_OPEN";
610             case UIACTION_LATENCY_REPORTED__ACTION__ACTION_KEYGUARD_FACE_UNLOCK_TO_HOME:
611                 return "ACTION_KEYGUARD_FACE_UNLOCK_TO_HOME";
612             case UIACTION_LATENCY_REPORTED__ACTION__ACTION_SHADE_WINDOW_DISPLAY_CHANGE:
613                 return "ACTION_SHADE_WINDOW_DISPLAY_CHANGE";
614             case UIACTION_LATENCY_REPORTED__ACTION__ACTION_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG:
615                 return "ACTION_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG";
616             case UIACTION_LATENCY_REPORTED__ACTION__ACTION_DESKTOP_MODE_ENTER_APP_HANDLE_MENU:
617                 return "ACTION_DESKTOP_MODE_ENTER_APP_HANDLE_MENU";
618             case UIACTION_LATENCY_REPORTED__ACTION__ACTION_DESKTOP_MODE_EXIT_MODE:
619                 return "ACTION_DESKTOP_MODE_EXIT_MODE";
620             default:
621                 throw new IllegalArgumentException("Invalid action");
622         }
623     }
624 
getTraceNameOfAction(@ction int action, String tag)625     private static String getTraceNameOfAction(@Action int action, String tag) {
626         if (TextUtils.isEmpty(tag)) {
627             return "L<" + getNameOfAction(STATSD_ACTION[action]) + ">";
628         } else {
629             return "L<" + getNameOfAction(STATSD_ACTION[action]) + "::" + tag + ">";
630         }
631     }
632 
getTraceTriggerNameForAction(@ction int action)633     private static String getTraceTriggerNameForAction(@Action int action) {
634         return "com.android.telemetry.latency-tracker-" + getNameOfAction(STATSD_ACTION[action]);
635     }
636 
637     /**
638      * @deprecated Use {@link #isEnabled(Context, int)}
639      */
640     @Deprecated
isEnabled(Context ctx)641     public static boolean isEnabled(Context ctx) {
642         return getInstance(ctx).isEnabled();
643     }
644 
645     /**
646      * @deprecated Used {@link #isEnabled(int)}
647      */
648     @Deprecated
isEnabled()649     public boolean isEnabled() {
650         synchronized (mLock) {
651             return mEnabled;
652         }
653     }
654 
isEnabled(Context ctx, int action)655     public static boolean isEnabled(Context ctx, int action) {
656         return getInstance(ctx).isEnabled(action);
657     }
658 
isEnabled(int action)659     public boolean isEnabled(int action) {
660         synchronized (mLock) {
661             ActionProperties actionProperties = mActionPropertiesMap.get(action);
662             if (actionProperties != null) {
663                 return actionProperties.isEnabled();
664             }
665             return false;
666         }
667     }
668 
669     /**
670      * Notifies that an action is starting. <s>This needs to be called from the main thread.</s>
671      *
672      * @param action The action to start. One of the ACTION_* values.
673      */
onActionStart(@ction int action)674     public void onActionStart(@Action int action) {
675         onActionStart(action, null);
676     }
677 
678     /**
679      * Notifies that an action is starting. <s>This needs to be called from the main thread.</s>
680      *
681      * @param action The action to start. One of the ACTION_* values.
682      * @param tag The brief description of the action.
683      */
onActionStart(@ction int action, String tag)684     public void onActionStart(@Action int action, String tag) {
685         synchronized (mLock) {
686             if (!isEnabled(action)) {
687                 return;
688             }
689             // skip if the action is already instrumenting.
690             if (mSessions.get(action) != null) {
691                 return;
692             }
693             Session session = new Session(action, tag);
694             session.begin(() -> onActionCancel(action));
695             mSessions.put(action, session);
696 
697             if (DEBUG) {
698                 Log.d(TAG, "onActionStart: " + session.name() + ", start=" + session.mStartRtc);
699             }
700         }
701     }
702 
703     /**
704      * Notifies that an action has ended. <s>This needs to be called from the main thread.</s>
705      *
706      * @param action The action to end. One of the ACTION_* values.
707      */
onActionEnd(@ction int action)708     public void onActionEnd(@Action int action) {
709         synchronized (mLock) {
710             if (!isEnabled(action)) {
711                 return;
712             }
713             Session session = mSessions.get(action);
714             if (session == null) {
715                 return;
716             }
717             session.end();
718             mSessions.delete(action);
719             logAction(action, session.duration());
720 
721             if (DEBUG) {
722                 Log.d(TAG, "onActionEnd:" + session.name() + ", duration=" + session.duration());
723             }
724         }
725     }
726 
727     /**
728      * Notifies that an action has canceled. <s>This needs to be called from the main thread.</s>
729      *
730      * @param action The action to cancel. One of the ACTION_* values.
731      * @hide
732      */
onActionCancel(@ction int action)733     public void onActionCancel(@Action int action) {
734         synchronized (mLock) {
735             Session session = mSessions.get(action);
736             if (session == null) {
737                 return;
738             }
739             session.cancel();
740             mSessions.delete(action);
741 
742             if (DEBUG) {
743                 Log.d(TAG, "onActionCancel: " + session.name());
744             }
745         }
746     }
747 
748     /**
749      * Testing API to get the time when a given action was started.
750      *
751      * @param action Action which to retrieve start time from
752      * @return Elapsed realtime timestamp when the action started. -1 if the action is not active.
753      * @hide
754      */
755     @VisibleForTesting
756     @ElapsedRealtimeLong
getActiveActionStartTime(@ction int action)757     public long getActiveActionStartTime(@Action int action) {
758         synchronized (mLock) {
759             if (mSessions.contains(action)) {
760                 return mSessions.get(action).mStartRtc;
761             }
762             return -1;
763         }
764     }
765 
766     /**
767      * Logs an action that has started and ended. This needs to be called from the main thread.
768      *
769      * @param action   The action to end. One of the ACTION_* values.
770      * @param duration The duration of the action in ms.
771      */
logAction(@ction int action, int duration)772     public void logAction(@Action int action, int duration) {
773         boolean shouldSample;
774         int traceThreshold;
775         synchronized (mLock) {
776             if (!isEnabled(action)) {
777                 return;
778             }
779             ActionProperties actionProperties = mActionPropertiesMap.get(action);
780             if (actionProperties == null) {
781                 return;
782             }
783             int nextRandNum = ThreadLocalRandom.current().nextInt(
784                     actionProperties.getSamplingInterval());
785             shouldSample = nextRandNum == 0;
786             traceThreshold = actionProperties.getTraceThreshold();
787         }
788 
789         boolean shouldTriggerPerfettoTrace = traceThreshold > 0 && duration >= traceThreshold;
790 
791         if (DEBUG) {
792             Log.i(TAG, "logAction: " + getNameOfAction(STATSD_ACTION[action])
793                     + " duration=" + duration
794                     + " shouldSample=" + shouldSample
795                     + " shouldTriggerPerfettoTrace=" + shouldTriggerPerfettoTrace);
796         }
797 
798         EventLog.writeEvent(EventLogTags.SYSUI_LATENCY, action, duration);
799         if (shouldTriggerPerfettoTrace) {
800             onTriggerPerfetto(getTraceTriggerNameForAction(action));
801         }
802         if (shouldSample) {
803             onLogToFrameworkStats(
804                     new FrameworkStatsLogEvent(action, FrameworkStatsLog.UI_ACTION_LATENCY_REPORTED,
805                             STATSD_ACTION[action], duration)
806             );
807         }
808     }
809 
810     static class Session {
811         @Action
812         private final int mAction;
813         private final String mTag;
814         private final String mName;
815         private Runnable mTimeoutRunnable;
816         private long mStartRtc = -1;
817         private long mEndRtc = -1;
818 
Session(@ction int action, @Nullable String tag)819         Session(@Action int action, @Nullable String tag) {
820             mAction = action;
821             mTag = tag;
822             mName = TextUtils.isEmpty(mTag)
823                     ? getNameOfAction(STATSD_ACTION[mAction])
824                     : getNameOfAction(STATSD_ACTION[mAction]) + "::" + mTag;
825         }
826 
name()827         String name() {
828             return mName;
829         }
830 
traceName()831         String traceName() {
832             return getTraceNameOfAction(mAction, mTag);
833         }
834 
begin(@onNull Runnable timeoutAction)835         void begin(@NonNull Runnable timeoutAction) {
836             mStartRtc = SystemClock.elapsedRealtime();
837             Trace.asyncTraceForTrackBegin(TRACE_TAG_APP, traceName(), traceName(), 0);
838 
839             // start counting timeout.
840             mTimeoutRunnable = () -> {
841                 Trace.instantForTrack(TRACE_TAG_APP, traceName(), "timeout");
842                 timeoutAction.run();
843             };
844             BackgroundThread.getHandler()
845                     .postDelayed(mTimeoutRunnable, TimeUnit.SECONDS.toMillis(15));
846         }
847 
end()848         void end() {
849             mEndRtc = SystemClock.elapsedRealtime();
850             Trace.asyncTraceForTrackEnd(TRACE_TAG_APP, traceName(), 0);
851             BackgroundThread.getHandler().removeCallbacks(mTimeoutRunnable);
852             mTimeoutRunnable = null;
853         }
854 
cancel()855         void cancel() {
856             Trace.instantForTrack(TRACE_TAG_APP, traceName(), "cancel");
857             Trace.asyncTraceForTrackEnd(TRACE_TAG_APP, traceName(), 0);
858             BackgroundThread.getHandler().removeCallbacks(mTimeoutRunnable);
859             mTimeoutRunnable = null;
860         }
861 
duration()862         int duration() {
863             return (int) (mEndRtc - mStartRtc);
864         }
865     }
866 
867     @VisibleForTesting
868     public static class ActionProperties {
869         static final String ENABLE_SUFFIX = "_enable";
870         static final String SAMPLE_INTERVAL_SUFFIX = "_sample_interval";
871         // TODO: migrate all usages of the legacy trace threshold property
872         static final String LEGACY_TRACE_THRESHOLD_SUFFIX = "";
873         static final String TRACE_THRESHOLD_SUFFIX = "_trace_threshold";
874 
875         @Action
876         private final int mAction;
877         private final boolean mEnabled;
878         private final int mSamplingInterval;
879         private final int mTraceThreshold;
880 
881         @VisibleForTesting
ActionProperties( @ction int action, boolean enabled, int samplingInterval, int traceThreshold)882         public ActionProperties(
883                 @Action int action,
884                 boolean enabled,
885                 int samplingInterval,
886                 int traceThreshold) {
887             this.mAction = action;
888             com.android.internal.util.AnnotationValidations.validate(
889                     Action.class, null, mAction);
890             this.mEnabled = enabled;
891             this.mSamplingInterval = samplingInterval;
892             this.mTraceThreshold = traceThreshold;
893         }
894 
895         @VisibleForTesting
896         @Action
getAction()897         public int getAction() {
898             return mAction;
899         }
900 
901         @VisibleForTesting
isEnabled()902         public boolean isEnabled() {
903             return mEnabled;
904         }
905 
906         @VisibleForTesting
getSamplingInterval()907         public int getSamplingInterval() {
908             return mSamplingInterval;
909         }
910 
911         @VisibleForTesting
getTraceThreshold()912         public int getTraceThreshold() {
913             return mTraceThreshold;
914         }
915 
916         @Override
toString()917         public String toString() {
918             return "ActionProperties{"
919                     + " mAction=" + mAction
920                     + ", mEnabled=" + mEnabled
921                     + ", mSamplingInterval=" + mSamplingInterval
922                     + ", mTraceThreshold=" + mTraceThreshold
923                     + "}";
924         }
925 
926         @Override
equals(@ullable Object o)927         public boolean equals(@Nullable Object o) {
928             if (this == o) {
929                 return true;
930             }
931             if (o == null) {
932                 return false;
933             }
934             if (!(o instanceof ActionProperties)) {
935                 return false;
936             }
937             ActionProperties that = (ActionProperties) o;
938             return mAction == that.mAction
939                     && mEnabled == that.mEnabled
940                     && mSamplingInterval == that.mSamplingInterval
941                     && mTraceThreshold == that.mTraceThreshold;
942         }
943 
944         @Override
hashCode()945         public int hashCode() {
946             int _hash = 1;
947             _hash = 31 * _hash + mAction;
948             _hash = 31 * _hash + Boolean.hashCode(mEnabled);
949             _hash = 31 * _hash + mSamplingInterval;
950             _hash = 31 * _hash + mTraceThreshold;
951             return _hash;
952         }
953     }
954 
955     /**
956      * Testing method intended to be overridden to determine when the LatencyTracker's device
957      * properties are updated.
958      */
959     @VisibleForTesting
onDeviceConfigPropertiesUpdated(SparseArray<ActionProperties> actionProperties)960     public void onDeviceConfigPropertiesUpdated(SparseArray<ActionProperties> actionProperties) {
961         if (DEBUG) {
962             Log.d(TAG, "onDeviceConfigPropertiesUpdated: " + actionProperties);
963         }
964     }
965 
966     /**
967      * Testing class intended to be overridden to determine when LatencyTracker triggers perfetto.
968      */
969     @VisibleForTesting
onTriggerPerfetto(String triggerName)970     public void onTriggerPerfetto(String triggerName) {
971         if (DEBUG) {
972             Log.i(TAG, "onTriggerPerfetto: triggerName=" + triggerName);
973         }
974         PerfettoTrigger.trigger(triggerName);
975     }
976 
977     /**
978      * Testing method intended to be overridden to determine when LatencyTracker writes to
979      * FrameworkStatsLog.
980      */
981     @VisibleForTesting
onLogToFrameworkStats(FrameworkStatsLogEvent event)982     public void onLogToFrameworkStats(FrameworkStatsLogEvent event) {
983         if (DEBUG) {
984             Log.i(TAG, "onLogToFrameworkStats: event=" + event);
985         }
986         FrameworkStatsLog.write(event.logCode, event.statsdAction, event.durationMillis);
987     }
988 
989     /**
990      * Testing class intended to reject what should be written to the {@link FrameworkStatsLog}
991      *
992      * <p>This class is used in {@link #onLogToFrameworkStats(FrameworkStatsLogEvent)} for test code
993      * to observer when and what information is being logged by {@link LatencyTracker}
994      */
995     @VisibleForTesting
996     public static class FrameworkStatsLogEvent {
997 
998         @VisibleForTesting
999         public final int action;
1000         @VisibleForTesting
1001         public final int logCode;
1002         @VisibleForTesting
1003         public final int statsdAction;
1004         @VisibleForTesting
1005         public final int durationMillis;
1006 
FrameworkStatsLogEvent(int action, int logCode, int statsdAction, int durationMillis)1007         private FrameworkStatsLogEvent(int action, int logCode, int statsdAction,
1008                 int durationMillis) {
1009             this.action = action;
1010             this.logCode = logCode;
1011             this.statsdAction = statsdAction;
1012             this.durationMillis = durationMillis;
1013         }
1014 
1015         @Override
toString()1016         public String toString() {
1017             return "FrameworkStatsLogEvent{"
1018                     + " logCode=" + logCode
1019                     + ", statsdAction=" + statsdAction
1020                     + ", durationMillis=" + durationMillis
1021                     + "}";
1022         }
1023     }
1024 }
1025