1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.internal.jank;
18 
19 import static android.Manifest.permission.READ_DEVICE_CONFIG;
20 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
21 import static android.provider.DeviceConfig.NAMESPACE_INTERACTION_JANK_MONITOR;
22 
23 import static com.android.internal.jank.FrameTracker.REASON_CANCEL_NORMAL;
24 import static com.android.internal.jank.FrameTracker.REASON_CANCEL_TIMEOUT;
25 import static com.android.internal.jank.FrameTracker.REASON_END_NORMAL;
26 import static com.android.internal.jank.FrameTracker.REASON_END_UNKNOWN;
27 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__BIOMETRIC_PROMPT_TRANSITION;
28 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__IME_INSETS_HIDE_ANIMATION;
29 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__IME_INSETS_SHOW_ANIMATION;
30 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_ALL_APPS_SCROLL;
31 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_CLOSE_TO_HOME;
32 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_CLOSE_TO_HOME_FALLBACK;
33 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_CLOSE_TO_PIP;
34 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_LAUNCH_FROM_ICON;
35 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_LAUNCH_FROM_RECENTS;
36 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_LAUNCH_FROM_WIDGET;
37 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_SWIPE_TO_RECENTS;
38 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_CLOSE_ALL_APPS_SWIPE;
39 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_CLOSE_ALL_APPS_TO_HOME;
40 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_OPEN_ALL_APPS;
41 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_OPEN_SEARCH_RESULT;
42 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_QUICK_SWITCH;
43 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_UNLOCK_ENTRANCE_ANIMATION;
44 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_CLOCK_MOVE_ANIMATION;
45 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_LAUNCH_CAMERA;
46 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_OCCLUSION;
47 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PASSWORD_APPEAR;
48 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PASSWORD_DISAPPEAR;
49 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PATTERN_APPEAR;
50 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PATTERN_DISAPPEAR;
51 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PIN_APPEAR;
52 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PIN_DISAPPEAR;
53 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_TRANSITION_FROM_AOD;
54 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_TRANSITION_TO_AOD;
55 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_UNLOCK_ANIMATION;
56 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__NOTIFICATION_SHADE_SWIPE;
57 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__ONE_HANDED_ENTER_TRANSITION;
58 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__ONE_HANDED_EXIT_TRANSITION;
59 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__PIP_TRANSITION;
60 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__RECENTS_SCROLLING;
61 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SCREEN_OFF;
62 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SCREEN_OFF_SHOW_AOD;
63 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SETTINGS_PAGE_SCROLL;
64 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SETTINGS_SLIDER;
65 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SETTINGS_TOGGLE;
66 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH;
67 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_HISTORY_BUTTON;
68 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_MEDIA_PLAYER;
69 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_QS_TILE;
70 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON;
71 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_CLEAR_ALL;
72 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_DIALOG_OPEN;
73 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_EXPAND_FROM_STATUS_BAR;
74 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_HEADS_UP_APPEAR;
75 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_HEADS_UP_DISAPPEAR;
76 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_NOTIFICATION_ADD;
77 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_NOTIFICATION_REMOVE;
78 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_QS_EXPAND_COLLAPSE;
79 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_QS_SCROLL_SWIPE;
80 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_ROW_EXPAND;
81 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_ROW_SWIPE;
82 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_SCROLL_FLING;
83 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SPLASHSCREEN_AVD;
84 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SPLASHSCREEN_EXIT_ANIM;
85 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SPLIT_SCREEN_DOUBLE_TAP_DIVIDER;
86 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SPLIT_SCREEN_ENTER;
87 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SPLIT_SCREEN_EXIT;
88 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SPLIT_SCREEN_RESIZE;
89 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP;
90 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SUW_LOADING_SCREEN_FOR_STATUS;
91 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SUW_LOADING_TO_NEXT_FLOW;
92 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SUW_LOADING_TO_SHOW_INFO_WITH_ACTIONS;
93 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SUW_SHOW_FUNCTION_SCREEN_WITH_ACTIONS;
94 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__TAKE_SCREENSHOT;
95 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__TASKBAR_COLLAPSE;
96 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__TASKBAR_EXPAND;
97 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__UNFOLD_ANIM;
98 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__USER_DIALOG_OPEN;
99 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__USER_SWITCH;
100 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__VOLUME_CONTROL;
101 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__WALLPAPER_TRANSITION;
102 
103 import android.Manifest;
104 import android.annotation.ColorInt;
105 import android.annotation.IntDef;
106 import android.annotation.NonNull;
107 import android.annotation.RequiresPermission;
108 import android.annotation.UiThread;
109 import android.annotation.WorkerThread;
110 import android.app.ActivityThread;
111 import android.content.Context;
112 import android.graphics.Color;
113 import android.os.Build;
114 import android.os.Handler;
115 import android.os.HandlerExecutor;
116 import android.os.HandlerThread;
117 import android.os.SystemClock;
118 import android.provider.DeviceConfig;
119 import android.text.TextUtils;
120 import android.util.Log;
121 import android.util.SparseArray;
122 import android.view.Choreographer;
123 import android.view.SurfaceControl;
124 import android.view.View;
125 
126 import com.android.internal.annotations.GuardedBy;
127 import com.android.internal.annotations.VisibleForTesting;
128 import com.android.internal.jank.FrameTracker.ChoreographerWrapper;
129 import com.android.internal.jank.FrameTracker.FrameMetricsWrapper;
130 import com.android.internal.jank.FrameTracker.FrameTrackerListener;
131 import com.android.internal.jank.FrameTracker.Reasons;
132 import com.android.internal.jank.FrameTracker.SurfaceControlWrapper;
133 import com.android.internal.jank.FrameTracker.ThreadedRendererWrapper;
134 import com.android.internal.jank.FrameTracker.ViewRootWrapper;
135 import com.android.internal.util.PerfettoTrigger;
136 
137 import java.lang.annotation.Retention;
138 import java.lang.annotation.RetentionPolicy;
139 import java.time.Instant;
140 import java.util.Locale;
141 import java.util.concurrent.ThreadLocalRandom;
142 import java.util.concurrent.TimeUnit;
143 
144 /**
145  * This class let users to begin and end the always on tracing mechanism.
146  *
147  * Enabling for local development:
148  *
149  * adb shell device_config put interaction_jank_monitor enabled true
150  * adb shell device_config put interaction_jank_monitor sampling_interval 1
151  *
152  * On debuggable builds, an overlay can be used to display the name of the
153  * currently running cuj using:
154  *
155  * adb shell device_config put interaction_jank_monitor debug_overlay_enabled true
156  *
157  * NOTE: The overlay will interfere with metrics, so it should only be used
158  * for understanding which UI events correspeond to which CUJs.
159  *
160  * @hide
161  */
162 public class InteractionJankMonitor {
163     private static final String TAG = InteractionJankMonitor.class.getSimpleName();
164     private static final boolean DEBUG = false;
165     private static final String ACTION_PREFIX = InteractionJankMonitor.class.getCanonicalName();
166 
167     private static final String DEFAULT_WORKER_NAME = TAG + "-Worker";
168     private static final long DEFAULT_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(2L);
169     static final long EXECUTOR_TASK_TIMEOUT = 500;
170     private static final String SETTINGS_ENABLED_KEY = "enabled";
171     private static final String SETTINGS_SAMPLING_INTERVAL_KEY = "sampling_interval";
172     private static final String SETTINGS_THRESHOLD_MISSED_FRAMES_KEY =
173             "trace_threshold_missed_frames";
174     private static final String SETTINGS_THRESHOLD_FRAME_TIME_MILLIS_KEY =
175             "trace_threshold_frame_time_millis";
176     private static final String SETTINGS_DEBUG_OVERLAY_ENABLED_KEY = "debug_overlay_enabled";
177     /** Default to being enabled on debug builds. */
178     private static final boolean DEFAULT_ENABLED = Build.IS_DEBUGGABLE;
179     /** Default to collecting data for all CUJs. */
180     private static final int DEFAULT_SAMPLING_INTERVAL = 1;
181     /** Default to triggering trace if 3 frames are missed OR a frame takes at least 64ms */
182     private static final int DEFAULT_TRACE_THRESHOLD_MISSED_FRAMES = 3;
183     private static final int DEFAULT_TRACE_THRESHOLD_FRAME_TIME_MILLIS = 64;
184     private static final boolean DEFAULT_DEBUG_OVERLAY_ENABLED = false;
185 
186     @VisibleForTesting
187     public static final int MAX_LENGTH_OF_CUJ_NAME = 80;
188     private static final int MAX_LENGTH_SESSION_NAME = 100;
189 
190     public static final String ACTION_SESSION_END = ACTION_PREFIX + ".ACTION_SESSION_END";
191     public static final String ACTION_SESSION_CANCEL = ACTION_PREFIX + ".ACTION_SESSION_CANCEL";
192 
193     // Every value must have a corresponding entry in CUJ_STATSD_INTERACTION_TYPE.
194     public static final int CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE = 0;
195     public static final int CUJ_NOTIFICATION_SHADE_SCROLL_FLING = 2;
196     public static final int CUJ_NOTIFICATION_SHADE_ROW_EXPAND = 3;
197     public static final int CUJ_NOTIFICATION_SHADE_ROW_SWIPE = 4;
198     public static final int CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE = 5;
199     public static final int CUJ_NOTIFICATION_SHADE_QS_SCROLL_SWIPE = 6;
200     public static final int CUJ_LAUNCHER_APP_LAUNCH_FROM_RECENTS = 7;
201     public static final int CUJ_LAUNCHER_APP_LAUNCH_FROM_ICON = 8;
202     public static final int CUJ_LAUNCHER_APP_CLOSE_TO_HOME = 9;
203     public static final int CUJ_LAUNCHER_APP_CLOSE_TO_PIP = 10;
204     public static final int CUJ_LAUNCHER_QUICK_SWITCH = 11;
205     public static final int CUJ_NOTIFICATION_HEADS_UP_APPEAR = 12;
206     public static final int CUJ_NOTIFICATION_HEADS_UP_DISAPPEAR = 13;
207     public static final int CUJ_NOTIFICATION_ADD = 14;
208     public static final int CUJ_NOTIFICATION_REMOVE = 15;
209     public static final int CUJ_NOTIFICATION_APP_START = 16;
210     public static final int CUJ_LOCKSCREEN_PASSWORD_APPEAR = 17;
211     public static final int CUJ_LOCKSCREEN_PATTERN_APPEAR = 18;
212     public static final int CUJ_LOCKSCREEN_PIN_APPEAR = 19;
213     public static final int CUJ_LOCKSCREEN_PASSWORD_DISAPPEAR = 20;
214     public static final int CUJ_LOCKSCREEN_PATTERN_DISAPPEAR = 21;
215     public static final int CUJ_LOCKSCREEN_PIN_DISAPPEAR = 22;
216     public static final int CUJ_LOCKSCREEN_TRANSITION_FROM_AOD = 23;
217     public static final int CUJ_LOCKSCREEN_TRANSITION_TO_AOD = 24;
218     public static final int CUJ_LAUNCHER_OPEN_ALL_APPS = 25;
219     public static final int CUJ_LAUNCHER_ALL_APPS_SCROLL = 26;
220     public static final int CUJ_LAUNCHER_APP_LAUNCH_FROM_WIDGET = 27;
221     public static final int CUJ_SETTINGS_PAGE_SCROLL = 28;
222     public static final int CUJ_LOCKSCREEN_UNLOCK_ANIMATION = 29;
223     public static final int CUJ_SHADE_APP_LAUNCH_FROM_HISTORY_BUTTON = 30;
224     public static final int CUJ_SHADE_APP_LAUNCH_FROM_MEDIA_PLAYER = 31;
225     public static final int CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE = 32;
226     public static final int CUJ_SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON = 33;
227     public static final int CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP = 34;
228     public static final int CUJ_PIP_TRANSITION = 35;
229     public static final int CUJ_WALLPAPER_TRANSITION = 36;
230     public static final int CUJ_USER_SWITCH = 37;
231     public static final int CUJ_SPLASHSCREEN_AVD = 38;
232     public static final int CUJ_SPLASHSCREEN_EXIT_ANIM = 39;
233     public static final int CUJ_SCREEN_OFF = 40;
234     public static final int CUJ_SCREEN_OFF_SHOW_AOD = 41;
235     public static final int CUJ_ONE_HANDED_ENTER_TRANSITION = 42;
236     public static final int CUJ_ONE_HANDED_EXIT_TRANSITION = 43;
237     public static final int CUJ_UNFOLD_ANIM = 44;
238     public static final int CUJ_SUW_LOADING_TO_SHOW_INFO_WITH_ACTIONS = 45;
239     public static final int CUJ_SUW_SHOW_FUNCTION_SCREEN_WITH_ACTIONS = 46;
240     public static final int CUJ_SUW_LOADING_TO_NEXT_FLOW = 47;
241     public static final int CUJ_SUW_LOADING_SCREEN_FOR_STATUS = 48;
242     public static final int CUJ_SPLIT_SCREEN_ENTER = 49;
243     public static final int CUJ_SPLIT_SCREEN_EXIT = 50;
244     public static final int CUJ_LOCKSCREEN_LAUNCH_CAMERA = 51; // reserved.
245     public static final int CUJ_SPLIT_SCREEN_RESIZE = 52;
246     public static final int CUJ_SETTINGS_SLIDER = 53;
247     public static final int CUJ_TAKE_SCREENSHOT = 54;
248     public static final int CUJ_VOLUME_CONTROL = 55;
249     public static final int CUJ_BIOMETRIC_PROMPT_TRANSITION = 56;
250     public static final int CUJ_SETTINGS_TOGGLE = 57;
251     public static final int CUJ_SHADE_DIALOG_OPEN = 58;
252     public static final int CUJ_USER_DIALOG_OPEN = 59;
253     public static final int CUJ_TASKBAR_EXPAND = 60;
254     public static final int CUJ_TASKBAR_COLLAPSE = 61;
255     public static final int CUJ_SHADE_CLEAR_ALL = 62;
256     public static final int CUJ_LAUNCHER_UNLOCK_ENTRANCE_ANIMATION = 63;
257     public static final int CUJ_LOCKSCREEN_OCCLUSION = 64;
258     public static final int CUJ_RECENTS_SCROLLING = 65;
259     public static final int CUJ_LAUNCHER_APP_SWIPE_TO_RECENTS = 66;
260     public static final int CUJ_LAUNCHER_CLOSE_ALL_APPS_SWIPE = 67;
261     public static final int CUJ_LAUNCHER_CLOSE_ALL_APPS_TO_HOME = 68;
262     public static final int CUJ_LOCKSCREEN_CLOCK_MOVE_ANIMATION = 70;
263     public static final int CUJ_LAUNCHER_OPEN_SEARCH_RESULT = 71;
264     // 72 - 77 are reserved for b/281564325.
265 
266     /**
267      * In some cases when we do not have any end-target, we play a simple slide-down animation.
268      * eg: Open an app from Overview/Task switcher such that there is no home-screen icon.
269      * eg: Exit the app using back gesture.
270      */
271     public static final int CUJ_LAUNCHER_APP_CLOSE_TO_HOME_FALLBACK = 78;
272     public static final int CUJ_SHADE_EXPAND_FROM_STATUS_BAR = 79;
273     public static final int CUJ_IME_INSETS_SHOW_ANIMATION = 80;
274     public static final int CUJ_IME_INSETS_HIDE_ANIMATION = 81;
275 
276     public static final int CUJ_SPLIT_SCREEN_DOUBLE_TAP_DIVIDER = 82;
277 
278     private static final int LAST_CUJ = CUJ_SPLIT_SCREEN_DOUBLE_TAP_DIVIDER;
279     private static final int NO_STATSD_LOGGING = -1;
280 
281     // Used to convert CujType to InteractionType enum value for statsd logging.
282     // Use NO_STATSD_LOGGING in case the measurement for a given CUJ should not be logged to statsd.
283     @VisibleForTesting
284     public static final int[] CUJ_TO_STATSD_INTERACTION_TYPE = new int[LAST_CUJ + 1];
285 
286     static {
287         CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__NOTIFICATION_SHADE_SWIPE;
288         CUJ_TO_STATSD_INTERACTION_TYPE[1] = NO_STATSD_LOGGING; // This is deprecated.
289         CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_NOTIFICATION_SHADE_SCROLL_FLING] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_SCROLL_FLING;
290         CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_NOTIFICATION_SHADE_ROW_EXPAND] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_ROW_EXPAND;
291         CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_NOTIFICATION_SHADE_ROW_SWIPE] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_ROW_SWIPE;
292         CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_QS_EXPAND_COLLAPSE;
293         CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_NOTIFICATION_SHADE_QS_SCROLL_SWIPE] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_QS_SCROLL_SWIPE;
294         CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_APP_LAUNCH_FROM_RECENTS] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_LAUNCH_FROM_RECENTS;
295         CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_APP_LAUNCH_FROM_ICON] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_LAUNCH_FROM_ICON;
296         CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_APP_CLOSE_TO_HOME] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_CLOSE_TO_HOME;
297         CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_APP_CLOSE_TO_PIP] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_CLOSE_TO_PIP;
298         CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_QUICK_SWITCH] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_QUICK_SWITCH;
299         CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_NOTIFICATION_HEADS_UP_APPEAR] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_HEADS_UP_APPEAR;
300         CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_NOTIFICATION_HEADS_UP_DISAPPEAR] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_HEADS_UP_DISAPPEAR;
301         CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_NOTIFICATION_ADD] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_NOTIFICATION_ADD;
302         CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_NOTIFICATION_REMOVE] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_NOTIFICATION_REMOVE;
303         CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_NOTIFICATION_APP_START] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH;
304         CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LOCKSCREEN_PASSWORD_APPEAR] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PASSWORD_APPEAR;
305         CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LOCKSCREEN_PATTERN_APPEAR] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PATTERN_APPEAR;
306         CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LOCKSCREEN_PIN_APPEAR] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PIN_APPEAR;
307         CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LOCKSCREEN_PASSWORD_DISAPPEAR] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PASSWORD_DISAPPEAR;
308         CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LOCKSCREEN_PATTERN_DISAPPEAR] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PATTERN_DISAPPEAR;
309         CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LOCKSCREEN_PIN_DISAPPEAR] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PIN_DISAPPEAR;
310         CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LOCKSCREEN_TRANSITION_FROM_AOD] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_TRANSITION_FROM_AOD;
311         CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LOCKSCREEN_TRANSITION_TO_AOD] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_TRANSITION_TO_AOD;
312         CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_OPEN_ALL_APPS] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_OPEN_ALL_APPS;
313         CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_ALL_APPS_SCROLL] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_ALL_APPS_SCROLL;
314         CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_APP_LAUNCH_FROM_WIDGET] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_LAUNCH_FROM_WIDGET;
315         CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SETTINGS_PAGE_SCROLL] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SETTINGS_PAGE_SCROLL;
316         CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LOCKSCREEN_UNLOCK_ANIMATION] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_UNLOCK_ANIMATION;
317         CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SHADE_APP_LAUNCH_FROM_HISTORY_BUTTON] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_HISTORY_BUTTON;
318         CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SHADE_APP_LAUNCH_FROM_MEDIA_PLAYER] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_MEDIA_PLAYER;
319         CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_QS_TILE;
320         CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON;
321         CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP;
322         CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_PIP_TRANSITION] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__PIP_TRANSITION;
323         CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_WALLPAPER_TRANSITION] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__WALLPAPER_TRANSITION;
324         CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_USER_SWITCH] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__USER_SWITCH;
325         CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SPLASHSCREEN_AVD] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SPLASHSCREEN_AVD;
326         CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SPLASHSCREEN_EXIT_ANIM] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SPLASHSCREEN_EXIT_ANIM;
327         CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SCREEN_OFF] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SCREEN_OFF;
328         CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SCREEN_OFF_SHOW_AOD] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SCREEN_OFF_SHOW_AOD;
329         CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_ONE_HANDED_ENTER_TRANSITION] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__ONE_HANDED_ENTER_TRANSITION;
330         CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_ONE_HANDED_EXIT_TRANSITION] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__ONE_HANDED_EXIT_TRANSITION;
331         CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_UNFOLD_ANIM] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__UNFOLD_ANIM;
332         CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SUW_LOADING_TO_SHOW_INFO_WITH_ACTIONS] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SUW_LOADING_TO_SHOW_INFO_WITH_ACTIONS;
333         CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SUW_SHOW_FUNCTION_SCREEN_WITH_ACTIONS] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SUW_SHOW_FUNCTION_SCREEN_WITH_ACTIONS;
334         CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SUW_LOADING_TO_NEXT_FLOW] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SUW_LOADING_TO_NEXT_FLOW;
335         CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SUW_LOADING_SCREEN_FOR_STATUS] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SUW_LOADING_SCREEN_FOR_STATUS;
336         CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SPLIT_SCREEN_ENTER] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SPLIT_SCREEN_ENTER;
337         CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SPLIT_SCREEN_EXIT] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SPLIT_SCREEN_EXIT;
338         CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LOCKSCREEN_LAUNCH_CAMERA] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_LAUNCH_CAMERA;
339         CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SPLIT_SCREEN_RESIZE] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SPLIT_SCREEN_RESIZE;
340         CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SETTINGS_SLIDER] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SETTINGS_SLIDER;
341         CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_TAKE_SCREENSHOT] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__TAKE_SCREENSHOT;
342         CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_VOLUME_CONTROL] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__VOLUME_CONTROL;
343         CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_BIOMETRIC_PROMPT_TRANSITION] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__BIOMETRIC_PROMPT_TRANSITION;
344         CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SETTINGS_TOGGLE] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SETTINGS_TOGGLE;
345         CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SHADE_DIALOG_OPEN] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_DIALOG_OPEN;
346         CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_USER_DIALOG_OPEN] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__USER_DIALOG_OPEN;
347         CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_TASKBAR_EXPAND] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__TASKBAR_EXPAND;
348         CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_TASKBAR_COLLAPSE] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__TASKBAR_COLLAPSE;
349         CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SHADE_CLEAR_ALL] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_CLEAR_ALL;
350         CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_UNLOCK_ENTRANCE_ANIMATION] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_UNLOCK_ENTRANCE_ANIMATION;
351         CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LOCKSCREEN_OCCLUSION] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_OCCLUSION;
352         CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_RECENTS_SCROLLING] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__RECENTS_SCROLLING;
353         CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_APP_SWIPE_TO_RECENTS] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_SWIPE_TO_RECENTS;
354         CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_CLOSE_ALL_APPS_SWIPE] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_CLOSE_ALL_APPS_SWIPE;
355         CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_CLOSE_ALL_APPS_TO_HOME] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_CLOSE_ALL_APPS_TO_HOME;
356         CUJ_TO_STATSD_INTERACTION_TYPE[69] = NO_STATSD_LOGGING; // This is deprecated.
357         CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LOCKSCREEN_CLOCK_MOVE_ANIMATION] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_CLOCK_MOVE_ANIMATION;
358         CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_OPEN_SEARCH_RESULT] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_OPEN_SEARCH_RESULT;
359         // 72 - 77 are reserved for b/281564325.
360         CUJ_TO_STATSD_INTERACTION_TYPE[72] = NO_STATSD_LOGGING;
361         CUJ_TO_STATSD_INTERACTION_TYPE[73] = NO_STATSD_LOGGING;
362         CUJ_TO_STATSD_INTERACTION_TYPE[74] = NO_STATSD_LOGGING;
363         CUJ_TO_STATSD_INTERACTION_TYPE[75] = NO_STATSD_LOGGING;
364         CUJ_TO_STATSD_INTERACTION_TYPE[76] = NO_STATSD_LOGGING;
365         CUJ_TO_STATSD_INTERACTION_TYPE[77] = NO_STATSD_LOGGING;
366         CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_APP_CLOSE_TO_HOME_FALLBACK] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_CLOSE_TO_HOME_FALLBACK;
367         CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SHADE_EXPAND_FROM_STATUS_BAR] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_EXPAND_FROM_STATUS_BAR;
368         CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_IME_INSETS_SHOW_ANIMATION] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__IME_INSETS_SHOW_ANIMATION;
369         CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_IME_INSETS_HIDE_ANIMATION] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__IME_INSETS_HIDE_ANIMATION;
370         CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SPLIT_SCREEN_DOUBLE_TAP_DIVIDER] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SPLIT_SCREEN_DOUBLE_TAP_DIVIDER;
371     }
372 
373     private static class InstanceHolder {
374         public static final InteractionJankMonitor INSTANCE =
375             new InteractionJankMonitor(new HandlerThread(DEFAULT_WORKER_NAME));
376     }
377 
378     private final DeviceConfig.OnPropertiesChangedListener mPropertiesChangedListener =
379             this::updateProperties;
380 
381     @GuardedBy("mLock")
382     private final SparseArray<FrameTracker> mRunningTrackers;
383     @GuardedBy("mLock")
384     private final SparseArray<Runnable> mTimeoutActions;
385     private final HandlerThread mWorker;
386     private final DisplayResolutionTracker mDisplayResolutionTracker;
387     private final Object mLock = new Object();
388     private @ColorInt int mDebugBgColor = Color.CYAN;
389     private double mDebugYOffset = 0.1;
390     private InteractionMonitorDebugOverlay mDebugOverlay;
391 
392     private volatile boolean mEnabled = DEFAULT_ENABLED;
393     private int mSamplingInterval = DEFAULT_SAMPLING_INTERVAL;
394     private int mTraceThresholdMissedFrames = DEFAULT_TRACE_THRESHOLD_MISSED_FRAMES;
395     private int mTraceThresholdFrameTimeMillis = DEFAULT_TRACE_THRESHOLD_FRAME_TIME_MILLIS;
396 
397     /** @hide */
398     @IntDef({
399             CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE,
400             CUJ_NOTIFICATION_SHADE_SCROLL_FLING,
401             CUJ_NOTIFICATION_SHADE_ROW_EXPAND,
402             CUJ_NOTIFICATION_SHADE_ROW_SWIPE,
403             CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE,
404             CUJ_NOTIFICATION_SHADE_QS_SCROLL_SWIPE,
405             CUJ_LAUNCHER_APP_LAUNCH_FROM_RECENTS,
406             CUJ_LAUNCHER_APP_LAUNCH_FROM_ICON,
407             CUJ_LAUNCHER_APP_CLOSE_TO_HOME,
408             CUJ_LAUNCHER_APP_CLOSE_TO_PIP,
409             CUJ_LAUNCHER_QUICK_SWITCH,
410             CUJ_NOTIFICATION_HEADS_UP_APPEAR,
411             CUJ_NOTIFICATION_HEADS_UP_DISAPPEAR,
412             CUJ_NOTIFICATION_ADD,
413             CUJ_NOTIFICATION_REMOVE,
414             CUJ_NOTIFICATION_APP_START,
415             CUJ_LOCKSCREEN_PASSWORD_APPEAR,
416             CUJ_LOCKSCREEN_PATTERN_APPEAR,
417             CUJ_LOCKSCREEN_PIN_APPEAR,
418             CUJ_LOCKSCREEN_PASSWORD_DISAPPEAR,
419             CUJ_LOCKSCREEN_PATTERN_DISAPPEAR,
420             CUJ_LOCKSCREEN_PIN_DISAPPEAR,
421             CUJ_LOCKSCREEN_TRANSITION_FROM_AOD,
422             CUJ_LOCKSCREEN_TRANSITION_TO_AOD,
423             CUJ_LAUNCHER_OPEN_ALL_APPS,
424             CUJ_LAUNCHER_ALL_APPS_SCROLL,
425             CUJ_LAUNCHER_APP_LAUNCH_FROM_WIDGET,
426             CUJ_SETTINGS_PAGE_SCROLL,
427             CUJ_LOCKSCREEN_UNLOCK_ANIMATION,
428             CUJ_SHADE_APP_LAUNCH_FROM_HISTORY_BUTTON,
429             CUJ_SHADE_APP_LAUNCH_FROM_MEDIA_PLAYER,
430             CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE,
431             CUJ_SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON,
432             CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP,
433             CUJ_PIP_TRANSITION,
434             CUJ_WALLPAPER_TRANSITION,
435             CUJ_USER_SWITCH,
436             CUJ_SPLASHSCREEN_AVD,
437             CUJ_SPLASHSCREEN_EXIT_ANIM,
438             CUJ_SCREEN_OFF,
439             CUJ_SCREEN_OFF_SHOW_AOD,
440             CUJ_ONE_HANDED_ENTER_TRANSITION,
441             CUJ_ONE_HANDED_EXIT_TRANSITION,
442             CUJ_UNFOLD_ANIM,
443             CUJ_SUW_LOADING_TO_SHOW_INFO_WITH_ACTIONS,
444             CUJ_SUW_SHOW_FUNCTION_SCREEN_WITH_ACTIONS,
445             CUJ_SUW_LOADING_TO_NEXT_FLOW,
446             CUJ_SUW_LOADING_SCREEN_FOR_STATUS,
447             CUJ_SPLIT_SCREEN_ENTER,
448             CUJ_SPLIT_SCREEN_EXIT,
449             CUJ_LOCKSCREEN_LAUNCH_CAMERA,
450             CUJ_SPLIT_SCREEN_RESIZE,
451             CUJ_SETTINGS_SLIDER,
452             CUJ_TAKE_SCREENSHOT,
453             CUJ_VOLUME_CONTROL,
454             CUJ_BIOMETRIC_PROMPT_TRANSITION,
455             CUJ_SETTINGS_TOGGLE,
456             CUJ_SHADE_DIALOG_OPEN,
457             CUJ_USER_DIALOG_OPEN,
458             CUJ_TASKBAR_EXPAND,
459             CUJ_TASKBAR_COLLAPSE,
460             CUJ_SHADE_CLEAR_ALL,
461             CUJ_LAUNCHER_UNLOCK_ENTRANCE_ANIMATION,
462             CUJ_LOCKSCREEN_OCCLUSION,
463             CUJ_RECENTS_SCROLLING,
464             CUJ_LAUNCHER_APP_SWIPE_TO_RECENTS,
465             CUJ_LAUNCHER_CLOSE_ALL_APPS_SWIPE,
466             CUJ_LAUNCHER_CLOSE_ALL_APPS_TO_HOME,
467             CUJ_LOCKSCREEN_CLOCK_MOVE_ANIMATION,
468             CUJ_LAUNCHER_OPEN_SEARCH_RESULT,
469             CUJ_LAUNCHER_APP_CLOSE_TO_HOME_FALLBACK,
470             CUJ_SHADE_EXPAND_FROM_STATUS_BAR,
471             CUJ_IME_INSETS_SHOW_ANIMATION,
472             CUJ_IME_INSETS_HIDE_ANIMATION,
473             CUJ_SPLIT_SCREEN_DOUBLE_TAP_DIVIDER,
474     })
475     @Retention(RetentionPolicy.SOURCE)
476     public @interface CujType {
477     }
478 
479     /**
480      * Get the singleton of InteractionJankMonitor.
481      *
482      * @return instance of InteractionJankMonitor
483      */
getInstance()484     public static InteractionJankMonitor getInstance() {
485         return InstanceHolder.INSTANCE;
486     }
487 
488     /**
489      * This constructor should be only public to tests.
490      *
491      * @param worker the worker thread for the callbacks
492      */
493     @VisibleForTesting
494     @RequiresPermission(Manifest.permission.READ_DEVICE_CONFIG)
InteractionJankMonitor(@onNull HandlerThread worker)495     public InteractionJankMonitor(@NonNull HandlerThread worker) {
496         mRunningTrackers = new SparseArray<>();
497         mTimeoutActions = new SparseArray<>();
498         mWorker = worker;
499         mWorker.start();
500         mDisplayResolutionTracker = new DisplayResolutionTracker(worker.getThreadHandler());
501         mSamplingInterval = DEFAULT_SAMPLING_INTERVAL;
502         mEnabled = DEFAULT_ENABLED;
503 
504         final Context context = ActivityThread.currentApplication();
505         if (context.checkCallingOrSelfPermission(READ_DEVICE_CONFIG) != PERMISSION_GRANTED) {
506             if (DEBUG) {
507                 Log.d(TAG, "Initialized the InteractionJankMonitor."
508                         + " (No READ_DEVICE_CONFIG permission to change configs)"
509                         + " enabled=" + mEnabled + ", interval=" + mSamplingInterval
510                         + ", missedFrameThreshold=" + mTraceThresholdMissedFrames
511                         + ", frameTimeThreshold=" + mTraceThresholdFrameTimeMillis
512                         + ", package=" + context.getPackageName());
513             }
514             return;
515         }
516 
517         // Post initialization to the background in case we're running on the main thread.
518         mWorker.getThreadHandler().post(
519                 () -> {
520                     try {
521                         mPropertiesChangedListener.onPropertiesChanged(
522                                 DeviceConfig.getProperties(NAMESPACE_INTERACTION_JANK_MONITOR));
523                         DeviceConfig.addOnPropertiesChangedListener(
524                                 NAMESPACE_INTERACTION_JANK_MONITOR,
525                                 new HandlerExecutor(mWorker.getThreadHandler()),
526                                 mPropertiesChangedListener);
527                     } catch (SecurityException ex) {
528                         Log.d(TAG, "Can't get properties: READ_DEVICE_CONFIG granted="
529                                 + context.checkCallingOrSelfPermission(READ_DEVICE_CONFIG)
530                                 + ", package=" + context.getPackageName());
531                     }
532                 });
533     }
534 
535     /**
536      * Creates a {@link FrameTracker} instance.
537      *
538      * @param config the config used in instrumenting
539      * @param session the session associates with this tracker
540      * @return instance of the FrameTracker
541      */
542     @VisibleForTesting
createFrameTracker(Configuration config, Session session)543     public FrameTracker createFrameTracker(Configuration config, Session session) {
544         final View view = config.mView;
545 
546         if (!config.hasValidView()) {
547             boolean attached = false;
548             boolean hasViewRoot = false;
549             boolean hasRenderer = false;
550             if (view != null) {
551                 attached = view.isAttachedToWindow();
552                 hasViewRoot = view.getViewRootImpl() != null;
553                 hasRenderer = view.getThreadedRenderer() != null;
554             }
555             Log.d(TAG, "create FrameTracker fails: view=" + view
556                     + ", attached=" + attached + ", hasViewRoot=" + hasViewRoot
557                     + ", hasRenderer=" + hasRenderer, new Throwable());
558             return null;
559         }
560 
561         final ThreadedRendererWrapper threadedRenderer =
562                 view == null ? null : new ThreadedRendererWrapper(view.getThreadedRenderer());
563         final ViewRootWrapper viewRoot =
564                 view == null ? null : new ViewRootWrapper(view.getViewRootImpl());
565         final SurfaceControlWrapper surfaceControl = new SurfaceControlWrapper();
566         final ChoreographerWrapper choreographer =
567                 new ChoreographerWrapper(Choreographer.getInstance());
568         final FrameTrackerListener eventsListener = (s, act) -> handleCujEvents(act, s);
569         final FrameMetricsWrapper frameMetrics = new FrameMetricsWrapper();
570 
571         return new FrameTracker(this, session, config.getHandler(), threadedRenderer, viewRoot,
572                 surfaceControl, choreographer, frameMetrics,
573                 new FrameTracker.StatsLogWrapper(mDisplayResolutionTracker),
574                 mTraceThresholdMissedFrames, mTraceThresholdFrameTimeMillis,
575                 eventsListener, config);
576     }
577 
578     @UiThread
handleCujEvents(String action, Session session)579     private void handleCujEvents(String action, Session session) {
580         // Clear the running and timeout tasks if the end / cancel was fired within the tracker.
581         // Or we might have memory leaks.
582         if (needRemoveTasks(action, session)) {
583             getTracker(session.getCuj()).getHandler().runWithScissors(() -> {
584                 removeTimeout(session.getCuj());
585                 removeTracker(session.getCuj(), session.getReason());
586             }, EXECUTOR_TASK_TIMEOUT);
587         }
588     }
589 
needRemoveTasks(String action, Session session)590     private boolean needRemoveTasks(String action, Session session) {
591         final boolean badEnd = action.equals(ACTION_SESSION_END)
592                 && session.getReason() != REASON_END_NORMAL;
593         final boolean badCancel = action.equals(ACTION_SESSION_CANCEL)
594                 && !(session.getReason() == REASON_CANCEL_NORMAL
595                 || session.getReason() == REASON_CANCEL_TIMEOUT);
596         return badEnd || badCancel;
597     }
598 
removeTimeout(@ujType int cujType)599     private void removeTimeout(@CujType int cujType) {
600         synchronized (mLock) {
601             Runnable timeout = mTimeoutActions.get(cujType);
602             if (timeout != null) {
603                 getTracker(cujType).getHandler().removeCallbacks(timeout);
604                 mTimeoutActions.remove(cujType);
605             }
606         }
607     }
608 
609     /**
610      * @param cujType cuj type
611      * @return true if the cuj is under instrumenting, false otherwise.
612      */
isInstrumenting(@ujType int cujType)613     public boolean isInstrumenting(@CujType int cujType) {
614         synchronized (mLock) {
615             return mRunningTrackers.contains(cujType);
616         }
617     }
618 
619     /**
620      * Begins a trace session.
621      *
622      * @param v an attached view.
623      * @param cujType the specific {@link InteractionJankMonitor.CujType}.
624      * @return boolean true if the tracker is started successfully, false otherwise.
625      */
begin(View v, @CujType int cujType)626     public boolean begin(View v, @CujType int cujType) {
627         try {
628             return begin(Configuration.Builder.withView(cujType, v));
629         } catch (IllegalArgumentException ex) {
630             Log.d(TAG, "Build configuration failed!", ex);
631             return false;
632         }
633     }
634 
635     /**
636      * Begins a trace session.
637      *
638      * @param builder the builder of the configurations for instrumenting the CUJ.
639      * @return boolean true if the tracker is begun successfully, false otherwise.
640      */
begin(@onNull Configuration.Builder builder)641     public boolean begin(@NonNull Configuration.Builder builder) {
642         try {
643             final Configuration config = builder.build();
644             postEventLogToWorkerThread((unixNanos, elapsedNanos, realtimeNanos) -> {
645                 EventLogTags.writeJankCujEventsBeginRequest(
646                         config.mCujType, unixNanos, elapsedNanos, realtimeNanos, config.mTag);
647             });
648             final TrackerResult result = new TrackerResult();
649             final boolean success = config.getHandler().runWithScissors(
650                     () -> result.mResult = beginInternal(config), EXECUTOR_TASK_TIMEOUT);
651             if (!success) {
652                 Log.d(TAG, "begin failed due to timeout, CUJ=" + getNameOfCuj(config.mCujType));
653                 return false;
654             }
655             return result.mResult;
656         } catch (IllegalArgumentException ex) {
657             Log.d(TAG, "Build configuration failed!", ex);
658             return false;
659         }
660     }
661 
662     @UiThread
beginInternal(@onNull Configuration conf)663     private boolean beginInternal(@NonNull Configuration conf) {
664         int cujType = conf.mCujType;
665         if (!shouldMonitor(cujType)) return false;
666         FrameTracker tracker = getTracker(cujType);
667         // Skip subsequent calls if we already have an ongoing tracing.
668         if (tracker != null) return false;
669 
670         // begin a new trace session.
671         tracker = createFrameTracker(conf, new Session(cujType, conf.mTag));
672         if (tracker == null) return false;
673         putTracker(cujType, tracker);
674         tracker.begin();
675 
676         // Cancel the trace if we don't get an end() call in specified duration.
677         scheduleTimeoutAction(
678                 cujType, conf.mTimeout, () -> cancel(cujType, REASON_CANCEL_TIMEOUT));
679         return true;
680     }
681 
682     /**
683      * Check if the monitoring is enabled and if it should be sampled.
684      */
685     @SuppressWarnings("RandomModInteger")
686     @VisibleForTesting
shouldMonitor(@ujType int cujType)687     public boolean shouldMonitor(@CujType int cujType) {
688         boolean shouldSample = ThreadLocalRandom.current().nextInt() % mSamplingInterval == 0;
689         if (!mEnabled || !shouldSample) {
690             if (DEBUG) {
691                 Log.d(TAG, "Skip monitoring cuj: " + getNameOfCuj(cujType)
692                         + ", enable=" + mEnabled + ", debuggable=" + DEFAULT_ENABLED
693                         + ", sample=" + shouldSample + ", interval=" + mSamplingInterval);
694             }
695             return false;
696         }
697         return true;
698     }
699 
700     /**
701      * Schedules a timeout action.
702      * @param cuj cuj type
703      * @param timeout duration to timeout
704      * @param action action once timeout
705      */
706     @VisibleForTesting
scheduleTimeoutAction(@ujType int cuj, long timeout, Runnable action)707     public void scheduleTimeoutAction(@CujType int cuj, long timeout, Runnable action) {
708         synchronized (mLock) {
709             mTimeoutActions.put(cuj, action);
710             getTracker(cuj).getHandler().postDelayed(action, timeout);
711         }
712     }
713 
714     /**
715      * Ends a trace session.
716      *
717      * @param cujType the specific {@link InteractionJankMonitor.CujType}.
718      * @return boolean true if the tracker is ended successfully, false otherwise.
719      */
end(@ujType int cujType)720     public boolean end(@CujType int cujType) {
721         postEventLogToWorkerThread((unixNanos, elapsedNanos, realtimeNanos) -> {
722             EventLogTags.writeJankCujEventsEndRequest(
723                     cujType, unixNanos, elapsedNanos, realtimeNanos);
724         });
725         FrameTracker tracker = getTracker(cujType);
726         // Skip this call since we haven't started a trace yet.
727         if (tracker == null) return false;
728         try {
729             final TrackerResult result = new TrackerResult();
730             final boolean success = tracker.getHandler().runWithScissors(
731                     () -> result.mResult = endInternal(cujType), EXECUTOR_TASK_TIMEOUT);
732             if (!success) {
733                 Log.d(TAG, "end failed due to timeout, CUJ=" + getNameOfCuj(cujType));
734                 return false;
735             }
736             return result.mResult;
737         } catch (IllegalArgumentException ex) {
738             Log.d(TAG, "Execute end task failed!", ex);
739             return false;
740         }
741     }
742 
743     @UiThread
endInternal(@ujType int cujType)744     private boolean endInternal(@CujType int cujType) {
745         // remove the timeout action first.
746         removeTimeout(cujType);
747         FrameTracker tracker = getTracker(cujType);
748         if (tracker == null) return false;
749         // if the end call doesn't return true, another thread is handling end of the cuj.
750         if (tracker.end(REASON_END_NORMAL)) {
751             removeTracker(cujType, REASON_END_NORMAL);
752         }
753         return true;
754     }
755 
756     /**
757      * Cancels the trace session.
758      *
759      * @return boolean true if the tracker is cancelled successfully, false otherwise.
760      */
cancel(@ujType int cujType)761     public boolean cancel(@CujType int cujType) {
762         postEventLogToWorkerThread((unixNanos, elapsedNanos, realtimeNanos) -> {
763             EventLogTags.writeJankCujEventsCancelRequest(
764                     cujType, unixNanos, elapsedNanos, realtimeNanos);
765         });
766         return cancel(cujType, REASON_CANCEL_NORMAL);
767     }
768 
769     /**
770      * Cancels the trace session.
771      *
772      * @return boolean true if the tracker is cancelled successfully, false otherwise.
773      */
774     @VisibleForTesting
cancel(@ujType int cujType, @Reasons int reason)775     public boolean cancel(@CujType int cujType, @Reasons int reason) {
776         FrameTracker tracker = getTracker(cujType);
777         // Skip this call since we haven't started a trace yet.
778         if (tracker == null) return false;
779         try {
780             final TrackerResult result = new TrackerResult();
781             final boolean success = tracker.getHandler().runWithScissors(
782                     () -> result.mResult = cancelInternal(cujType, reason), EXECUTOR_TASK_TIMEOUT);
783             if (!success) {
784                 Log.d(TAG, "cancel failed due to timeout, CUJ=" + getNameOfCuj(cujType));
785                 return false;
786             }
787             return result.mResult;
788         } catch (IllegalArgumentException ex) {
789             Log.d(TAG, "Execute cancel task failed!", ex);
790             return false;
791         }
792     }
793 
794     @UiThread
cancelInternal(@ujType int cujType, @Reasons int reason)795     private boolean cancelInternal(@CujType int cujType, @Reasons int reason) {
796         // remove the timeout action first.
797         removeTimeout(cujType);
798         FrameTracker tracker = getTracker(cujType);
799         if (tracker == null) return false;
800         // if the cancel call doesn't return true, another thread is handling cancel of the cuj.
801         if (tracker.cancel(reason)) {
802             removeTracker(cujType, reason);
803         }
804         return true;
805     }
806 
807     @UiThread
putTracker(@ujType int cuj, @NonNull FrameTracker tracker)808     private void putTracker(@CujType int cuj, @NonNull FrameTracker tracker) {
809         synchronized (mLock) {
810             mRunningTrackers.put(cuj, tracker);
811             if (mDebugOverlay != null) {
812                 mDebugOverlay.onTrackerAdded(cuj, tracker);
813             }
814             if (DEBUG) {
815                 Log.d(TAG, "Added tracker for " + getNameOfCuj(cuj)
816                         + ". mRunningTrackers=" + listNamesOfCujs(mRunningTrackers));
817             }
818         }
819     }
820 
getTracker(@ujType int cuj)821     private FrameTracker getTracker(@CujType int cuj) {
822         synchronized (mLock) {
823             return mRunningTrackers.get(cuj);
824         }
825     }
826 
827     @UiThread
removeTracker(@ujType int cuj, int reason)828     private void removeTracker(@CujType int cuj, int reason) {
829         synchronized (mLock) {
830             mRunningTrackers.remove(cuj);
831             if (mDebugOverlay != null) {
832                 mDebugOverlay.onTrackerRemoved(cuj, reason, mRunningTrackers);
833             }
834             if (DEBUG) {
835                 Log.d(TAG, "Removed tracker for " + getNameOfCuj(cuj)
836                         + ". mRunningTrackers=" + listNamesOfCujs(mRunningTrackers));
837             }
838         }
839     }
840 
841     @WorkerThread
updateProperties(DeviceConfig.Properties properties)842     private void updateProperties(DeviceConfig.Properties properties) {
843         mSamplingInterval = properties.getInt(SETTINGS_SAMPLING_INTERVAL_KEY,
844                 DEFAULT_SAMPLING_INTERVAL);
845         mTraceThresholdMissedFrames = properties.getInt(SETTINGS_THRESHOLD_MISSED_FRAMES_KEY,
846                 DEFAULT_TRACE_THRESHOLD_MISSED_FRAMES);
847         mTraceThresholdFrameTimeMillis = properties.getInt(
848                 SETTINGS_THRESHOLD_FRAME_TIME_MILLIS_KEY,
849                 DEFAULT_TRACE_THRESHOLD_FRAME_TIME_MILLIS);
850         // Never allow the debug overlay to be used on user builds
851         boolean debugOverlayEnabled = Build.IS_DEBUGGABLE && properties.getBoolean(
852                 SETTINGS_DEBUG_OVERLAY_ENABLED_KEY,
853                 DEFAULT_DEBUG_OVERLAY_ENABLED);
854         if (debugOverlayEnabled && mDebugOverlay == null) {
855             mDebugOverlay = new InteractionMonitorDebugOverlay(mLock, mDebugBgColor, mDebugYOffset);
856         } else if (!debugOverlayEnabled && mDebugOverlay != null) {
857             mDebugOverlay.dispose();
858             mDebugOverlay = null;
859         }
860         // The memory visibility is powered by the volatile field, mEnabled.
861         mEnabled = properties.getBoolean(SETTINGS_ENABLED_KEY, DEFAULT_ENABLED);
862     }
863 
864     @VisibleForTesting
getPropertiesChangedListener()865     public DeviceConfig.OnPropertiesChangedListener getPropertiesChangedListener() {
866         return mPropertiesChangedListener;
867     }
868 
869     /**
870      * Triggers the perfetto daemon to collect and upload data.
871      */
872     @VisibleForTesting
trigger(Session session)873     public void trigger(Session session) {
874         mWorker.getThreadHandler().post(
875                 () -> PerfettoTrigger.trigger(session.getPerfettoTrigger()));
876     }
877 
878     /**
879      * A helper method to translate interaction type to CUJ name.
880      *
881      * @param interactionType the interaction type defined in AtomsProto.java
882      * @return the name of the interaction type
883      */
getNameOfInteraction(int interactionType)884     public static String getNameOfInteraction(int interactionType) {
885         // There is an offset amount of 1 between cujType and interactionType.
886         return getNameOfCuj(getCujTypeFromInteraction(interactionType));
887     }
888 
889     /**
890      * A helper method to translate interaction type to CUJ type.
891      *
892      * @param interactionType the interaction type defined in AtomsProto.java
893      * @return the integer in {@link CujType}
894      */
getCujTypeFromInteraction(int interactionType)895     private static int getCujTypeFromInteraction(int interactionType) {
896         return interactionType - 1;
897     }
898 
899     /**
900      * Configures the debug overlay used for displaying interaction names on the screen while they
901      * occur.
902      *
903      * @param bgColor the background color of the box used to display the CUJ names
904      * @param yOffset number between 0 and 1 to indicate where the top of the box should be relative
905      *                to the height of the screen
906      */
configDebugOverlay(@olorInt int bgColor, double yOffset)907     public void configDebugOverlay(@ColorInt int bgColor, double yOffset) {
908         mDebugBgColor = bgColor;
909         mDebugYOffset = yOffset;
910     }
911 
912     /**
913      * A helper method for getting a string representation of all running CUJs. For example,
914      * "(LOCKSCREEN_TRANSITION_FROM_AOD, IME_INSETS_ANIMATION)"
915      */
listNamesOfCujs(SparseArray<FrameTracker> trackers)916     private static String listNamesOfCujs(SparseArray<FrameTracker> trackers) {
917         if (!DEBUG) {
918             return null;
919         }
920         StringBuilder sb = new StringBuilder();
921         sb.append('(');
922         for (int i = 0; i < trackers.size(); i++) {
923             sb.append(getNameOfCuj(trackers.keyAt(i)));
924             if (i < trackers.size() - 1) {
925                 sb.append(", ");
926             }
927         }
928         sb.append(')');
929         return sb.toString();
930     }
931 
932     /**
933      * A helper method to translate CUJ type to CUJ name.
934      *
935      * @param cujType the cuj type defined in this file
936      * @return the name of the cuj type
937      */
getNameOfCuj(int cujType)938     public static String getNameOfCuj(int cujType) {
939         // Please note:
940         // 1. The length of the returned string shouldn't exceed MAX_LENGTH_OF_CUJ_NAME.
941         // 2. The returned string should be the same with the name defined in atoms.proto.
942         switch (cujType) {
943             case CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE:
944                 return "NOTIFICATION_SHADE_EXPAND_COLLAPSE";
945             case CUJ_NOTIFICATION_SHADE_SCROLL_FLING:
946                 return "NOTIFICATION_SHADE_SCROLL_FLING";
947             case CUJ_NOTIFICATION_SHADE_ROW_EXPAND:
948                 return "NOTIFICATION_SHADE_ROW_EXPAND";
949             case CUJ_NOTIFICATION_SHADE_ROW_SWIPE:
950                 return "NOTIFICATION_SHADE_ROW_SWIPE";
951             case CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE:
952                 return "NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE";
953             case CUJ_NOTIFICATION_SHADE_QS_SCROLL_SWIPE:
954                 return "NOTIFICATION_SHADE_QS_SCROLL_SWIPE";
955             case CUJ_LAUNCHER_APP_LAUNCH_FROM_RECENTS:
956                 return "LAUNCHER_APP_LAUNCH_FROM_RECENTS";
957             case CUJ_LAUNCHER_APP_LAUNCH_FROM_ICON:
958                 return "LAUNCHER_APP_LAUNCH_FROM_ICON";
959             case CUJ_LAUNCHER_APP_CLOSE_TO_HOME:
960                 return "LAUNCHER_APP_CLOSE_TO_HOME";
961             case CUJ_LAUNCHER_APP_CLOSE_TO_PIP:
962                 return "LAUNCHER_APP_CLOSE_TO_PIP";
963             case CUJ_LAUNCHER_QUICK_SWITCH:
964                 return "LAUNCHER_QUICK_SWITCH";
965             case CUJ_NOTIFICATION_HEADS_UP_APPEAR:
966                 return "NOTIFICATION_HEADS_UP_APPEAR";
967             case CUJ_NOTIFICATION_HEADS_UP_DISAPPEAR:
968                 return "NOTIFICATION_HEADS_UP_DISAPPEAR";
969             case CUJ_NOTIFICATION_ADD:
970                 return "NOTIFICATION_ADD";
971             case CUJ_NOTIFICATION_REMOVE:
972                 return "NOTIFICATION_REMOVE";
973             case CUJ_NOTIFICATION_APP_START:
974                 return "NOTIFICATION_APP_START";
975             case CUJ_LOCKSCREEN_PASSWORD_APPEAR:
976                 return "LOCKSCREEN_PASSWORD_APPEAR";
977             case CUJ_LOCKSCREEN_PATTERN_APPEAR:
978                 return "LOCKSCREEN_PATTERN_APPEAR";
979             case CUJ_LOCKSCREEN_PIN_APPEAR:
980                 return "LOCKSCREEN_PIN_APPEAR";
981             case CUJ_LOCKSCREEN_PASSWORD_DISAPPEAR:
982                 return "LOCKSCREEN_PASSWORD_DISAPPEAR";
983             case CUJ_LOCKSCREEN_PATTERN_DISAPPEAR:
984                 return "LOCKSCREEN_PATTERN_DISAPPEAR";
985             case CUJ_LOCKSCREEN_PIN_DISAPPEAR:
986                 return "LOCKSCREEN_PIN_DISAPPEAR";
987             case CUJ_LOCKSCREEN_TRANSITION_FROM_AOD:
988                 return "LOCKSCREEN_TRANSITION_FROM_AOD";
989             case CUJ_LOCKSCREEN_TRANSITION_TO_AOD:
990                 return "LOCKSCREEN_TRANSITION_TO_AOD";
991             case CUJ_LAUNCHER_OPEN_ALL_APPS :
992                 return "LAUNCHER_OPEN_ALL_APPS";
993             case CUJ_LAUNCHER_ALL_APPS_SCROLL:
994                 return "LAUNCHER_ALL_APPS_SCROLL";
995             case CUJ_LAUNCHER_APP_LAUNCH_FROM_WIDGET:
996                 return "LAUNCHER_APP_LAUNCH_FROM_WIDGET";
997             case CUJ_SETTINGS_PAGE_SCROLL:
998                 return "SETTINGS_PAGE_SCROLL";
999             case CUJ_LOCKSCREEN_UNLOCK_ANIMATION:
1000                 return "LOCKSCREEN_UNLOCK_ANIMATION";
1001             case CUJ_SHADE_APP_LAUNCH_FROM_HISTORY_BUTTON:
1002                 return "SHADE_APP_LAUNCH_FROM_HISTORY_BUTTON";
1003             case CUJ_SHADE_APP_LAUNCH_FROM_MEDIA_PLAYER:
1004                 return "SHADE_APP_LAUNCH_FROM_MEDIA_PLAYER";
1005             case CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE:
1006                 return "SHADE_APP_LAUNCH_FROM_QS_TILE";
1007             case CUJ_SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON:
1008                 return "SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON";
1009             case CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP:
1010                 return "STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP";
1011             case CUJ_PIP_TRANSITION:
1012                 return "PIP_TRANSITION";
1013             case CUJ_WALLPAPER_TRANSITION:
1014                 return "WALLPAPER_TRANSITION";
1015             case CUJ_USER_SWITCH:
1016                 return "USER_SWITCH";
1017             case CUJ_SPLASHSCREEN_AVD:
1018                 return "SPLASHSCREEN_AVD";
1019             case CUJ_SPLASHSCREEN_EXIT_ANIM:
1020                 return "SPLASHSCREEN_EXIT_ANIM";
1021             case CUJ_SCREEN_OFF:
1022                 return "SCREEN_OFF";
1023             case CUJ_SCREEN_OFF_SHOW_AOD:
1024                 return "SCREEN_OFF_SHOW_AOD";
1025             case CUJ_ONE_HANDED_ENTER_TRANSITION:
1026                 return "ONE_HANDED_ENTER_TRANSITION";
1027             case CUJ_ONE_HANDED_EXIT_TRANSITION:
1028                 return "ONE_HANDED_EXIT_TRANSITION";
1029             case CUJ_UNFOLD_ANIM:
1030                 return "UNFOLD_ANIM";
1031             case CUJ_SUW_LOADING_TO_SHOW_INFO_WITH_ACTIONS:
1032                 return "SUW_LOADING_TO_SHOW_INFO_WITH_ACTIONS";
1033             case CUJ_SUW_SHOW_FUNCTION_SCREEN_WITH_ACTIONS:
1034                 return "SUW_SHOW_FUNCTION_SCREEN_WITH_ACTIONS";
1035             case CUJ_SUW_LOADING_TO_NEXT_FLOW:
1036                 return "SUW_LOADING_TO_NEXT_FLOW";
1037             case CUJ_SUW_LOADING_SCREEN_FOR_STATUS:
1038                 return "SUW_LOADING_SCREEN_FOR_STATUS";
1039             case CUJ_SPLIT_SCREEN_ENTER:
1040                 return "SPLIT_SCREEN_ENTER";
1041             case CUJ_SPLIT_SCREEN_EXIT:
1042                 return "SPLIT_SCREEN_EXIT";
1043             case CUJ_LOCKSCREEN_LAUNCH_CAMERA:
1044                 return "LOCKSCREEN_LAUNCH_CAMERA";
1045             case CUJ_SPLIT_SCREEN_RESIZE:
1046                 return "SPLIT_SCREEN_RESIZE";
1047             case CUJ_SETTINGS_SLIDER:
1048                 return "SETTINGS_SLIDER";
1049             case CUJ_TAKE_SCREENSHOT:
1050                 return "TAKE_SCREENSHOT";
1051             case CUJ_VOLUME_CONTROL:
1052                 return "VOLUME_CONTROL";
1053             case CUJ_BIOMETRIC_PROMPT_TRANSITION:
1054                 return "BIOMETRIC_PROMPT_TRANSITION";
1055             case CUJ_SETTINGS_TOGGLE:
1056                 return "SETTINGS_TOGGLE";
1057             case CUJ_SHADE_DIALOG_OPEN:
1058                 return "SHADE_DIALOG_OPEN";
1059             case CUJ_USER_DIALOG_OPEN:
1060                 return "USER_DIALOG_OPEN";
1061             case CUJ_TASKBAR_EXPAND:
1062                 return "TASKBAR_EXPAND";
1063             case CUJ_TASKBAR_COLLAPSE:
1064                 return "TASKBAR_COLLAPSE";
1065             case CUJ_SHADE_CLEAR_ALL:
1066                 return "SHADE_CLEAR_ALL";
1067             case CUJ_LAUNCHER_UNLOCK_ENTRANCE_ANIMATION:
1068                 return "LAUNCHER_UNLOCK_ENTRANCE_ANIMATION";
1069             case CUJ_LOCKSCREEN_OCCLUSION:
1070                 return "LOCKSCREEN_OCCLUSION";
1071             case CUJ_RECENTS_SCROLLING:
1072                 return "RECENTS_SCROLLING";
1073             case CUJ_LAUNCHER_APP_SWIPE_TO_RECENTS:
1074                 return "LAUNCHER_APP_SWIPE_TO_RECENTS";
1075             case CUJ_LAUNCHER_CLOSE_ALL_APPS_SWIPE:
1076                 return "LAUNCHER_CLOSE_ALL_APPS_SWIPE";
1077             case CUJ_LAUNCHER_CLOSE_ALL_APPS_TO_HOME:
1078                 return "LAUNCHER_CLOSE_ALL_APPS_TO_HOME";
1079             case CUJ_LOCKSCREEN_CLOCK_MOVE_ANIMATION:
1080                 return "LOCKSCREEN_CLOCK_MOVE_ANIMATION";
1081             case CUJ_LAUNCHER_OPEN_SEARCH_RESULT:
1082                 return "LAUNCHER_OPEN_SEARCH_RESULT";
1083             case CUJ_LAUNCHER_APP_CLOSE_TO_HOME_FALLBACK:
1084                 return "LAUNCHER_APP_CLOSE_TO_HOME_FALLBACK";
1085             case CUJ_SHADE_EXPAND_FROM_STATUS_BAR:
1086                 return "SHADE_EXPAND_FROM_STATUS_BAR";
1087             case CUJ_IME_INSETS_SHOW_ANIMATION:
1088                 return "IME_INSETS_SHOW_ANIMATION";
1089             case CUJ_IME_INSETS_HIDE_ANIMATION:
1090                 return "IME_INSETS_HIDE_ANIMATION";
1091             case CUJ_SPLIT_SCREEN_DOUBLE_TAP_DIVIDER:
1092                 return "SPLIT_SCREEN_DOUBLE_TAP_DIVIDER";
1093         }
1094         return "UNKNOWN";
1095     }
1096 
1097     private static class TrackerResult {
1098         private boolean mResult;
1099     }
1100 
1101     /**
1102      * Configurations used while instrumenting the CUJ. <br/>
1103      * <b>It may refer to an attached view, don't use static reference for any purpose.</b>
1104      */
1105     public static class Configuration {
1106         private final View mView;
1107         private final Context mContext;
1108         private final long mTimeout;
1109         private final String mTag;
1110         private final boolean mSurfaceOnly;
1111         private final SurfaceControl mSurfaceControl;
1112         private final @CujType int mCujType;
1113         private final boolean mDeferMonitor;
1114         private final Handler mHandler;
1115 
1116         /**
1117          * A builder for building Configuration. {@link #setView(View)} is essential
1118          * if {@link #setSurfaceOnly(boolean)} is not set, otherwise both
1119          * {@link #setSurfaceControl(SurfaceControl)} and {@link #setContext(Context)}
1120          * are necessary<br/>
1121          * <b>It may refer to an attached view, don't use static reference for any purpose.</b>
1122          */
1123         public static class Builder {
1124             private View mAttrView = null;
1125             private Context mAttrContext = null;
1126             private long mAttrTimeout = DEFAULT_TIMEOUT_MS;
1127             private String mAttrTag = "";
1128             private boolean mAttrSurfaceOnly;
1129             private SurfaceControl mAttrSurfaceControl;
1130             private @CujType int mAttrCujType;
1131             private boolean mAttrDeferMonitor = true;
1132 
1133             /**
1134              * Creates a builder which instruments only surface.
1135              * @param cuj The enum defined in {@link InteractionJankMonitor.CujType}.
1136              * @param context context
1137              * @param surfaceControl surface control
1138              * @return builder
1139              */
withSurface(@ujType int cuj, @NonNull Context context, @NonNull SurfaceControl surfaceControl)1140             public static Builder withSurface(@CujType int cuj, @NonNull Context context,
1141                     @NonNull SurfaceControl surfaceControl) {
1142                 return new Builder(cuj)
1143                         .setContext(context)
1144                         .setSurfaceControl(surfaceControl)
1145                         .setSurfaceOnly(true);
1146             }
1147 
1148             /**
1149              * Creates a builder which instruments both surface and view.
1150              * @param cuj The enum defined in {@link InteractionJankMonitor.CujType}.
1151              * @param view view
1152              * @return builder
1153              */
withView(@ujType int cuj, @NonNull View view)1154             public static Builder withView(@CujType int cuj, @NonNull View view) {
1155                 return new Builder(cuj).setView(view)
1156                         .setContext(view.getContext());
1157             }
1158 
Builder(@ujType int cuj)1159             private Builder(@CujType int cuj) {
1160                 mAttrCujType = cuj;
1161             }
1162 
1163             /**
1164              * Specifies a view, must be set if {@link #setSurfaceOnly(boolean)} is set to false.
1165              * @param view an attached view
1166              * @return builder
1167              */
setView(@onNull View view)1168             private Builder setView(@NonNull View view) {
1169                 mAttrView = view;
1170                 return this;
1171             }
1172 
1173             /**
1174              * @param timeout duration to cancel the instrumentation in ms
1175              * @return builder
1176              */
setTimeout(long timeout)1177             public Builder setTimeout(long timeout) {
1178                 mAttrTimeout = timeout;
1179                 return this;
1180             }
1181 
1182             /**
1183              * @param tag The postfix of the CUJ in the output trace.
1184              *           It provides a brief description for the CUJ like the concrete class
1185              *           who is dealing with the CUJ or the important state with the CUJ, etc.
1186              * @return builder
1187              */
setTag(@onNull String tag)1188             public Builder setTag(@NonNull String tag) {
1189                 mAttrTag = tag;
1190                 return this;
1191             }
1192 
1193             /**
1194              * Indicates if only instrument with surface,
1195              * if true, must also setup with {@link #setContext(Context)}
1196              * and {@link #setSurfaceControl(SurfaceControl)}.
1197              * @param surfaceOnly true if only instrument with surface, false otherwise
1198              * @return builder Surface only builder.
1199              */
setSurfaceOnly(boolean surfaceOnly)1200             private Builder setSurfaceOnly(boolean surfaceOnly) {
1201                 mAttrSurfaceOnly = surfaceOnly;
1202                 return this;
1203             }
1204 
1205             /**
1206              * Specifies a context, must set if {@link #setSurfaceOnly(boolean)} is set.
1207              */
setContext(Context context)1208             private Builder setContext(Context context) {
1209                 mAttrContext = context;
1210                 return this;
1211             }
1212 
1213             /**
1214              * Specifies a surface control, must be set if {@link #setSurfaceOnly(boolean)} is set.
1215              */
setSurfaceControl(SurfaceControl surfaceControl)1216             private Builder setSurfaceControl(SurfaceControl surfaceControl) {
1217                 mAttrSurfaceControl = surfaceControl;
1218                 return this;
1219             }
1220 
1221             /**
1222              * Indicates if the instrument should be deferred to the next frame.
1223              * @param defer true if the instrument should be deferred to the next frame.
1224              * @return builder
1225              */
setDeferMonitorForAnimationStart(boolean defer)1226             public Builder setDeferMonitorForAnimationStart(boolean defer) {
1227                 mAttrDeferMonitor = defer;
1228                 return this;
1229             }
1230 
1231             /**
1232              * Builds the {@link Configuration} instance
1233              * @return the instance of {@link Configuration}
1234              * @throws IllegalArgumentException if any invalid attribute is set
1235              */
build()1236             public Configuration build() throws IllegalArgumentException {
1237                 return new Configuration(
1238                         mAttrCujType, mAttrView, mAttrTag, mAttrTimeout,
1239                         mAttrSurfaceOnly, mAttrContext, mAttrSurfaceControl,
1240                         mAttrDeferMonitor);
1241             }
1242         }
1243 
Configuration(@ujType int cuj, View view, String tag, long timeout, boolean surfaceOnly, Context context, SurfaceControl surfaceControl, boolean deferMonitor)1244         private Configuration(@CujType int cuj, View view, String tag, long timeout,
1245                 boolean surfaceOnly, Context context, SurfaceControl surfaceControl,
1246                 boolean deferMonitor) {
1247             mCujType = cuj;
1248             mTag = tag;
1249             mTimeout = timeout;
1250             mView = view;
1251             mSurfaceOnly = surfaceOnly;
1252             mContext = context != null
1253                     ? context
1254                     : (view != null ? view.getContext().getApplicationContext() : null);
1255             mSurfaceControl = surfaceControl;
1256             mDeferMonitor = deferMonitor;
1257             validate();
1258             mHandler = mSurfaceOnly ? mContext.getMainThreadHandler() : mView.getHandler();
1259         }
1260 
validate()1261         private void validate() {
1262             boolean shouldThrow = false;
1263             final StringBuilder msg = new StringBuilder();
1264 
1265             if (mTag == null) {
1266                 shouldThrow = true;
1267                 msg.append("Invalid tag; ");
1268             }
1269             if (mTimeout < 0) {
1270                 shouldThrow = true;
1271                 msg.append("Invalid timeout value; ");
1272             }
1273             if (mSurfaceOnly) {
1274                 if (mContext == null) {
1275                     shouldThrow = true;
1276                     msg.append("Must pass in a context if only instrument surface; ");
1277                 }
1278                 if (mSurfaceControl == null || !mSurfaceControl.isValid()) {
1279                     shouldThrow = true;
1280                     msg.append("Must pass in a valid surface control if only instrument surface; ");
1281                 }
1282             } else {
1283                 if (!hasValidView()) {
1284                     shouldThrow = true;
1285                     boolean attached = false;
1286                     boolean hasViewRoot = false;
1287                     boolean hasRenderer = false;
1288                     if (mView != null) {
1289                         attached = mView.isAttachedToWindow();
1290                         hasViewRoot = mView.getViewRootImpl() != null;
1291                         hasRenderer = mView.getThreadedRenderer() != null;
1292                     }
1293                     String err = "invalid view: view=" + mView + ", attached=" + attached
1294                             + ", hasViewRoot=" + hasViewRoot + ", hasRenderer=" + hasRenderer;
1295                     msg.append(err);
1296                 }
1297             }
1298             if (shouldThrow) {
1299                 throw new IllegalArgumentException(msg.toString());
1300             }
1301         }
1302 
hasValidView()1303         boolean hasValidView() {
1304             return mSurfaceOnly
1305                     || (mView != null && mView.isAttachedToWindow()
1306                     && mView.getViewRootImpl() != null && mView.getThreadedRenderer() != null);
1307         }
1308 
1309         /**
1310          * @return true if only instrumenting surface, false otherwise
1311          */
isSurfaceOnly()1312         public boolean isSurfaceOnly() {
1313             return mSurfaceOnly;
1314         }
1315 
1316         /**
1317          * @return the surafce control which is instrumenting
1318          */
getSurfaceControl()1319         public SurfaceControl getSurfaceControl() {
1320             return mSurfaceControl;
1321         }
1322 
1323         @VisibleForTesting
1324         /**
1325          * @return a view which is attached to the view tree.
1326          */
getView()1327         public View getView() {
1328             return mView;
1329         }
1330 
1331         /**
1332          * @return true if the monitoring should be deferred to the next frame, false otherwise.
1333          */
shouldDeferMonitor()1334         public boolean shouldDeferMonitor() {
1335             return mDeferMonitor;
1336         }
1337 
1338         @VisibleForTesting
getHandler()1339         public Handler getHandler() {
1340             return mHandler;
1341         }
1342 
1343         /**
1344          * @return the ID of the display this interaction in on.
1345          */
1346         @VisibleForTesting
getDisplayId()1347         public int getDisplayId() {
1348             return (mSurfaceOnly ? mContext : mView.getContext()).getDisplayId();
1349         }
1350     }
1351 
1352     /**
1353      * A class to represent a session.
1354      */
1355     public static class Session {
1356         @CujType
1357         private final int mCujType;
1358         private final long mTimeStamp;
1359         @Reasons
1360         private int mReason = REASON_END_UNKNOWN;
1361         private final String mName;
1362 
Session(@ujType int cujType, @NonNull String postfix)1363         public Session(@CujType int cujType, @NonNull String postfix) {
1364             mCujType = cujType;
1365             mTimeStamp = System.nanoTime();
1366             mName = generateSessionName(getNameOfCuj(cujType), postfix);
1367         }
1368 
generateSessionName(@onNull String cujName, @NonNull String cujPostfix)1369         private String generateSessionName(@NonNull String cujName, @NonNull String cujPostfix) {
1370             final boolean hasPostfix = !TextUtils.isEmpty(cujPostfix);
1371             // We assert that the cujName shouldn't exceed MAX_LENGTH_OF_CUJ_NAME.
1372             if (cujName.length() > MAX_LENGTH_OF_CUJ_NAME) {
1373                 throw new IllegalArgumentException(TextUtils.formatSimple(
1374                         "The length of cuj name <%s> exceeds %d", cujName, MAX_LENGTH_OF_CUJ_NAME));
1375             }
1376             if (hasPostfix) {
1377                 final int remaining = MAX_LENGTH_SESSION_NAME - cujName.length();
1378                 if (cujPostfix.length() > remaining) {
1379                     cujPostfix = cujPostfix.substring(0, remaining - 3).concat("...");
1380                 }
1381             }
1382             // The max length of the whole string should be:
1383             // 105 with postfix, 83 without postfix
1384             return hasPostfix
1385                     ? TextUtils.formatSimple("J<%s::%s>", cujName, cujPostfix)
1386                     : TextUtils.formatSimple("J<%s>", cujName);
1387         }
1388 
1389         @CujType
getCuj()1390         public int getCuj() {
1391             return mCujType;
1392         }
1393 
getStatsdInteractionType()1394         public int getStatsdInteractionType() {
1395             return CUJ_TO_STATSD_INTERACTION_TYPE[mCujType];
1396         }
1397 
1398         /** Describes whether the measurement from this session should be written to statsd. */
logToStatsd()1399         public boolean logToStatsd() {
1400             return getStatsdInteractionType() != NO_STATSD_LOGGING;
1401         }
1402 
getPerfettoTrigger()1403         public String getPerfettoTrigger() {
1404             return String.format(Locale.US, "com.android.telemetry.interaction-jank-monitor-%d",
1405                     mCujType);
1406         }
1407 
getName()1408         public String getName() {
1409             return mName;
1410         }
1411 
getTimeStamp()1412         public long getTimeStamp() {
1413             return mTimeStamp;
1414         }
1415 
setReason(@easons int reason)1416         public void setReason(@Reasons int reason) {
1417             mReason = reason;
1418         }
1419 
1420         @Reasons
getReason()1421         public int getReason() {
1422             return mReason;
1423         }
1424     }
1425 
1426     @FunctionalInterface
1427     private interface TimeFunction {
invoke(long unixNanos, long elapsedNanos, long realtimeNanos)1428         void invoke(long unixNanos, long elapsedNanos, long realtimeNanos);
1429     }
1430 
postEventLogToWorkerThread(TimeFunction logFunction)1431     private void postEventLogToWorkerThread(TimeFunction logFunction) {
1432         final Instant now = Instant.now();
1433         final long unixNanos = TimeUnit.NANOSECONDS.convert(now.getEpochSecond(), TimeUnit.SECONDS)
1434                 + now.getNano();
1435         final long elapsedNanos = SystemClock.elapsedRealtimeNanos();
1436         final long realtimeNanos = SystemClock.uptimeNanos();
1437 
1438         mWorker.getThreadHandler().post(() -> {
1439             logFunction.invoke(unixNanos, elapsedNanos, realtimeNanos);
1440         });
1441     }
1442 }
1443