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.view.SurfaceControl.JankData.JANK_APP_DEADLINE_MISSED; 20 import static android.view.SurfaceControl.JankData.JANK_NONE; 21 import static android.view.SurfaceControl.JankData.JANK_SURFACEFLINGER_DEADLINE_MISSED; 22 23 import static com.android.internal.jank.FrameTracker.SurfaceControlWrapper; 24 import static com.android.internal.jank.FrameTracker.ViewRootWrapper; 25 import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE; 26 import static com.android.internal.jank.InteractionJankMonitor.CUJ_TO_STATSD_INTERACTION_TYPE; 27 import static com.android.internal.jank.InteractionJankMonitor.CUJ_WALLPAPER_TRANSITION; 28 import static com.android.internal.util.FrameworkStatsLog.UI_INTERACTION_FRAME_INFO_REPORTED; 29 30 import static com.google.common.truth.Truth.assertThat; 31 32 import static org.mockito.ArgumentMatchers.any; 33 import static org.mockito.ArgumentMatchers.eq; 34 import static org.mockito.Mockito.doNothing; 35 import static org.mockito.Mockito.doReturn; 36 import static org.mockito.Mockito.mock; 37 import static org.mockito.Mockito.never; 38 import static org.mockito.Mockito.only; 39 import static org.mockito.Mockito.spy; 40 import static org.mockito.Mockito.verify; 41 import static org.mockito.Mockito.when; 42 43 import android.os.Handler; 44 import android.view.FrameMetrics; 45 import android.view.SurfaceControl; 46 import android.view.SurfaceControl.JankData; 47 import android.view.SurfaceControl.JankData.JankType; 48 import android.view.SurfaceControl.OnJankDataListener; 49 import android.view.View; 50 import android.view.ViewAttachTestActivity; 51 52 import androidx.test.filters.SmallTest; 53 import androidx.test.rule.ActivityTestRule; 54 55 import com.android.internal.jank.FrameTracker.ChoreographerWrapper; 56 import com.android.internal.jank.FrameTracker.FrameMetricsWrapper; 57 import com.android.internal.jank.FrameTracker.StatsLogWrapper; 58 import com.android.internal.jank.FrameTracker.ThreadedRendererWrapper; 59 import com.android.internal.jank.InteractionJankMonitor.Configuration; 60 import com.android.internal.jank.InteractionJankMonitor.Session; 61 62 import org.junit.Before; 63 import org.junit.Rule; 64 import org.junit.Test; 65 import org.mockito.ArgumentCaptor; 66 import org.mockito.Mockito; 67 68 import java.util.concurrent.TimeUnit; 69 70 @SmallTest 71 public class FrameTrackerTest { 72 private static final String CUJ_POSTFIX = ""; 73 private ViewAttachTestActivity mActivity; 74 75 @Rule 76 public ActivityTestRule<ViewAttachTestActivity> mRule = 77 new ActivityTestRule<>(ViewAttachTestActivity.class); 78 79 private ThreadedRendererWrapper mRenderer; 80 private FrameMetricsWrapper mWrapper; 81 private SurfaceControlWrapper mSurfaceControlWrapper; 82 private ViewRootWrapper mViewRootWrapper; 83 private ChoreographerWrapper mChoreographer; 84 private StatsLogWrapper mStatsLog; 85 private ArgumentCaptor<OnJankDataListener> mListenerCapture; 86 private SurfaceControl mSurfaceControl; 87 private ArgumentCaptor<Runnable> mRunnableArgumentCaptor; 88 89 @Before setup()90 public void setup() { 91 // Prepare an activity for getting ThreadedRenderer later. 92 mActivity = mRule.getActivity(); 93 View view = mActivity.getWindow().getDecorView(); 94 assertThat(view.isAttachedToWindow()).isTrue(); 95 96 mWrapper = Mockito.spy(new FrameMetricsWrapper()); 97 mRenderer = Mockito.spy(new ThreadedRendererWrapper(view.getThreadedRenderer())); 98 doNothing().when(mRenderer).addObserver(any()); 99 doNothing().when(mRenderer).removeObserver(any()); 100 101 mSurfaceControl = new SurfaceControl.Builder().setName("Surface").build(); 102 mViewRootWrapper = mock(ViewRootWrapper.class); 103 when(mViewRootWrapper.getSurfaceControl()).thenReturn(mSurfaceControl); 104 doNothing().when(mViewRootWrapper).addSurfaceChangedCallback(any()); 105 doNothing().when(mViewRootWrapper).removeSurfaceChangedCallback(any()); 106 mSurfaceControlWrapper = mock(SurfaceControlWrapper.class); 107 108 mListenerCapture = ArgumentCaptor.forClass(OnJankDataListener.class); 109 doNothing().when(mSurfaceControlWrapper).addJankStatsListener( 110 mListenerCapture.capture(), any()); 111 doNothing().when(mSurfaceControlWrapper).removeJankStatsListener( 112 mListenerCapture.capture()); 113 114 mChoreographer = mock(ChoreographerWrapper.class); 115 mStatsLog = mock(StatsLogWrapper.class); 116 mRunnableArgumentCaptor = ArgumentCaptor.forClass(Runnable.class); 117 } 118 spyFrameTracker(int cuj, String postfix, boolean surfaceOnly)119 private FrameTracker spyFrameTracker(int cuj, String postfix, boolean surfaceOnly) { 120 InteractionJankMonitor monitor = mock(InteractionJankMonitor.class); 121 Handler handler = mRule.getActivity().getMainThreadHandler(); 122 Session session = new Session(cuj, postfix); 123 Configuration config = mock(Configuration.class); 124 when(config.isSurfaceOnly()).thenReturn(surfaceOnly); 125 when(config.getSurfaceControl()).thenReturn(mSurfaceControl); 126 when(config.shouldDeferMonitor()).thenReturn(true); 127 View view = mRule.getActivity().getWindow().getDecorView(); 128 Handler spyHandler = spy(new Handler(handler.getLooper())); 129 when(config.getView()).thenReturn(surfaceOnly ? null : view); 130 when(config.getHandler()).thenReturn(spyHandler); 131 FrameTracker frameTracker = Mockito.spy( 132 new FrameTracker(monitor, session, spyHandler, mRenderer, mViewRootWrapper, 133 mSurfaceControlWrapper, mChoreographer, mWrapper, mStatsLog, 134 /* traceThresholdMissedFrames= */ 1, 135 /* traceThresholdFrameTimeMillis= */ -1, 136 /* FrameTrackerListener= */ null, config)); 137 doNothing().when(frameTracker).triggerPerfetto(); 138 doNothing().when(frameTracker).postTraceStartMarker(mRunnableArgumentCaptor.capture()); 139 return frameTracker; 140 } 141 142 @Test testOnlyFirstWindowFrameOverThreshold()143 public void testOnlyFirstWindowFrameOverThreshold() { 144 FrameTracker tracker = spyFrameTracker( 145 CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, CUJ_POSTFIX, /* surfaceOnly= */ false); 146 147 // Just provide current timestamp anytime mWrapper asked for VSYNC_TIMESTAMP 148 when(mWrapper.getMetric(FrameMetrics.VSYNC_TIMESTAMP)) 149 .then(unusedInvocation -> System.nanoTime()); 150 151 when(mChoreographer.getVsyncId()).thenReturn(100L); 152 tracker.begin(); 153 mRunnableArgumentCaptor.getValue().run(); 154 verify(mRenderer, only()).addObserver(any()); 155 156 // send first frame with a long duration - should not be taken into account 157 sendFirstWindowFrame(tracker, 100, JANK_APP_DEADLINE_MISSED, 100L); 158 159 // send another frame with a short duration - should not be considered janky 160 sendFrame(tracker, 5, JANK_NONE, 101L); 161 162 // end the trace session, the last janky frame is after the end() so is discarded. 163 when(mChoreographer.getVsyncId()).thenReturn(102L); 164 tracker.end(FrameTracker.REASON_END_NORMAL); 165 sendFrame(tracker, 5, JANK_NONE, 102L); 166 sendFrame(tracker, 500, JANK_APP_DEADLINE_MISSED, 103L); 167 168 verify(tracker).removeObservers(); 169 verify(tracker, never()).triggerPerfetto(); 170 verify(mStatsLog).write(eq(UI_INTERACTION_FRAME_INFO_REPORTED), 171 eq(CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE]), 172 eq(2L) /* totalFrames */, 173 eq(0L) /* missedFrames */, 174 eq(5000000L) /* maxFrameTimeNanos */, 175 eq(0L) /* missedSfFramesCount */, 176 eq(0L) /* missedAppFramesCount */, 177 eq(0L) /* maxSuccessiveMissedFramesCount */); 178 } 179 180 @Test testSfJank()181 public void testSfJank() { 182 FrameTracker tracker = spyFrameTracker( 183 CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, CUJ_POSTFIX, /* surfaceOnly= */ false); 184 185 when(mChoreographer.getVsyncId()).thenReturn(100L); 186 tracker.begin(); 187 mRunnableArgumentCaptor.getValue().run(); 188 verify(mRenderer, only()).addObserver(any()); 189 190 // send first frame - not janky 191 sendFrame(tracker, 4, JANK_NONE, 100L); 192 193 // send another frame - should be considered janky 194 sendFrame(tracker, 40, JANK_SURFACEFLINGER_DEADLINE_MISSED, 101L); 195 196 // end the trace session 197 when(mChoreographer.getVsyncId()).thenReturn(102L); 198 tracker.end(FrameTracker.REASON_END_NORMAL); 199 sendFrame(tracker, 4, JANK_NONE, 102L); 200 201 verify(tracker).removeObservers(); 202 203 // We detected a janky frame - trigger Perfetto 204 verify(tracker).triggerPerfetto(); 205 206 verify(mStatsLog).write(eq(UI_INTERACTION_FRAME_INFO_REPORTED), 207 eq(CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE]), 208 eq(2L) /* totalFrames */, 209 eq(1L) /* missedFrames */, 210 eq(40000000L) /* maxFrameTimeNanos */, 211 eq(1L) /* missedSfFramesCount */, 212 eq(0L) /* missedAppFramesCount */, 213 eq(1L) /* maxSuccessiveMissedFramesCount */); 214 } 215 216 @Test testFirstFrameJankyNoTrigger()217 public void testFirstFrameJankyNoTrigger() { 218 FrameTracker tracker = spyFrameTracker( 219 CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, CUJ_POSTFIX, /* surfaceOnly= */ false); 220 221 when(mChoreographer.getVsyncId()).thenReturn(100L); 222 tracker.begin(); 223 mRunnableArgumentCaptor.getValue().run(); 224 verify(mRenderer, only()).addObserver(any()); 225 226 // send first frame - janky 227 sendFrame(tracker, 40, JANK_APP_DEADLINE_MISSED, 100L); 228 229 // send another frame - not jank 230 sendFrame(tracker, 4, JANK_NONE, 101L); 231 232 // end the trace session 233 when(mChoreographer.getVsyncId()).thenReturn(102L); 234 tracker.end(FrameTracker.REASON_END_NORMAL); 235 sendFrame(tracker, 4, JANK_NONE, 102L); 236 237 verify(tracker).removeObservers(); 238 239 // We detected a janky frame - trigger Perfetto 240 verify(tracker, never()).triggerPerfetto(); 241 242 verify(mStatsLog).write(eq(UI_INTERACTION_FRAME_INFO_REPORTED), 243 eq(CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE]), 244 eq(2L) /* totalFrames */, 245 eq(0L) /* missedFrames */, 246 eq(4000000L) /* maxFrameTimeNanos */, 247 eq(0L) /* missedSfFramesCount */, 248 eq(0L) /* missedAppFramesCount */, 249 eq(0L) /* maxSuccessiveMissedFramesCount */); 250 } 251 252 @Test testOtherFrameOverThreshold()253 public void testOtherFrameOverThreshold() { 254 FrameTracker tracker = spyFrameTracker( 255 CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, CUJ_POSTFIX, /* surfaceOnly= */ false); 256 257 when(mChoreographer.getVsyncId()).thenReturn(100L); 258 tracker.begin(); 259 mRunnableArgumentCaptor.getValue().run(); 260 verify(mRenderer, only()).addObserver(any()); 261 262 // send first frame - not janky 263 sendFrame(tracker, 4, JANK_NONE, 100L); 264 265 // send another frame - should be considered janky 266 sendFrame(tracker, 40, JANK_APP_DEADLINE_MISSED, 101L); 267 268 // end the trace session 269 when(mChoreographer.getVsyncId()).thenReturn(102L); 270 tracker.end(FrameTracker.REASON_END_NORMAL); 271 sendFrame(tracker, 4, JANK_NONE, 102L); 272 273 verify(tracker).removeObservers(); 274 275 // We detected a janky frame - trigger Perfetto 276 verify(tracker).triggerPerfetto(); 277 278 verify(mStatsLog).write(eq(UI_INTERACTION_FRAME_INFO_REPORTED), 279 eq(CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE]), 280 eq(2L) /* totalFrames */, 281 eq(1L) /* missedFrames */, 282 eq(40000000L) /* maxFrameTimeNanos */, 283 eq(0L) /* missedSfFramesCount */, 284 eq(1L) /* missedAppFramesCount */, 285 eq(1L) /* maxSuccessiveMissedFramesCount */); 286 } 287 288 @Test testLastFrameOverThresholdBeforeEnd()289 public void testLastFrameOverThresholdBeforeEnd() { 290 FrameTracker tracker = spyFrameTracker( 291 CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, CUJ_POSTFIX, /* surfaceOnly= */ false); 292 293 when(mChoreographer.getVsyncId()).thenReturn(100L); 294 tracker.begin(); 295 mRunnableArgumentCaptor.getValue().run(); 296 verify(mRenderer, only()).addObserver(any()); 297 298 // send first frame - not janky 299 sendFrame(tracker, 4, JANK_NONE, 100L); 300 301 // send another frame - not janky 302 sendFrame(tracker, 4, JANK_NONE, 101L); 303 304 // end the trace session, simulate one more valid callback came after the end call. 305 when(mChoreographer.getVsyncId()).thenReturn(102L); 306 tracker.end(FrameTracker.REASON_END_NORMAL); 307 sendFrame(tracker, 50, JANK_APP_DEADLINE_MISSED, 102L); 308 309 // One more callback with VSYNC after the end() vsync id. 310 sendFrame(tracker, 4, JANK_NONE, 103L); 311 312 verify(tracker).removeObservers(); 313 314 // We detected a janky frame - trigger Perfetto 315 verify(tracker).triggerPerfetto(); 316 317 verify(mStatsLog).write(eq(UI_INTERACTION_FRAME_INFO_REPORTED), 318 eq(CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE]), 319 eq(2L) /* totalFrames */, 320 eq(1L) /* missedFrames */, 321 eq(50000000L) /* maxFrameTimeNanos */, 322 eq(0L) /* missedSfFramesCount */, 323 eq(1L) /* missedAppFramesCount */, 324 eq(1L) /* maxSuccessiveMissedFramesCount */); 325 } 326 327 /** 328 * b/223787365 329 */ 330 @Test testNoOvercountingAfterEnd()331 public void testNoOvercountingAfterEnd() { 332 FrameTracker tracker = spyFrameTracker( 333 CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, CUJ_POSTFIX, /* surfaceOnly= */ false); 334 335 when(mChoreographer.getVsyncId()).thenReturn(100L); 336 tracker.begin(); 337 mRunnableArgumentCaptor.getValue().run(); 338 verify(mRenderer, only()).addObserver(any()); 339 340 // send first frame - not janky 341 sendFrame(tracker, 4, JANK_NONE, 100L); 342 343 // send another frame - not janky 344 sendFrame(tracker, 4, JANK_NONE, 101L); 345 346 // end the trace session, simulate one more valid callback came after the end call. 347 when(mChoreographer.getVsyncId()).thenReturn(102L); 348 tracker.end(FrameTracker.REASON_END_NORMAL); 349 350 // Send incomplete callback for 102L 351 sendSfFrame(tracker, 102L, JANK_NONE); 352 353 // Send janky but complete callbck fo 103L 354 sendFrame(tracker, 50, JANK_APP_DEADLINE_MISSED, 103L); 355 356 verify(tracker).removeObservers(); 357 verify(tracker, never()).triggerPerfetto(); 358 verify(mStatsLog).write(eq(UI_INTERACTION_FRAME_INFO_REPORTED), 359 eq(CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE]), 360 eq(2L) /* totalFrames */, 361 eq(0L) /* missedFrames */, 362 eq(4000000L) /* maxFrameTimeNanos */, 363 eq(0L) /* missedSfFramesCount */, 364 eq(0L) /* missedAppFramesCount */, 365 eq(0L) /* maxSuccessiveMissedFramesCount */); 366 } 367 368 @Test testBeginCancel()369 public void testBeginCancel() { 370 FrameTracker tracker = spyFrameTracker( 371 CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, CUJ_POSTFIX, /* surfaceOnly= */ false); 372 373 when(mChoreographer.getVsyncId()).thenReturn(100L); 374 tracker.begin(); 375 mRunnableArgumentCaptor.getValue().run(); 376 verify(mRenderer).addObserver(any()); 377 378 // First frame - not janky 379 sendFrame(tracker, 4, JANK_NONE, 100L); 380 381 // normal frame - not janky 382 sendFrame(tracker, 4, JANK_NONE, 101L); 383 384 // a janky frame 385 sendFrame(tracker, 50, JANK_APP_DEADLINE_MISSED, 102L); 386 387 tracker.cancel(FrameTracker.REASON_CANCEL_NORMAL); 388 verify(tracker).removeObservers(); 389 // Since the tracker has been cancelled, shouldn't trigger perfetto. 390 verify(tracker, never()).triggerPerfetto(); 391 } 392 393 @Test testCancelIfEndVsyncIdEqualsToBeginVsyncId()394 public void testCancelIfEndVsyncIdEqualsToBeginVsyncId() { 395 FrameTracker tracker = spyFrameTracker( 396 CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, CUJ_POSTFIX, /* surfaceOnly= */ false); 397 398 when(mChoreographer.getVsyncId()).thenReturn(100L); 399 tracker.begin(); 400 mRunnableArgumentCaptor.getValue().run(); 401 verify(mRenderer, only()).addObserver(any()); 402 403 // end the trace session 404 when(mChoreographer.getVsyncId()).thenReturn(101L); 405 tracker.end(FrameTracker.REASON_END_NORMAL); 406 407 // Since the begin vsync id (101) equals to the end vsync id (101), will be treat as cancel. 408 verify(tracker).cancel(FrameTracker.REASON_CANCEL_SAME_VSYNC); 409 410 // Observers should be removed in this case, or FrameTracker object will be leaked. 411 verify(tracker).removeObservers(); 412 413 // Should never trigger Perfetto since it is a cancel. 414 verify(tracker, never()).triggerPerfetto(); 415 } 416 417 @Test testCancelIfEndVsyncIdLessThanBeginVsyncId()418 public void testCancelIfEndVsyncIdLessThanBeginVsyncId() { 419 FrameTracker tracker = spyFrameTracker( 420 CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, CUJ_POSTFIX, /* surfaceOnly= */ false); 421 422 when(mChoreographer.getVsyncId()).thenReturn(100L); 423 tracker.begin(); 424 mRunnableArgumentCaptor.getValue().run(); 425 verify(mRenderer, only()).addObserver(any()); 426 427 // end the trace session at the same vsync id, end vsync id will less than the begin one. 428 // Because the begin vsync id is supposed to the next frame, 429 tracker.end(FrameTracker.REASON_END_NORMAL); 430 431 // The begin vsync id (101) is larger than the end one (100), will be treat as cancel. 432 verify(tracker).cancel(FrameTracker.REASON_CANCEL_SAME_VSYNC); 433 434 // Observers should be removed in this case, or FrameTracker object will be leaked. 435 verify(tracker).removeObservers(); 436 437 // Should never trigger Perfetto since it is a cancel. 438 verify(tracker, never()).triggerPerfetto(); 439 } 440 441 @Test testCancelWhenSessionNeverBegun()442 public void testCancelWhenSessionNeverBegun() { 443 FrameTracker tracker = spyFrameTracker( 444 CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, CUJ_POSTFIX, /* surfaceOnly= */ false); 445 446 tracker.cancel(FrameTracker.REASON_CANCEL_NORMAL); 447 verify(tracker).removeObservers(); 448 } 449 450 @Test testEndWhenSessionNeverBegun()451 public void testEndWhenSessionNeverBegun() { 452 FrameTracker tracker = spyFrameTracker( 453 CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, CUJ_POSTFIX, /* surfaceOnly= */ false); 454 455 tracker.end(FrameTracker.REASON_END_NORMAL); 456 verify(tracker).removeObservers(); 457 } 458 459 @Test testSurfaceOnlyOtherFrameJanky()460 public void testSurfaceOnlyOtherFrameJanky() { 461 FrameTracker tracker = spyFrameTracker( 462 CUJ_WALLPAPER_TRANSITION, CUJ_POSTFIX, /* surfaceOnly= */ true); 463 464 when(mChoreographer.getVsyncId()).thenReturn(100L); 465 tracker.begin(); 466 mRunnableArgumentCaptor.getValue().run(); 467 verify(mSurfaceControlWrapper).addJankStatsListener(any(), any()); 468 469 // First frame - not janky 470 sendFrame(tracker, JANK_NONE, 100L); 471 // normal frame - not janky 472 sendFrame(tracker, JANK_NONE, 101L); 473 // a janky frame 474 sendFrame(tracker, JANK_APP_DEADLINE_MISSED, 102L); 475 476 when(mChoreographer.getVsyncId()).thenReturn(102L); 477 tracker.end(FrameTracker.REASON_CANCEL_NORMAL); 478 479 // an extra frame to trigger finish 480 sendFrame(tracker, JANK_NONE, 103L); 481 482 verify(mSurfaceControlWrapper).removeJankStatsListener(any()); 483 verify(tracker).triggerPerfetto(); 484 485 verify(mStatsLog).write(eq(UI_INTERACTION_FRAME_INFO_REPORTED), 486 eq(CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_WALLPAPER_TRANSITION]), 487 eq(2L) /* totalFrames */, 488 eq(1L) /* missedFrames */, 489 eq(0L) /* maxFrameTimeNanos */, 490 eq(0L) /* missedSfFramesCount */, 491 eq(1L) /* missedAppFramesCount */, 492 eq(1L) /* maxSuccessiveMissedFramesCount */); 493 } 494 495 @Test testSurfaceOnlyFirstFrameJanky()496 public void testSurfaceOnlyFirstFrameJanky() { 497 FrameTracker tracker = spyFrameTracker( 498 CUJ_WALLPAPER_TRANSITION, CUJ_POSTFIX, /* surfaceOnly= */ true); 499 500 when(mChoreographer.getVsyncId()).thenReturn(100L); 501 tracker.begin(); 502 mRunnableArgumentCaptor.getValue().run(); 503 verify(mSurfaceControlWrapper).addJankStatsListener(any(), any()); 504 505 // First frame - janky 506 sendFrame(tracker, JANK_APP_DEADLINE_MISSED, 100L); 507 // normal frame - not janky 508 sendFrame(tracker, JANK_NONE, 101L); 509 // normal frame - not janky 510 sendFrame(tracker, JANK_NONE, 102L); 511 512 when(mChoreographer.getVsyncId()).thenReturn(102L); 513 tracker.end(FrameTracker.REASON_CANCEL_NORMAL); 514 515 // an extra frame to trigger finish 516 sendFrame(tracker, JANK_NONE, 103L); 517 518 verify(mSurfaceControlWrapper).removeJankStatsListener(any()); 519 verify(tracker, never()).triggerPerfetto(); 520 521 verify(mStatsLog).write(eq(UI_INTERACTION_FRAME_INFO_REPORTED), 522 eq(CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_WALLPAPER_TRANSITION]), 523 eq(2L) /* totalFrames */, 524 eq(0L) /* missedFrames */, 525 eq(0L) /* maxFrameTimeNanos */, 526 eq(0L) /* missedSfFramesCount */, 527 eq(0L) /* missedAppFramesCount */, 528 eq(0L) /* maxSuccessiveMissedFramesCount */); 529 } 530 531 @Test testSurfaceOnlyLastFrameJanky()532 public void testSurfaceOnlyLastFrameJanky() { 533 FrameTracker tracker = spyFrameTracker( 534 CUJ_WALLPAPER_TRANSITION, CUJ_POSTFIX, /* surfaceOnly= */ true); 535 536 when(mChoreographer.getVsyncId()).thenReturn(100L); 537 tracker.begin(); 538 mRunnableArgumentCaptor.getValue().run(); 539 verify(mSurfaceControlWrapper).addJankStatsListener(any(), any()); 540 541 // First frame - not janky 542 sendFrame(tracker, JANK_NONE, 100L); 543 // normal frame - not janky 544 sendFrame(tracker, JANK_NONE, 101L); 545 // normal frame - not janky 546 sendFrame(tracker, JANK_NONE, 102L); 547 548 when(mChoreographer.getVsyncId()).thenReturn(102L); 549 tracker.end(FrameTracker.REASON_CANCEL_NORMAL); 550 551 // janky frame, should be ignored, trigger finish 552 sendFrame(tracker, JANK_APP_DEADLINE_MISSED, 103L); 553 554 verify(mSurfaceControlWrapper).removeJankStatsListener(any()); 555 verify(tracker, never()).triggerPerfetto(); 556 557 verify(mStatsLog).write(eq(UI_INTERACTION_FRAME_INFO_REPORTED), 558 eq(CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_WALLPAPER_TRANSITION]), 559 eq(2L) /* totalFrames */, 560 eq(0L) /* missedFrames */, 561 eq(0L) /* maxFrameTimeNanos */, 562 eq(0L) /* missedSfFramesCount */, 563 eq(0L) /* missedAppFramesCount */, 564 eq(0L) /* maxSuccessiveMissedFramesCount */); 565 } 566 567 @Test testMaxSuccessiveMissedFramesCount()568 public void testMaxSuccessiveMissedFramesCount() { 569 FrameTracker tracker = spyFrameTracker( 570 CUJ_WALLPAPER_TRANSITION, CUJ_POSTFIX, /* surfaceOnly= */ true); 571 when(mChoreographer.getVsyncId()).thenReturn(100L); 572 tracker.begin(); 573 mRunnableArgumentCaptor.getValue().run(); 574 verify(mSurfaceControlWrapper).addJankStatsListener(any(), any()); 575 sendFrame(tracker, JANK_SURFACEFLINGER_DEADLINE_MISSED, 100L); 576 sendFrame(tracker, JANK_SURFACEFLINGER_DEADLINE_MISSED, 101L); 577 sendFrame(tracker, JANK_APP_DEADLINE_MISSED, 102L); 578 sendFrame(tracker, JANK_NONE, 103L); 579 sendFrame(tracker, JANK_APP_DEADLINE_MISSED, 104L); 580 sendFrame(tracker, JANK_APP_DEADLINE_MISSED, 105L); 581 when(mChoreographer.getVsyncId()).thenReturn(106L); 582 tracker.end(FrameTracker.REASON_END_NORMAL); 583 sendFrame(tracker, JANK_SURFACEFLINGER_DEADLINE_MISSED, 106L); 584 sendFrame(tracker, JANK_SURFACEFLINGER_DEADLINE_MISSED, 107L); 585 verify(mSurfaceControlWrapper).removeJankStatsListener(any()); 586 verify(tracker).triggerPerfetto(); 587 verify(mStatsLog).write(eq(UI_INTERACTION_FRAME_INFO_REPORTED), 588 eq(CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_WALLPAPER_TRANSITION]), 589 eq(6L) /* totalFrames */, 590 eq(5L) /* missedFrames */, 591 eq(0L) /* maxFrameTimeNanos */, 592 eq(2L) /* missedSfFramesCount */, 593 eq(3L) /* missedAppFramesCount */, 594 eq(3L) /* maxSuccessiveMissedFramesCount */); 595 } 596 sendFirstWindowFrame(FrameTracker tracker, long durationMillis, @JankType int jankType, long vsyncId)597 private void sendFirstWindowFrame(FrameTracker tracker, long durationMillis, 598 @JankType int jankType, long vsyncId) { 599 sendFrame(tracker, durationMillis, jankType, vsyncId, /* firstWindowFrame= */ true); 600 } 601 sendFrame(FrameTracker tracker, long durationMillis, @JankType int jankType, long vsyncId)602 private void sendFrame(FrameTracker tracker, long durationMillis, 603 @JankType int jankType, long vsyncId) { 604 sendFrame(tracker, durationMillis, jankType, vsyncId, /* firstWindowFrame= */ false); 605 } 606 607 /** 608 * Used for surface only test. 609 */ sendFrame(FrameTracker tracker, @JankType int jankType, long vsyncId)610 private void sendFrame(FrameTracker tracker, @JankType int jankType, long vsyncId) { 611 sendFrame(tracker, /* durationMillis= */ -1, 612 jankType, vsyncId, /* firstWindowFrame= */ false); 613 } 614 sendFrame(FrameTracker tracker, long durationMillis, @JankType int jankType, long vsyncId, boolean firstWindowFrame)615 private void sendFrame(FrameTracker tracker, long durationMillis, 616 @JankType int jankType, long vsyncId, boolean firstWindowFrame) { 617 if (!tracker.mSurfaceOnly) { 618 sendHwuiFrame(tracker, durationMillis, vsyncId, firstWindowFrame); 619 } 620 sendSfFrame(tracker, vsyncId, jankType); 621 } 622 sendHwuiFrame(FrameTracker tracker, long durationMillis, long vsyncId, boolean firstWindowFrame)623 private void sendHwuiFrame(FrameTracker tracker, long durationMillis, long vsyncId, 624 boolean firstWindowFrame) { 625 when(mWrapper.getTiming()).thenReturn(new long[]{0, vsyncId}); 626 doReturn(firstWindowFrame ? 1L : 0L).when(mWrapper) 627 .getMetric(FrameMetrics.FIRST_DRAW_FRAME); 628 doReturn(TimeUnit.MILLISECONDS.toNanos(durationMillis)) 629 .when(mWrapper).getMetric(FrameMetrics.TOTAL_DURATION); 630 final ArgumentCaptor<Runnable> captor = ArgumentCaptor.forClass(Runnable.class); 631 doNothing().when(tracker).postCallback(captor.capture()); 632 tracker.onFrameMetricsAvailable(0); 633 captor.getValue().run(); 634 } 635 sendSfFrame(FrameTracker tracker, long vsyncId, @JankType int jankType)636 private void sendSfFrame(FrameTracker tracker, long vsyncId, @JankType int jankType) { 637 final ArgumentCaptor<Runnable> captor = ArgumentCaptor.forClass(Runnable.class); 638 doNothing().when(tracker).postCallback(captor.capture()); 639 mListenerCapture.getValue().onJankDataAvailable(new JankData[] { 640 new JankData(vsyncId, jankType) 641 }); 642 captor.getValue().run(); 643 } 644 } 645