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.content.Intent.FLAG_RECEIVER_REGISTERED_ONLY; 20 21 import static com.android.internal.jank.FrameTracker.REASON_CANCEL_NORMAL; 22 import static com.android.internal.jank.FrameTracker.REASON_CANCEL_NOT_BEGUN; 23 import static com.android.internal.jank.FrameTracker.REASON_CANCEL_TIMEOUT; 24 import static com.android.internal.jank.FrameTracker.REASON_END_NORMAL; 25 import static com.android.internal.jank.FrameTracker.REASON_END_UNKNOWN; 26 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_ALL_APPS_SCROLL; 27 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_CLOSE_TO_HOME; 28 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_CLOSE_TO_PIP; 29 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_LAUNCH_FROM_ICON; 30 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_LAUNCH_FROM_RECENTS; 31 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_LAUNCH_FROM_WIDGET; 32 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_OPEN_ALL_APPS; 33 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_QUICK_SWITCH; 34 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PASSWORD_APPEAR; 35 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PASSWORD_DISAPPEAR; 36 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PATTERN_APPEAR; 37 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PATTERN_DISAPPEAR; 38 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PIN_APPEAR; 39 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PIN_DISAPPEAR; 40 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_TRANSITION_FROM_AOD; 41 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_TRANSITION_TO_AOD; 42 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_UNLOCK_ANIMATION; 43 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__NOTIFICATION_SHADE_SWIPE; 44 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__PIP_TRANSITION; 45 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SETTINGS_PAGE_SCROLL; 46 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH; 47 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_HISTORY_BUTTON; 48 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_MEDIA_PLAYER; 49 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_QS_TILE; 50 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON; 51 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_EXPAND_COLLAPSE_LOCK; 52 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_HEADS_UP_APPEAR; 53 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_HEADS_UP_DISAPPEAR; 54 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_NOTIFICATION_ADD; 55 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_NOTIFICATION_REMOVE; 56 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_QS_EXPAND_COLLAPSE; 57 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_QS_SCROLL_SWIPE; 58 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_ROW_EXPAND; 59 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_ROW_SWIPE; 60 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_SCROLL_FLING; 61 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SPLASHSCREEN_AVD; 62 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SPLASHSCREEN_EXIT_ANIM; 63 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP; 64 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__USER_SWITCH; 65 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__WALLPAPER_TRANSITION; 66 67 import android.annotation.IntDef; 68 import android.annotation.NonNull; 69 import android.content.Context; 70 import android.content.Intent; 71 import android.os.Build; 72 import android.os.HandlerExecutor; 73 import android.os.HandlerThread; 74 import android.os.SystemProperties; 75 import android.provider.DeviceConfig; 76 import android.text.TextUtils; 77 import android.util.Log; 78 import android.util.SparseArray; 79 import android.view.Choreographer; 80 import android.view.SurfaceControl; 81 import android.view.View; 82 83 import com.android.internal.annotations.VisibleForTesting; 84 import com.android.internal.jank.FrameTracker.ChoreographerWrapper; 85 import com.android.internal.jank.FrameTracker.FrameMetricsWrapper; 86 import com.android.internal.jank.FrameTracker.FrameTrackerListener; 87 import com.android.internal.jank.FrameTracker.Reasons; 88 import com.android.internal.jank.FrameTracker.SurfaceControlWrapper; 89 import com.android.internal.jank.FrameTracker.ThreadedRendererWrapper; 90 import com.android.internal.jank.FrameTracker.ViewRootWrapper; 91 import com.android.internal.util.PerfettoTrigger; 92 93 import java.lang.annotation.Retention; 94 import java.lang.annotation.RetentionPolicy; 95 import java.util.Locale; 96 import java.util.concurrent.ThreadLocalRandom; 97 import java.util.concurrent.TimeUnit; 98 99 /** 100 * This class let users to begin and end the always on tracing mechanism. 101 * 102 * Enabling for local development: 103 * 104 * adb shell device_config put interaction_jank_monitor enabled true 105 * adb shell device_config put interaction_jank_monitor sampling_interval 1 106 * 107 * @hide 108 */ 109 public class InteractionJankMonitor { 110 private static final String TAG = InteractionJankMonitor.class.getSimpleName(); 111 private static final boolean DEBUG = false; 112 private static final String ACTION_PREFIX = InteractionJankMonitor.class.getCanonicalName(); 113 114 private static final String DEFAULT_WORKER_NAME = TAG + "-Worker"; 115 private static final long DEFAULT_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(2L); 116 private static final String SETTINGS_ENABLED_KEY = "enabled"; 117 private static final String SETTINGS_SAMPLING_INTERVAL_KEY = "sampling_interval"; 118 private static final String SETTINGS_THRESHOLD_MISSED_FRAMES_KEY = 119 "trace_threshold_missed_frames"; 120 private static final String SETTINGS_THRESHOLD_FRAME_TIME_MILLIS_KEY = 121 "trace_threshold_frame_time_millis"; 122 /** Default to being enabled on debug builds. */ 123 private static final boolean DEFAULT_ENABLED = Build.IS_DEBUGGABLE; 124 /** Default to collecting data for all CUJs. */ 125 private static final int DEFAULT_SAMPLING_INTERVAL = 1; 126 /** Default to triggering trace if 3 frames are missed OR a frame takes at least 64ms */ 127 private static final int DEFAULT_TRACE_THRESHOLD_MISSED_FRAMES = 3; 128 private static final int DEFAULT_TRACE_THRESHOLD_FRAME_TIME_MILLIS = 64; 129 130 public static final String ACTION_SESSION_BEGIN = ACTION_PREFIX + ".ACTION_SESSION_BEGIN"; 131 public static final String ACTION_SESSION_END = ACTION_PREFIX + ".ACTION_SESSION_END"; 132 public static final String ACTION_SESSION_CANCEL = ACTION_PREFIX + ".ACTION_SESSION_CANCEL"; 133 public static final String ACTION_METRICS_LOGGED = ACTION_PREFIX + ".ACTION_METRICS_LOGGED"; 134 public static final String BUNDLE_KEY_CUJ_NAME = ACTION_PREFIX + ".CUJ_NAME"; 135 public static final String BUNDLE_KEY_TIMESTAMP = ACTION_PREFIX + ".TIMESTAMP"; 136 @VisibleForTesting 137 public static final String PROP_NOTIFY_CUJ_EVENT = "debug.jank.notify_cuj_events"; 138 139 // Every value must have a corresponding entry in CUJ_STATSD_INTERACTION_TYPE. 140 public static final int CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE = 0; 141 public static final int CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE_LOCK = 1; 142 public static final int CUJ_NOTIFICATION_SHADE_SCROLL_FLING = 2; 143 public static final int CUJ_NOTIFICATION_SHADE_ROW_EXPAND = 3; 144 public static final int CUJ_NOTIFICATION_SHADE_ROW_SWIPE = 4; 145 public static final int CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE = 5; 146 public static final int CUJ_NOTIFICATION_SHADE_QS_SCROLL_SWIPE = 6; 147 public static final int CUJ_LAUNCHER_APP_LAUNCH_FROM_RECENTS = 7; 148 public static final int CUJ_LAUNCHER_APP_LAUNCH_FROM_ICON = 8; 149 public static final int CUJ_LAUNCHER_APP_CLOSE_TO_HOME = 9; 150 public static final int CUJ_LAUNCHER_APP_CLOSE_TO_PIP = 10; 151 public static final int CUJ_LAUNCHER_QUICK_SWITCH = 11; 152 public static final int CUJ_NOTIFICATION_HEADS_UP_APPEAR = 12; 153 public static final int CUJ_NOTIFICATION_HEADS_UP_DISAPPEAR = 13; 154 public static final int CUJ_NOTIFICATION_ADD = 14; 155 public static final int CUJ_NOTIFICATION_REMOVE = 15; 156 public static final int CUJ_NOTIFICATION_APP_START = 16; 157 public static final int CUJ_LOCKSCREEN_PASSWORD_APPEAR = 17; 158 public static final int CUJ_LOCKSCREEN_PATTERN_APPEAR = 18; 159 public static final int CUJ_LOCKSCREEN_PIN_APPEAR = 19; 160 public static final int CUJ_LOCKSCREEN_PASSWORD_DISAPPEAR = 20; 161 public static final int CUJ_LOCKSCREEN_PATTERN_DISAPPEAR = 21; 162 public static final int CUJ_LOCKSCREEN_PIN_DISAPPEAR = 22; 163 public static final int CUJ_LOCKSCREEN_TRANSITION_FROM_AOD = 23; 164 public static final int CUJ_LOCKSCREEN_TRANSITION_TO_AOD = 24; 165 public static final int CUJ_LAUNCHER_OPEN_ALL_APPS = 25; 166 public static final int CUJ_LAUNCHER_ALL_APPS_SCROLL = 26; 167 public static final int CUJ_LAUNCHER_APP_LAUNCH_FROM_WIDGET = 27; 168 public static final int CUJ_SETTINGS_PAGE_SCROLL = 28; 169 public static final int CUJ_LOCKSCREEN_UNLOCK_ANIMATION = 29; 170 public static final int CUJ_SHADE_APP_LAUNCH_FROM_HISTORY_BUTTON = 30; 171 public static final int CUJ_SHADE_APP_LAUNCH_FROM_MEDIA_PLAYER = 31; 172 public static final int CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE = 32; 173 public static final int CUJ_SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON = 33; 174 public static final int CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP = 34; 175 public static final int CUJ_PIP_TRANSITION = 35; 176 public static final int CUJ_WALLPAPER_TRANSITION = 36; 177 public static final int CUJ_USER_SWITCH = 37; 178 public static final int CUJ_SPLASHSCREEN_AVD = 38; 179 public static final int CUJ_SPLASHSCREEN_EXIT_ANIM = 39; 180 181 private static final int NO_STATSD_LOGGING = -1; 182 183 // Used to convert CujType to InteractionType enum value for statsd logging. 184 // Use NO_STATSD_LOGGING in case the measurement for a given CUJ should not be logged to statsd. 185 @VisibleForTesting 186 public static final int[] CUJ_TO_STATSD_INTERACTION_TYPE = { 187 // This should be mapping to CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE. 188 UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__NOTIFICATION_SHADE_SWIPE, 189 UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_EXPAND_COLLAPSE_LOCK, 190 UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_SCROLL_FLING, 191 UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_ROW_EXPAND, 192 UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_ROW_SWIPE, 193 UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_QS_EXPAND_COLLAPSE, 194 UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_QS_SCROLL_SWIPE, 195 UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_LAUNCH_FROM_RECENTS, 196 UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_LAUNCH_FROM_ICON, 197 UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_CLOSE_TO_HOME, 198 UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_CLOSE_TO_PIP, 199 UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_QUICK_SWITCH, 200 UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_HEADS_UP_APPEAR, 201 UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_HEADS_UP_DISAPPEAR, 202 UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_NOTIFICATION_ADD, 203 UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_NOTIFICATION_REMOVE, 204 UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH, 205 UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PASSWORD_APPEAR, 206 UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PATTERN_APPEAR, 207 UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PIN_APPEAR, 208 UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PASSWORD_DISAPPEAR, 209 UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PATTERN_DISAPPEAR, 210 UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PIN_DISAPPEAR, 211 UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_TRANSITION_FROM_AOD, 212 UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_TRANSITION_TO_AOD, 213 UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_OPEN_ALL_APPS, 214 UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_ALL_APPS_SCROLL, 215 UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_LAUNCH_FROM_WIDGET, 216 UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SETTINGS_PAGE_SCROLL, 217 UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_UNLOCK_ANIMATION, 218 UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_HISTORY_BUTTON, 219 UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_MEDIA_PLAYER, 220 UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_QS_TILE, 221 UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON, 222 UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP, 223 UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__PIP_TRANSITION, 224 UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__WALLPAPER_TRANSITION, 225 UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__USER_SWITCH, 226 UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SPLASHSCREEN_AVD, 227 UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SPLASHSCREEN_EXIT_ANIM, 228 }; 229 230 private static volatile InteractionJankMonitor sInstance; 231 232 private final DeviceConfig.OnPropertiesChangedListener mPropertiesChangedListener = 233 this::updateProperties; 234 235 private final FrameMetricsWrapper mMetrics; 236 private final SparseArray<FrameTracker> mRunningTrackers; 237 private final SparseArray<Runnable> mTimeoutActions; 238 private final HandlerThread mWorker; 239 private final Object mLock = new Object(); 240 241 private boolean mEnabled = DEFAULT_ENABLED; 242 private int mSamplingInterval = DEFAULT_SAMPLING_INTERVAL; 243 private int mTraceThresholdMissedFrames = DEFAULT_TRACE_THRESHOLD_MISSED_FRAMES; 244 private int mTraceThresholdFrameTimeMillis = DEFAULT_TRACE_THRESHOLD_FRAME_TIME_MILLIS; 245 246 /** @hide */ 247 @IntDef({ 248 CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, 249 CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE_LOCK, 250 CUJ_NOTIFICATION_SHADE_SCROLL_FLING, 251 CUJ_NOTIFICATION_SHADE_ROW_EXPAND, 252 CUJ_NOTIFICATION_SHADE_ROW_SWIPE, 253 CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE, 254 CUJ_NOTIFICATION_SHADE_QS_SCROLL_SWIPE, 255 CUJ_LAUNCHER_APP_LAUNCH_FROM_RECENTS, 256 CUJ_LAUNCHER_APP_LAUNCH_FROM_ICON, 257 CUJ_LAUNCHER_APP_CLOSE_TO_HOME, 258 CUJ_LAUNCHER_APP_CLOSE_TO_PIP, 259 CUJ_LAUNCHER_QUICK_SWITCH, 260 CUJ_NOTIFICATION_HEADS_UP_APPEAR, 261 CUJ_NOTIFICATION_HEADS_UP_DISAPPEAR, 262 CUJ_NOTIFICATION_ADD, 263 CUJ_NOTIFICATION_REMOVE, 264 CUJ_NOTIFICATION_APP_START, 265 CUJ_LOCKSCREEN_PASSWORD_APPEAR, 266 CUJ_LOCKSCREEN_PATTERN_APPEAR, 267 CUJ_LOCKSCREEN_PIN_APPEAR, 268 CUJ_LOCKSCREEN_PASSWORD_DISAPPEAR, 269 CUJ_LOCKSCREEN_PATTERN_DISAPPEAR, 270 CUJ_LOCKSCREEN_PIN_DISAPPEAR, 271 CUJ_LOCKSCREEN_TRANSITION_FROM_AOD, 272 CUJ_LOCKSCREEN_TRANSITION_TO_AOD, 273 CUJ_LAUNCHER_OPEN_ALL_APPS, 274 CUJ_LAUNCHER_ALL_APPS_SCROLL, 275 CUJ_LAUNCHER_APP_LAUNCH_FROM_WIDGET, 276 CUJ_SETTINGS_PAGE_SCROLL, 277 CUJ_LOCKSCREEN_UNLOCK_ANIMATION, 278 CUJ_SHADE_APP_LAUNCH_FROM_HISTORY_BUTTON, 279 CUJ_SHADE_APP_LAUNCH_FROM_MEDIA_PLAYER, 280 CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE, 281 CUJ_SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON, 282 CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP, 283 CUJ_PIP_TRANSITION, 284 CUJ_WALLPAPER_TRANSITION, 285 CUJ_USER_SWITCH, 286 CUJ_SPLASHSCREEN_AVD, 287 CUJ_SPLASHSCREEN_EXIT_ANIM, 288 }) 289 @Retention(RetentionPolicy.SOURCE) 290 public @interface CujType { 291 } 292 293 /** 294 * Get the singleton of InteractionJankMonitor. 295 * 296 * @return instance of InteractionJankMonitor 297 */ getInstance()298 public static InteractionJankMonitor getInstance() { 299 // Use DCL here since this method might be invoked very often. 300 if (sInstance == null) { 301 synchronized (InteractionJankMonitor.class) { 302 if (sInstance == null) { 303 sInstance = new InteractionJankMonitor(new HandlerThread(DEFAULT_WORKER_NAME)); 304 } 305 } 306 } 307 return sInstance; 308 } 309 310 /** 311 * This constructor should be only public to tests. 312 * 313 * @param worker the worker thread for the callbacks 314 */ 315 @VisibleForTesting InteractionJankMonitor(@onNull HandlerThread worker)316 public InteractionJankMonitor(@NonNull HandlerThread worker) { 317 mRunningTrackers = new SparseArray<>(); 318 mTimeoutActions = new SparseArray<>(); 319 mWorker = worker; 320 mMetrics = new FrameMetricsWrapper(); 321 mWorker.start(); 322 mEnabled = DEFAULT_ENABLED; 323 mSamplingInterval = DEFAULT_SAMPLING_INTERVAL; 324 325 // Post initialization to the background in case we're running on the main 326 // thread. 327 mWorker.getThreadHandler().post( 328 () -> mPropertiesChangedListener.onPropertiesChanged( 329 DeviceConfig.getProperties( 330 DeviceConfig.NAMESPACE_INTERACTION_JANK_MONITOR))); 331 DeviceConfig.addOnPropertiesChangedListener( 332 DeviceConfig.NAMESPACE_INTERACTION_JANK_MONITOR, 333 new HandlerExecutor(mWorker.getThreadHandler()), 334 mPropertiesChangedListener); 335 } 336 getLock()337 Object getLock() { 338 return mLock; 339 } 340 341 /** 342 * Creates a {@link FrameTracker} instance. 343 * 344 * @param config the config used in instrumenting 345 * @param session the session associates with this tracker 346 * @return instance of the FrameTracker 347 */ 348 @VisibleForTesting createFrameTracker(Configuration config, Session session)349 public FrameTracker createFrameTracker(Configuration config, Session session) { 350 final View view = config.mView; 351 final ThreadedRendererWrapper threadedRenderer = 352 view == null ? null : new ThreadedRendererWrapper(view.getThreadedRenderer()); 353 final ViewRootWrapper viewRoot = 354 view == null ? null : new ViewRootWrapper(view.getViewRootImpl()); 355 356 final SurfaceControlWrapper surfaceControl = new SurfaceControlWrapper(); 357 final ChoreographerWrapper choreographer = 358 new ChoreographerWrapper(Choreographer.getInstance()); 359 360 synchronized (mLock) { 361 FrameTrackerListener eventsListener = 362 (s, act) -> handleCujEvents(config.getContext(), act, s); 363 return new FrameTracker(session, mWorker.getThreadHandler(), 364 threadedRenderer, viewRoot, surfaceControl, choreographer, mMetrics, 365 mTraceThresholdMissedFrames, mTraceThresholdFrameTimeMillis, 366 eventsListener, config); 367 } 368 } 369 handleCujEvents(Context context, String action, Session session)370 private void handleCujEvents(Context context, String action, Session session) { 371 // Clear the running and timeout tasks if the end / cancel was fired within the tracker. 372 // Or we might have memory leaks. 373 if (needRemoveTasks(action, session)) { 374 removeTimeout(session.getCuj()); 375 removeTracker(session.getCuj()); 376 } 377 378 // Notify the receivers if necessary. 379 if (session.shouldNotify()) { 380 if (context != null) { 381 notifyEvents(context, action, session); 382 } else { 383 throw new IllegalArgumentException( 384 "Can't notify cuj events due to lack of context: cuj=" 385 + session.getName() + ", action=" + action); 386 } 387 } 388 } 389 needRemoveTasks(String action, Session session)390 private boolean needRemoveTasks(String action, Session session) { 391 final boolean badEnd = action.equals(ACTION_SESSION_END) 392 && session.getReason() != REASON_END_NORMAL; 393 final boolean badCancel = action.equals(ACTION_SESSION_CANCEL) 394 && !(session.getReason() == REASON_CANCEL_NORMAL 395 || session.getReason() == REASON_CANCEL_TIMEOUT); 396 return badEnd || badCancel; 397 } 398 399 /** 400 * Notifies who may interest in some CUJ events. 401 */ 402 @VisibleForTesting notifyEvents(Context context, String action, Session session)403 public void notifyEvents(Context context, String action, Session session) { 404 if (action.equals(ACTION_SESSION_CANCEL) 405 && session.getReason() == REASON_CANCEL_NOT_BEGUN) { 406 return; 407 } 408 Intent intent = new Intent(action); 409 intent.putExtra(BUNDLE_KEY_CUJ_NAME, getNameOfCuj(session.getCuj())); 410 intent.putExtra(BUNDLE_KEY_TIMESTAMP, session.getTimeStamp()); 411 intent.addFlags(FLAG_RECEIVER_REGISTERED_ONLY); 412 context.sendBroadcast(intent); 413 } 414 removeTimeout(@ujType int cujType)415 private void removeTimeout(@CujType int cujType) { 416 synchronized (mLock) { 417 Runnable timeout = mTimeoutActions.get(cujType); 418 if (timeout != null) { 419 mWorker.getThreadHandler().removeCallbacks(timeout); 420 mTimeoutActions.remove(cujType); 421 } 422 } 423 } 424 425 /** 426 * Begins a trace session. 427 * 428 * @param v an attached view. 429 * @param cujType the specific {@link InteractionJankMonitor.CujType}. 430 * @return boolean true if the tracker is started successfully, false otherwise. 431 */ begin(View v, @CujType int cujType)432 public boolean begin(View v, @CujType int cujType) { 433 try { 434 return beginInternal( 435 Configuration.Builder.withView(cujType, v) 436 .build()); 437 } catch (IllegalArgumentException ex) { 438 Log.d(TAG, "Build configuration failed!", ex); 439 return false; 440 } 441 } 442 443 /** 444 * Begins a trace session. 445 * 446 * @param builder the builder of the configurations for instrumenting the CUJ. 447 * @return boolean true if the tracker is started successfully, false otherwise. 448 */ begin(@onNull Configuration.Builder builder)449 public boolean begin(@NonNull Configuration.Builder builder) { 450 try { 451 return beginInternal(builder.build()); 452 } catch (IllegalArgumentException ex) { 453 Log.d(TAG, "Build configuration failed!", ex); 454 return false; 455 } 456 } 457 beginInternal(@onNull Configuration conf)458 private boolean beginInternal(@NonNull Configuration conf) { 459 synchronized (mLock) { 460 int cujType = conf.mCujType; 461 if (!shouldMonitor(cujType)) return false; 462 FrameTracker tracker = getTracker(cujType); 463 // Skip subsequent calls if we already have an ongoing tracing. 464 if (tracker != null) return false; 465 466 // begin a new trace session. 467 tracker = createFrameTracker(conf, new Session(cujType, conf.mTag)); 468 mRunningTrackers.put(cujType, tracker); 469 tracker.begin(); 470 471 // Cancel the trace if we don't get an end() call in specified duration. 472 scheduleTimeoutAction( 473 cujType, conf.mTimeout, () -> cancel(cujType, REASON_CANCEL_TIMEOUT)); 474 return true; 475 } 476 } 477 478 /** 479 * Check if the monitoring is enabled and if it should be sampled. 480 */ 481 @SuppressWarnings("RandomModInteger") 482 @VisibleForTesting shouldMonitor(@ujType int cujType)483 public boolean shouldMonitor(@CujType int cujType) { 484 boolean shouldSample = ThreadLocalRandom.current().nextInt() % mSamplingInterval == 0; 485 if (!mEnabled || !shouldSample) { 486 if (DEBUG) { 487 Log.d(TAG, "Skip monitoring cuj: " + getNameOfCuj(cujType) 488 + ", enable=" + mEnabled + ", debuggable=" + DEFAULT_ENABLED 489 + ", sample=" + shouldSample + ", interval=" + mSamplingInterval); 490 } 491 return false; 492 } 493 return true; 494 } 495 496 /** 497 * Schedules a timeout action. 498 * @param cuj cuj type 499 * @param timeout duration to timeout 500 * @param action action once timeout 501 */ 502 @VisibleForTesting scheduleTimeoutAction(@ujType int cuj, long timeout, Runnable action)503 public void scheduleTimeoutAction(@CujType int cuj, long timeout, Runnable action) { 504 mTimeoutActions.put(cuj, action); 505 mWorker.getThreadHandler().postDelayed(action, timeout); 506 } 507 508 /** 509 * Ends a trace session. 510 * 511 * @param cujType the specific {@link InteractionJankMonitor.CujType}. 512 * @return boolean true if the tracker is ended successfully, false otherwise. 513 */ end(@ujType int cujType)514 public boolean end(@CujType int cujType) { 515 synchronized (mLock) { 516 // remove the timeout action first. 517 removeTimeout(cujType); 518 FrameTracker tracker = getTracker(cujType); 519 // Skip this call since we haven't started a trace yet. 520 if (tracker == null) return false; 521 // if the end call doesn't return true, another thread is handling end of the cuj. 522 if (tracker.end(REASON_END_NORMAL)) { 523 removeTracker(cujType); 524 } 525 return true; 526 } 527 } 528 529 /** 530 * Cancels the trace session. 531 * 532 * @return boolean true if the tracker is cancelled successfully, false otherwise. 533 */ cancel(@ujType int cujType)534 public boolean cancel(@CujType int cujType) { 535 return cancel(cujType, REASON_CANCEL_NORMAL); 536 } 537 538 /** 539 * Cancels the trace session. 540 * 541 * @return boolean true if the tracker is cancelled successfully, false otherwise. 542 */ 543 @VisibleForTesting cancel(@ujType int cujType, @Reasons int reason)544 public boolean cancel(@CujType int cujType, @Reasons int reason) { 545 synchronized (mLock) { 546 // remove the timeout action first. 547 removeTimeout(cujType); 548 FrameTracker tracker = getTracker(cujType); 549 // Skip this call since we haven't started a trace yet. 550 if (tracker == null) return false; 551 // if the cancel call doesn't return true, another thread is handling cancel of the cuj. 552 if (tracker.cancel(reason)) { 553 removeTracker(cujType); 554 } 555 return true; 556 } 557 } 558 getTracker(@ujType int cuj)559 private FrameTracker getTracker(@CujType int cuj) { 560 return mRunningTrackers.get(cuj); 561 } 562 removeTracker(@ujType int cuj)563 private void removeTracker(@CujType int cuj) { 564 mRunningTrackers.remove(cuj); 565 } 566 updateProperties(DeviceConfig.Properties properties)567 private void updateProperties(DeviceConfig.Properties properties) { 568 synchronized (mLock) { 569 mSamplingInterval = properties.getInt(SETTINGS_SAMPLING_INTERVAL_KEY, 570 DEFAULT_SAMPLING_INTERVAL); 571 mEnabled = properties.getBoolean(SETTINGS_ENABLED_KEY, DEFAULT_ENABLED); 572 mTraceThresholdMissedFrames = properties.getInt(SETTINGS_THRESHOLD_MISSED_FRAMES_KEY, 573 DEFAULT_TRACE_THRESHOLD_MISSED_FRAMES); 574 mTraceThresholdFrameTimeMillis = properties.getInt( 575 SETTINGS_THRESHOLD_FRAME_TIME_MILLIS_KEY, 576 DEFAULT_TRACE_THRESHOLD_FRAME_TIME_MILLIS); 577 } 578 } 579 580 @VisibleForTesting getPropertiesChangedListener()581 public DeviceConfig.OnPropertiesChangedListener getPropertiesChangedListener() { 582 return mPropertiesChangedListener; 583 } 584 585 /** 586 * Triggers the perfetto daemon to collect and upload data. 587 */ 588 @VisibleForTesting trigger(Session session)589 public void trigger(Session session) { 590 mWorker.getThreadHandler().post( 591 () -> PerfettoTrigger.trigger(session.getPerfettoTrigger())); 592 } 593 594 /** 595 * A helper method to translate interaction type to CUJ name. 596 * 597 * @param interactionType the interaction type defined in AtomsProto.java 598 * @return the name of the interaction type 599 */ getNameOfInteraction(int interactionType)600 public static String getNameOfInteraction(int interactionType) { 601 // There is an offset amount of 1 between cujType and interactionType. 602 return getNameOfCuj(interactionType - 1); 603 } 604 605 /** 606 * A helper method to translate CUJ type to CUJ name. 607 * 608 * @param cujType the cuj type defined in this file 609 * @return the name of the cuj type 610 */ getNameOfCuj(int cujType)611 public static String getNameOfCuj(int cujType) { 612 switch (cujType) { 613 case CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE: 614 return "SHADE_EXPAND_COLLAPSE"; 615 case CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE_LOCK: 616 return "SHADE_EXPAND_COLLAPSE_LOCK"; 617 case CUJ_NOTIFICATION_SHADE_SCROLL_FLING: 618 return "SHADE_SCROLL_FLING"; 619 case CUJ_NOTIFICATION_SHADE_ROW_EXPAND: 620 return "SHADE_ROW_EXPAND"; 621 case CUJ_NOTIFICATION_SHADE_ROW_SWIPE: 622 return "SHADE_ROW_SWIPE"; 623 case CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE: 624 return "SHADE_QS_EXPAND_COLLAPSE"; 625 case CUJ_NOTIFICATION_SHADE_QS_SCROLL_SWIPE: 626 return "SHADE_QS_SCROLL_SWIPE"; 627 case CUJ_LAUNCHER_APP_LAUNCH_FROM_RECENTS: 628 return "LAUNCHER_APP_LAUNCH_FROM_RECENTS"; 629 case CUJ_LAUNCHER_APP_LAUNCH_FROM_ICON: 630 return "LAUNCHER_APP_LAUNCH_FROM_ICON"; 631 case CUJ_LAUNCHER_APP_CLOSE_TO_HOME: 632 return "LAUNCHER_APP_CLOSE_TO_HOME"; 633 case CUJ_LAUNCHER_APP_CLOSE_TO_PIP: 634 return "LAUNCHER_APP_CLOSE_TO_PIP"; 635 case CUJ_LAUNCHER_QUICK_SWITCH: 636 return "LAUNCHER_QUICK_SWITCH"; 637 case CUJ_NOTIFICATION_HEADS_UP_APPEAR: 638 return "NOTIFICATION_HEADS_UP_APPEAR"; 639 case CUJ_NOTIFICATION_HEADS_UP_DISAPPEAR: 640 return "NOTIFICATION_HEADS_UP_DISAPPEAR"; 641 case CUJ_NOTIFICATION_ADD: 642 return "NOTIFICATION_ADD"; 643 case CUJ_NOTIFICATION_REMOVE: 644 return "NOTIFICATION_REMOVE"; 645 case CUJ_NOTIFICATION_APP_START: 646 return "NOTIFICATION_APP_START"; 647 case CUJ_LOCKSCREEN_PASSWORD_APPEAR: 648 return "LOCKSCREEN_PASSWORD_APPEAR"; 649 case CUJ_LOCKSCREEN_PATTERN_APPEAR: 650 return "LOCKSCREEN_PATTERN_APPEAR"; 651 case CUJ_LOCKSCREEN_PIN_APPEAR: 652 return "LOCKSCREEN_PIN_APPEAR"; 653 case CUJ_LOCKSCREEN_PASSWORD_DISAPPEAR: 654 return "LOCKSCREEN_PASSWORD_DISAPPEAR"; 655 case CUJ_LOCKSCREEN_PATTERN_DISAPPEAR: 656 return "LOCKSCREEN_PATTERN_DISAPPEAR"; 657 case CUJ_LOCKSCREEN_PIN_DISAPPEAR: 658 return "LOCKSCREEN_PIN_DISAPPEAR"; 659 case CUJ_LOCKSCREEN_TRANSITION_FROM_AOD: 660 return "LOCKSCREEN_TRANSITION_FROM_AOD"; 661 case CUJ_LOCKSCREEN_TRANSITION_TO_AOD: 662 return "LOCKSCREEN_TRANSITION_TO_AOD"; 663 case CUJ_LAUNCHER_OPEN_ALL_APPS : 664 return "LAUNCHER_OPEN_ALL_APPS"; 665 case CUJ_LAUNCHER_ALL_APPS_SCROLL: 666 return "LAUNCHER_ALL_APPS_SCROLL"; 667 case CUJ_LAUNCHER_APP_LAUNCH_FROM_WIDGET: 668 return "LAUNCHER_APP_LAUNCH_FROM_WIDGET"; 669 case CUJ_SETTINGS_PAGE_SCROLL: 670 return "SETTINGS_PAGE_SCROLL"; 671 case CUJ_LOCKSCREEN_UNLOCK_ANIMATION: 672 return "LOCKSCREEN_UNLOCK_ANIMATION"; 673 case CUJ_SHADE_APP_LAUNCH_FROM_HISTORY_BUTTON: 674 return "SHADE_APP_LAUNCH_FROM_HISTORY_BUTTON"; 675 case CUJ_SHADE_APP_LAUNCH_FROM_MEDIA_PLAYER: 676 return "SHADE_APP_LAUNCH_FROM_MEDIA_PLAYER"; 677 case CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE: 678 return "SHADE_APP_LAUNCH_FROM_QS_TILE"; 679 case CUJ_SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON: 680 return "SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON"; 681 case CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP: 682 return "STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP"; 683 case CUJ_PIP_TRANSITION: 684 return "PIP_TRANSITION"; 685 case CUJ_WALLPAPER_TRANSITION: 686 return "WALLPAPER_TRANSITION"; 687 case CUJ_USER_SWITCH: 688 return "USER_SWITCH"; 689 case CUJ_SPLASHSCREEN_AVD: 690 return "SPLASHSCREEN_AVD"; 691 case CUJ_SPLASHSCREEN_EXIT_ANIM: 692 return "SPLASHSCREEN_EXIT_ANIM"; 693 } 694 return "UNKNOWN"; 695 } 696 697 /** 698 * Configurations used while instrumenting the CUJ. <br/> 699 * <b>It may refer to an attached view, don't use static reference for any purpose.</b> 700 */ 701 public static class Configuration { 702 private final View mView; 703 private final Context mContext; 704 private final long mTimeout; 705 private final String mTag; 706 private final boolean mSurfaceOnly; 707 private final SurfaceControl mSurfaceControl; 708 private final @CujType int mCujType; 709 710 /** 711 * A builder for building Configuration. {@link #setView(View)} is essential 712 * if {@link #setSurfaceOnly(boolean)} is not set, otherwise both 713 * {@link #setSurfaceControl(SurfaceControl)} and {@link #setContext(Context)} 714 * are necessary<br/> 715 * <b>It may refer to an attached view, don't use static reference for any purpose.</b> 716 */ 717 public static class Builder { 718 private View mAttrView = null; 719 private Context mAttrContext = null; 720 private long mAttrTimeout = DEFAULT_TIMEOUT_MS; 721 private String mAttrTag = ""; 722 private boolean mAttrSurfaceOnly; 723 private SurfaceControl mAttrSurfaceControl; 724 private @CujType int mAttrCujType; 725 726 /** 727 * Creates a builder which instruments only surface. 728 * @param cuj The enum defined in {@link InteractionJankMonitor.CujType}. 729 * @param context context 730 * @param surfaceControl surface control 731 * @return builder 732 */ withSurface(@ujType int cuj, @NonNull Context context, @NonNull SurfaceControl surfaceControl)733 public static Builder withSurface(@CujType int cuj, @NonNull Context context, 734 @NonNull SurfaceControl surfaceControl) { 735 return new Builder(cuj) 736 .setContext(context) 737 .setSurfaceControl(surfaceControl) 738 .setSurfaceOnly(true); 739 } 740 741 /** 742 * Creates a builder which instruments both surface and view. 743 * @param cuj The enum defined in {@link InteractionJankMonitor.CujType}. 744 * @param view view 745 * @return builder 746 */ withView(@ujType int cuj, @NonNull View view)747 public static Builder withView(@CujType int cuj, @NonNull View view) { 748 return new Builder(cuj).setView(view) 749 .setContext(view.getContext()); 750 } 751 Builder(@ujType int cuj)752 private Builder(@CujType int cuj) { 753 mAttrCujType = cuj; 754 } 755 756 /** 757 * Specifies a view, must be set if {@link #setSurfaceOnly(boolean)} is set to false. 758 * @param view an attached view 759 * @return builder 760 */ setView(@onNull View view)761 private Builder setView(@NonNull View view) { 762 mAttrView = view; 763 return this; 764 } 765 766 /** 767 * @param timeout duration to cancel the instrumentation in ms 768 * @return builder 769 */ setTimeout(long timeout)770 public Builder setTimeout(long timeout) { 771 mAttrTimeout = timeout; 772 return this; 773 } 774 775 /** 776 * @param tag The postfix of the CUJ in the output trace. 777 * It provides a brief description for the CUJ like the concrete class 778 * who is dealing with the CUJ or the important state with the CUJ, etc. 779 * @return builder 780 */ setTag(@onNull String tag)781 public Builder setTag(@NonNull String tag) { 782 mAttrTag = tag; 783 return this; 784 } 785 786 /** 787 * Indicates if only instrument with surface, 788 * if true, must also setup with {@link #setContext(Context)} 789 * and {@link #setSurfaceControl(SurfaceControl)}. 790 * @param surfaceOnly true if only instrument with surface, false otherwise 791 * @return builder Surface only builder. 792 */ setSurfaceOnly(boolean surfaceOnly)793 private Builder setSurfaceOnly(boolean surfaceOnly) { 794 mAttrSurfaceOnly = surfaceOnly; 795 return this; 796 } 797 798 /** 799 * Specifies a context, must set if {@link #setSurfaceOnly(boolean)} is set. 800 */ setContext(Context context)801 private Builder setContext(Context context) { 802 mAttrContext = context; 803 return this; 804 } 805 806 /** 807 * Specifies a surface control, must be set if {@link #setSurfaceOnly(boolean)} is set. 808 */ setSurfaceControl(SurfaceControl surfaceControl)809 private Builder setSurfaceControl(SurfaceControl surfaceControl) { 810 mAttrSurfaceControl = surfaceControl; 811 return this; 812 } 813 814 /** 815 * Builds the {@link Configuration} instance 816 * @return the instance of {@link Configuration} 817 * @throws IllegalArgumentException if any invalid attribute is set 818 */ build()819 public Configuration build() throws IllegalArgumentException { 820 return new Configuration( 821 mAttrCujType, mAttrView, mAttrTag, mAttrTimeout, 822 mAttrSurfaceOnly, mAttrContext, mAttrSurfaceControl); 823 } 824 } 825 Configuration(@ujType int cuj, View view, String tag, long timeout, boolean surfaceOnly, Context context, SurfaceControl surfaceControl)826 private Configuration(@CujType int cuj, View view, String tag, long timeout, 827 boolean surfaceOnly, Context context, SurfaceControl surfaceControl) { 828 mCujType = cuj; 829 mTag = tag; 830 mTimeout = timeout; 831 mView = view; 832 mSurfaceOnly = surfaceOnly; 833 mContext = context != null 834 ? context 835 : (view != null ? view.getContext().getApplicationContext() : null); 836 mSurfaceControl = surfaceControl; 837 validate(); 838 } 839 validate()840 private void validate() { 841 boolean shouldThrow = false; 842 final StringBuilder msg = new StringBuilder(); 843 844 if (mTag == null) { 845 shouldThrow = true; 846 msg.append("Invalid tag; "); 847 } 848 if (mTimeout < 0) { 849 shouldThrow = true; 850 msg.append("Invalid timeout value; "); 851 } 852 if (mSurfaceOnly) { 853 if (mContext == null) { 854 shouldThrow = true; 855 msg.append("Must pass in a context if only instrument surface; "); 856 } 857 if (mSurfaceControl == null || !mSurfaceControl.isValid()) { 858 shouldThrow = true; 859 msg.append("Must pass in a valid surface control if only instrument surface; "); 860 } 861 } else { 862 if (mView == null || !mView.isAttachedToWindow()) { 863 shouldThrow = true; 864 msg.append("Null view or unattached view while instrumenting view; "); 865 } 866 } 867 if (shouldThrow) { 868 throw new IllegalArgumentException(msg.toString()); 869 } 870 } 871 872 /** 873 * @return true if only instrumenting surface, false otherwise 874 */ isSurfaceOnly()875 public boolean isSurfaceOnly() { 876 return mSurfaceOnly; 877 } 878 879 /** 880 * @return the surafce control which is instrumenting 881 */ getSurfaceControl()882 public SurfaceControl getSurfaceControl() { 883 return mSurfaceControl; 884 } 885 getView()886 View getView() { 887 return mView; 888 } 889 getContext()890 Context getContext() { 891 return mContext; 892 } 893 } 894 895 /** 896 * A class to represent a session. 897 */ 898 public static class Session { 899 @CujType 900 private final int mCujType; 901 private final long mTimeStamp; 902 @Reasons 903 private int mReason = REASON_END_UNKNOWN; 904 private final boolean mShouldNotify; 905 private final String mName; 906 Session(@ujType int cujType, @NonNull String postfix)907 public Session(@CujType int cujType, @NonNull String postfix) { 908 mCujType = cujType; 909 mTimeStamp = System.nanoTime(); 910 mShouldNotify = SystemProperties.getBoolean(PROP_NOTIFY_CUJ_EVENT, false); 911 mName = TextUtils.isEmpty(postfix) 912 ? String.format("J<%s>", getNameOfCuj(mCujType)) 913 : String.format("J<%s::%s>", getNameOfCuj(mCujType), postfix); 914 } 915 916 @CujType getCuj()917 public int getCuj() { 918 return mCujType; 919 } 920 getStatsdInteractionType()921 public int getStatsdInteractionType() { 922 return CUJ_TO_STATSD_INTERACTION_TYPE[mCujType]; 923 } 924 925 /** Describes whether the measurement from this session should be written to statsd. */ logToStatsd()926 public boolean logToStatsd() { 927 return getStatsdInteractionType() != NO_STATSD_LOGGING; 928 } 929 getPerfettoTrigger()930 public String getPerfettoTrigger() { 931 return String.format(Locale.US, "com.android.telemetry.interaction-jank-monitor-%d", 932 mCujType); 933 } 934 getName()935 public String getName() { 936 return mName; 937 } 938 getTimeStamp()939 public long getTimeStamp() { 940 return mTimeStamp; 941 } 942 setReason(@easons int reason)943 public void setReason(@Reasons int reason) { 944 mReason = reason; 945 } 946 getReason()947 public @Reasons int getReason() { 948 return mReason; 949 } 950 951 /** Determines if should notify the receivers of cuj events */ shouldNotify()952 public boolean shouldNotify() { 953 return mShouldNotify; 954 } 955 } 956 } 957