blob: 3f86f0d001d7008b2b58eb8973987fae27d83010 [file] [log] [blame]
Vishnu Nair8248b7c2018-08-01 10:13:36 -07001/*
2 * Copyright (C) 2018 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
17package com.android.server.wm.flicker.monitor;
18
19import static android.view.FrameStats.UNDEFINED_TIME_NANO;
20
21import android.app.Instrumentation;
22import android.util.Log;
23import android.view.FrameStats;
24
25/**
26 * Monitors {@link android.view.WindowAnimationFrameStats} to detect janky frames.
27 *
Brett Chabot502ec7a2019-03-01 14:43:20 -080028 * Adapted from {@link androidx.test.jank.internal.WindowAnimationFrameStatsMonitorImpl}
Vishnu Nair8248b7c2018-08-01 10:13:36 -070029 * using the same threshold to determine jank.
30 */
31public class WindowAnimationFrameStatsMonitor implements ITransitionMonitor {
32
33 private static final String TAG = "FLICKER";
34 // Maximum normalized error in frame duration before the frame is considered janky
35 private static final double MAX_ERROR = 0.5f;
36 // Maximum normalized frame duration before the frame is considered a pause
37 private static final double PAUSE_THRESHOLD = 15.0f;
38 private Instrumentation mInstrumentation;
39 private FrameStats stats;
40 private int numJankyFrames;
41 private long mLongestFrameNano = 0L;
42
43
44 /**
45 * Constructs a WindowAnimationFrameStatsMonitor instance.
46 */
47 public WindowAnimationFrameStatsMonitor(Instrumentation instrumentation) {
48 mInstrumentation = instrumentation;
49 }
50
51 private void analyze() {
52 int frameCount = stats.getFrameCount();
53 long refreshPeriodNano = stats.getRefreshPeriodNano();
54
55 // Skip first frame
56 for (int i = 2; i < frameCount; i++) {
57 // Handle frames that have not been presented.
58 if (stats.getFramePresentedTimeNano(i) == UNDEFINED_TIME_NANO) {
59 // The animation must not have completed. Warn and break out of the loop.
60 Log.w(TAG, "Skipping fenced frame.");
61 break;
62 }
63 long frameDurationNano = stats.getFramePresentedTimeNano(i) -
64 stats.getFramePresentedTimeNano(i - 1);
65 double normalized = (double) frameDurationNano / refreshPeriodNano;
66 if (normalized < PAUSE_THRESHOLD) {
67 if (normalized > 1.0f + MAX_ERROR) {
68 numJankyFrames++;
69 }
70 mLongestFrameNano = Math.max(mLongestFrameNano, frameDurationNano);
71 }
72 }
73 }
74
75 @Override
76 public void start() {
77 // Clear out any previous data
78 numJankyFrames = 0;
79 mLongestFrameNano = 0;
80 mInstrumentation.getUiAutomation().clearWindowAnimationFrameStats();
81 }
82
83 @Override
84 public void stop() {
85 stats = mInstrumentation.getUiAutomation().getWindowAnimationFrameStats();
86 analyze();
87 }
88
89 public boolean jankyFramesDetected() {
90 return stats.getFrameCount() > 0 && numJankyFrames > 0;
91 }
92
93 @Override
94 public String toString() {
95 return stats.toString() +
96 " RefreshPeriodNano:" + stats.getRefreshPeriodNano() +
97 " NumJankyFrames:" + numJankyFrames +
98 " LongestFrameNano:" + mLongestFrameNano;
99 }
100}