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