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