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