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.DISPLAY_HAL;
20 import static android.view.SurfaceControl.JankData.JANK_APP_DEADLINE_MISSED;
21 import static android.view.SurfaceControl.JankData.JANK_NONE;
22 import static android.view.SurfaceControl.JankData.JANK_SURFACEFLINGER_DEADLINE_MISSED;
23 import static android.view.SurfaceControl.JankData.JANK_SURFACEFLINGER_GPU_DEADLINE_MISSED;
24 import static android.view.SurfaceControl.JankData.PREDICTION_ERROR;
25 import static android.view.SurfaceControl.JankData.SURFACE_FLINGER_SCHEDULING;
26 
27 import static com.android.internal.jank.InteractionJankMonitor.ACTION_METRICS_LOGGED;
28 import static com.android.internal.jank.InteractionJankMonitor.ACTION_SESSION_BEGIN;
29 import static com.android.internal.jank.InteractionJankMonitor.ACTION_SESSION_CANCEL;
30 import static com.android.internal.jank.InteractionJankMonitor.ACTION_SESSION_END;
31 
32 import android.annotation.IntDef;
33 import android.annotation.NonNull;
34 import android.annotation.Nullable;
35 import android.graphics.HardwareRendererObserver;
36 import android.os.Handler;
37 import android.os.Trace;
38 import android.util.Log;
39 import android.util.SparseArray;
40 import android.view.Choreographer;
41 import android.view.FrameMetrics;
42 import android.view.SurfaceControl;
43 import android.view.SurfaceControl.JankData.JankType;
44 import android.view.ThreadedRenderer;
45 import android.view.ViewRootImpl;
46 
47 import com.android.internal.annotations.VisibleForTesting;
48 import com.android.internal.jank.InteractionJankMonitor.Configuration;
49 import com.android.internal.jank.InteractionJankMonitor.Session;
50 import com.android.internal.util.FrameworkStatsLog;
51 
52 import java.lang.annotation.Retention;
53 import java.lang.annotation.RetentionPolicy;
54 import java.util.concurrent.TimeUnit;
55 
56 /**
57  * A class that allows the app to get the frame metrics from HardwareRendererObserver.
58  * @hide
59  */
60 public class FrameTracker extends SurfaceControl.OnJankDataListener
61         implements HardwareRendererObserver.OnFrameMetricsAvailableListener {
62     private static final String TAG = "FrameTracker";
63     private static final boolean DEBUG = false;
64 
65     private static final long INVALID_ID = -1;
66     public static final int NANOS_IN_MILLISECOND = 1_000_000;
67 
68     static final int REASON_END_UNKNOWN = -1;
69     static final int REASON_END_NORMAL = 0;
70     static final int REASON_END_SURFACE_DESTROYED = 1;
71     static final int REASON_CANCEL_NORMAL = 16;
72     static final int REASON_CANCEL_NOT_BEGUN = 17;
73     static final int REASON_CANCEL_SAME_VSYNC = 18;
74     static final int REASON_CANCEL_TIMEOUT = 19;
75 
76     /** @hide */
77     @IntDef({
78             REASON_END_UNKNOWN,
79             REASON_END_NORMAL,
80             REASON_END_SURFACE_DESTROYED,
81             REASON_CANCEL_NORMAL,
82             REASON_CANCEL_NOT_BEGUN,
83             REASON_CANCEL_SAME_VSYNC,
84     })
85     @Retention(RetentionPolicy.SOURCE)
86     public @interface Reasons {
87     }
88 
89     private final HardwareRendererObserver mObserver;
90     private SurfaceControl mSurfaceControl;
91     private final int mTraceThresholdMissedFrames;
92     private final int mTraceThresholdFrameTimeMillis;
93     private final ThreadedRendererWrapper mRendererWrapper;
94     private final FrameMetricsWrapper mMetricsWrapper;
95     private final SparseArray<JankInfo> mJankInfos = new SparseArray<>();
96     private final Session mSession;
97     private final ViewRootWrapper mViewRoot;
98     private final SurfaceControlWrapper mSurfaceControlWrapper;
99     private final ViewRootImpl.SurfaceChangedCallback mSurfaceChangedCallback;
100     private final Handler mHandler;
101     private final ChoreographerWrapper mChoreographer;
102     private final Object mLock = InteractionJankMonitor.getInstance().getLock();
103 
104     @VisibleForTesting
105     public final boolean mSurfaceOnly;
106 
107     private long mBeginVsyncId = INVALID_ID;
108     private long mEndVsyncId = INVALID_ID;
109     private boolean mMetricsFinalized;
110     private boolean mCancelled = false;
111     private FrameTrackerListener mListener;
112     private boolean mTracingStarted = false;
113     private Runnable mWaitForFinishTimedOut;
114 
115     private static class JankInfo {
116         long frameVsyncId;
117         long totalDurationNanos;
118         boolean isFirstFrame;
119         boolean hwuiCallbackFired;
120         boolean surfaceControlCallbackFired;
121         @JankType int jankType;
122 
createFromHwuiCallback(long frameVsyncId, long totalDurationNanos, boolean isFirstFrame)123         static JankInfo createFromHwuiCallback(long frameVsyncId, long totalDurationNanos,
124                 boolean isFirstFrame) {
125             return new JankInfo(frameVsyncId, true, false, JANK_NONE, totalDurationNanos,
126                     isFirstFrame);
127         }
128 
createFromSurfaceControlCallback(long frameVsyncId, @JankType int jankType)129         static JankInfo createFromSurfaceControlCallback(long frameVsyncId,
130                 @JankType int jankType) {
131             return new JankInfo(frameVsyncId, false, true, jankType, 0, false /* isFirstFrame */);
132         }
133 
JankInfo(long frameVsyncId, boolean hwuiCallbackFired, boolean surfaceControlCallbackFired, @JankType int jankType, long totalDurationNanos, boolean isFirstFrame)134         private JankInfo(long frameVsyncId, boolean hwuiCallbackFired,
135                 boolean surfaceControlCallbackFired, @JankType int jankType,
136                 long totalDurationNanos, boolean isFirstFrame) {
137             this.frameVsyncId = frameVsyncId;
138             this.hwuiCallbackFired = hwuiCallbackFired;
139             this.surfaceControlCallbackFired = surfaceControlCallbackFired;
140             this.totalDurationNanos = totalDurationNanos;
141             this.jankType = jankType;
142             this.isFirstFrame = isFirstFrame;
143         }
144     }
145 
FrameTracker(@onNull Session session, @NonNull Handler handler, @Nullable ThreadedRendererWrapper renderer, @Nullable ViewRootWrapper viewRootWrapper, @NonNull SurfaceControlWrapper surfaceControlWrapper, @NonNull ChoreographerWrapper choreographer, @Nullable FrameMetricsWrapper metrics, int traceThresholdMissedFrames, int traceThresholdFrameTimeMillis, @Nullable FrameTrackerListener listener, @NonNull Configuration config)146     public FrameTracker(@NonNull Session session, @NonNull Handler handler,
147             @Nullable ThreadedRendererWrapper renderer, @Nullable ViewRootWrapper viewRootWrapper,
148             @NonNull SurfaceControlWrapper surfaceControlWrapper,
149             @NonNull ChoreographerWrapper choreographer,
150             @Nullable FrameMetricsWrapper metrics,
151             int traceThresholdMissedFrames, int traceThresholdFrameTimeMillis,
152             @Nullable FrameTrackerListener listener, @NonNull Configuration config) {
153         mSurfaceOnly = config.isSurfaceOnly();
154         mSession = session;
155         mHandler = handler;
156         mChoreographer = choreographer;
157         mSurfaceControlWrapper = surfaceControlWrapper;
158 
159         // HWUI instrumentation init.
160         mRendererWrapper = mSurfaceOnly ? null : renderer;
161         mMetricsWrapper = mSurfaceOnly ? null : metrics;
162         mViewRoot = mSurfaceOnly ? null : viewRootWrapper;
163         mObserver = mSurfaceOnly
164                 ? null
165                 : new HardwareRendererObserver(this, mMetricsWrapper.getTiming(),
166                         handler, /* waitForPresentTime= */ false);
167 
168         mTraceThresholdMissedFrames = traceThresholdMissedFrames;
169         mTraceThresholdFrameTimeMillis = traceThresholdFrameTimeMillis;
170         mListener = listener;
171 
172         if (mSurfaceOnly) {
173             mSurfaceControl = config.getSurfaceControl();
174             mSurfaceChangedCallback = null;
175         } else {
176             // HWUI instrumentation init.
177             // If the surface isn't valid yet, wait until it's created.
178             if (mViewRoot.getSurfaceControl().isValid()) {
179                 mSurfaceControl = mViewRoot.getSurfaceControl();
180             }
181 
182             mSurfaceChangedCallback = new ViewRootImpl.SurfaceChangedCallback() {
183                 @Override
184                 public void surfaceCreated(SurfaceControl.Transaction t) {
185                     synchronized (mLock) {
186                         if (mSurfaceControl == null) {
187                             mSurfaceControl = mViewRoot.getSurfaceControl();
188                             if (mBeginVsyncId != INVALID_ID) {
189                                 mSurfaceControlWrapper.addJankStatsListener(
190                                         FrameTracker.this, mSurfaceControl);
191                                 postTraceStartMarker();
192                             }
193                         }
194                     }
195                 }
196 
197                 @Override
198                 public void surfaceReplaced(SurfaceControl.Transaction t) {
199                 }
200 
201                 @Override
202                 public void surfaceDestroyed() {
203 
204                     // Wait a while to give the system a chance for the remaining
205                     // frames to arrive, then force finish the session.
206                     mHandler.postDelayed(() -> {
207                         synchronized (mLock) {
208                             if (DEBUG) {
209                                 Log.d(TAG, "surfaceDestroyed: " + mSession.getName()
210                                         + ", finalized=" + mMetricsFinalized
211                                         + ", info=" + mJankInfos.size()
212                                         + ", vsync=" + mBeginVsyncId);
213                             }
214                             if (!mMetricsFinalized) {
215                                 end(REASON_END_SURFACE_DESTROYED);
216                                 finish(mJankInfos.size() - 1);
217                             }
218                         }
219                     }, 50);
220                 }
221             };
222             // This callback has a reference to FrameTracker,
223             // remember to remove it to avoid leakage.
224             mViewRoot.addSurfaceChangedCallback(mSurfaceChangedCallback);
225         }
226     }
227 
228     /**
229      * Begin a trace session of the CUJ.
230      */
begin()231     public void begin() {
232         synchronized (mLock) {
233             mBeginVsyncId = mChoreographer.getVsyncId() + 1;
234             if (DEBUG) {
235                 Log.d(TAG, "begin: " + mSession.getName() + ", begin=" + mBeginVsyncId);
236             }
237             if (mSurfaceControl != null) {
238                 postTraceStartMarker();
239                 mSurfaceControlWrapper.addJankStatsListener(this, mSurfaceControl);
240             }
241             if (!mSurfaceOnly) {
242                 mRendererWrapper.addObserver(mObserver);
243             }
244             notifyCujEvent(ACTION_SESSION_BEGIN);
245         }
246     }
247 
248     /**
249      * Start trace section at appropriate time.
250      */
251     @VisibleForTesting
postTraceStartMarker()252     public void postTraceStartMarker() {
253         mChoreographer.mChoreographer.postCallback(Choreographer.CALLBACK_INPUT, () -> {
254             synchronized (mLock) {
255                 if (mCancelled || mEndVsyncId != INVALID_ID) {
256                     return;
257                 }
258                 mTracingStarted = true;
259                 Trace.beginAsyncSection(mSession.getName(), (int) mBeginVsyncId);
260             }
261         }, null);
262     }
263 
264     /**
265      * End the trace session of the CUJ.
266      */
end(@easons int reason)267     public boolean end(@Reasons int reason) {
268         synchronized (mLock) {
269             if (mCancelled || mEndVsyncId != INVALID_ID) return false;
270             mEndVsyncId = mChoreographer.getVsyncId();
271             // Cancel the session if:
272             // 1. The session begins and ends at the same vsync id.
273             // 2. The session never begun.
274             if (mBeginVsyncId == INVALID_ID) {
275                 return cancel(REASON_CANCEL_NOT_BEGUN);
276             } else if (mEndVsyncId <= mBeginVsyncId) {
277                 return cancel(REASON_CANCEL_SAME_VSYNC);
278             } else {
279                 if (DEBUG) {
280                     Log.d(TAG, "end: " + mSession.getName()
281                             + ", end=" + mEndVsyncId + ", reason=" + reason);
282                 }
283                 Trace.endAsyncSection(mSession.getName(), (int) mBeginVsyncId);
284                 mSession.setReason(reason);
285 
286                 // We don't remove observer here,
287                 // will remove it when all the frame metrics in this duration are called back.
288                 // See onFrameMetricsAvailable for the logic of removing the observer.
289                 // Waiting at most 10 seconds for all callbacks to finish.
290                 mWaitForFinishTimedOut = () -> {
291                     Log.e(TAG, "force finish cuj because of time out:" + mSession.getName());
292                     finish(mJankInfos.size() - 1);
293                 };
294                 mHandler.postDelayed(mWaitForFinishTimedOut, TimeUnit.SECONDS.toMillis(10));
295                 notifyCujEvent(ACTION_SESSION_END);
296                 return true;
297             }
298         }
299     }
300 
301     /**
302      * Cancel the trace session of the CUJ.
303      */
cancel(@easons int reason)304     public boolean cancel(@Reasons int reason) {
305         synchronized (mLock) {
306             final boolean cancelFromEnd =
307                     reason == REASON_CANCEL_NOT_BEGUN || reason == REASON_CANCEL_SAME_VSYNC;
308             if (mCancelled || (mEndVsyncId != INVALID_ID && !cancelFromEnd)) return false;
309             mCancelled = true;
310             // We don't need to end the trace section if it never begun.
311             if (mTracingStarted) {
312                 Trace.endAsyncSection(mSession.getName(), (int) mBeginVsyncId);
313             }
314 
315             // Always remove the observers in cancel call to avoid leakage.
316             removeObservers();
317 
318             if (DEBUG) {
319                 Log.d(TAG, "cancel: " + mSession.getName() + ", begin=" + mBeginVsyncId
320                         + ", end=" + mEndVsyncId + ", reason=" + reason);
321             }
322 
323             mSession.setReason(reason);
324             // Notify the listener the session has been cancelled.
325             // We don't notify the listeners if the session never begun.
326             notifyCujEvent(ACTION_SESSION_CANCEL);
327             return true;
328         }
329     }
330 
notifyCujEvent(String action)331     private void notifyCujEvent(String action) {
332         if (mListener == null) return;
333         mListener.onCujEvents(mSession, action);
334     }
335 
336     @Override
onJankDataAvailable(SurfaceControl.JankData[] jankData)337     public void onJankDataAvailable(SurfaceControl.JankData[] jankData) {
338         synchronized (mLock) {
339             if (mCancelled) {
340                 return;
341             }
342 
343             for (SurfaceControl.JankData jankStat : jankData) {
344                 if (!isInRange(jankStat.frameVsyncId)) {
345                     continue;
346                 }
347                 JankInfo info = findJankInfo(jankStat.frameVsyncId);
348                 if (info != null) {
349                     info.surfaceControlCallbackFired = true;
350                     info.jankType = jankStat.jankType;
351                 } else {
352                     mJankInfos.put((int) jankStat.frameVsyncId,
353                             JankInfo.createFromSurfaceControlCallback(
354                                     jankStat.frameVsyncId, jankStat.jankType));
355                 }
356             }
357             processJankInfos();
358         }
359     }
360 
findJankInfo(long frameVsyncId)361     private @Nullable JankInfo findJankInfo(long frameVsyncId) {
362         return mJankInfos.get((int) frameVsyncId);
363     }
364 
isInRange(long vsyncId)365     private boolean isInRange(long vsyncId) {
366         // It's possible that we may miss a callback for the frame with vsyncId == mEndVsyncId.
367         // Because of that, we collect all frames even if they happen after the end so we eventually
368         // have a frame after the end with both callbacks present.
369         return vsyncId >= mBeginVsyncId;
370     }
371 
372     @Override
onFrameMetricsAvailable(int dropCountSinceLastInvocation)373     public void onFrameMetricsAvailable(int dropCountSinceLastInvocation) {
374         synchronized (mLock) {
375             if (mCancelled) {
376                 return;
377             }
378 
379             // Since this callback might come a little bit late after the end() call.
380             // We should keep tracking the begin / end timestamp that we can compare with
381             // vsync timestamp to check if the frame is in the duration of the CUJ.
382             long totalDurationNanos = mMetricsWrapper.getMetric(FrameMetrics.TOTAL_DURATION);
383             boolean isFirstFrame = mMetricsWrapper.getMetric(FrameMetrics.FIRST_DRAW_FRAME) == 1;
384             long frameVsyncId =
385                     mMetricsWrapper.getTiming()[FrameMetrics.Index.FRAME_TIMELINE_VSYNC_ID];
386 
387             if (!isInRange(frameVsyncId)) {
388                 return;
389             }
390             JankInfo info = findJankInfo(frameVsyncId);
391             if (info != null) {
392                 info.hwuiCallbackFired = true;
393                 info.totalDurationNanos = totalDurationNanos;
394                 info.isFirstFrame = isFirstFrame;
395             } else {
396                 mJankInfos.put((int) frameVsyncId, JankInfo.createFromHwuiCallback(
397                         frameVsyncId, totalDurationNanos, isFirstFrame));
398             }
399             processJankInfos();
400         }
401     }
402 
403     /**
404      * Finds the first index in {@link #mJankInfos} which happened on or after {@link #mEndVsyncId},
405      * or -1 if the session hasn't ended yet.
406      */
getIndexOnOrAfterEnd()407     private int getIndexOnOrAfterEnd() {
408         if (mEndVsyncId == INVALID_ID || mMetricsFinalized) {
409             return -1;
410         }
411         JankInfo last = mJankInfos.size() == 0 ? null : mJankInfos.valueAt(mJankInfos.size() - 1);
412         if (last == null) {
413             return -1;
414         }
415         if (last.frameVsyncId < mEndVsyncId) {
416             return -1;
417         }
418 
419         int lastIndex = -1;
420         for (int i = mJankInfos.size() - 1; i >= 0; i--) {
421             JankInfo info = mJankInfos.valueAt(i);
422             if (info.frameVsyncId >= mEndVsyncId) {
423                 if (isLastIndexCandidate(info)) {
424                     lastIndex = i;
425                 }
426             } else {
427                 break;
428             }
429         }
430         return lastIndex;
431     }
432 
processJankInfos()433     private void processJankInfos() {
434         int indexOnOrAfterEnd = getIndexOnOrAfterEnd();
435         if (indexOnOrAfterEnd == -1) {
436             return;
437         }
438         finish(indexOnOrAfterEnd);
439     }
440 
isLastIndexCandidate(JankInfo info)441     private boolean isLastIndexCandidate(JankInfo info) {
442         return mSurfaceOnly
443                 ? info.surfaceControlCallbackFired
444                 : info.hwuiCallbackFired && info.surfaceControlCallbackFired;
445     }
446 
finish(int indexOnOrAfterEnd)447     private void finish(int indexOnOrAfterEnd) {
448         mHandler.removeCallbacks(mWaitForFinishTimedOut);
449         mWaitForFinishTimedOut = null;
450         mMetricsFinalized = true;
451 
452         // The tracing has been ended, remove the observer, see if need to trigger perfetto.
453         removeObservers();
454 
455         int totalFramesCount = 0;
456         long maxFrameTimeNanos = 0;
457         int missedFramesCount = 0;
458         int missedAppFramesCount = 0;
459         int missedSfFramesCount = 0;
460 
461         for (int i = 0; i <= indexOnOrAfterEnd; i++) {
462             JankInfo info = mJankInfos.valueAt(i);
463             final boolean isFirstDrawn = !mSurfaceOnly && info.isFirstFrame;
464             if (isFirstDrawn) {
465                 continue;
466             }
467             if (info.surfaceControlCallbackFired) {
468                 totalFramesCount++;
469                 boolean missedFrame = false;
470                 if ((info.jankType & PREDICTION_ERROR) != 0
471                         || ((info.jankType & JANK_APP_DEADLINE_MISSED) != 0)) {
472                     Log.w(TAG, "Missed App frame:" + info.jankType);
473                     missedAppFramesCount++;
474                     missedFrame = true;
475                 }
476                 if ((info.jankType & DISPLAY_HAL) != 0
477                         || (info.jankType & JANK_SURFACEFLINGER_DEADLINE_MISSED) != 0
478                         || (info.jankType & JANK_SURFACEFLINGER_GPU_DEADLINE_MISSED) != 0
479                         || (info.jankType & SURFACE_FLINGER_SCHEDULING) != 0) {
480                     Log.w(TAG, "Missed SF frame:" + info.jankType);
481                     missedSfFramesCount++;
482                     missedFrame = true;
483                 }
484                 if (missedFrame) {
485                     missedFramesCount++;
486                 }
487                 // TODO (b/174755489): Early latch currently gets fired way too often, so we have
488                 // to ignore it for now.
489                 if (!mSurfaceOnly && !info.hwuiCallbackFired) {
490                     Log.w(TAG, "Missing HWUI jank callback for vsyncId: " + info.frameVsyncId);
491                 }
492             }
493             if (!mSurfaceOnly && info.hwuiCallbackFired) {
494                 maxFrameTimeNanos = Math.max(info.totalDurationNanos, maxFrameTimeNanos);
495                 if (!info.surfaceControlCallbackFired) {
496                     Log.w(TAG, "Missing SF jank callback for vsyncId: " + info.frameVsyncId);
497                 }
498             }
499         }
500 
501         // Log the frame stats as counters to make them easily accessible in traces.
502         Trace.traceCounter(Trace.TRACE_TAG_APP, mSession.getName() + "#missedFrames",
503                 missedFramesCount);
504         Trace.traceCounter(Trace.TRACE_TAG_APP, mSession.getName() + "#missedAppFrames",
505                 missedAppFramesCount);
506         Trace.traceCounter(Trace.TRACE_TAG_APP, mSession.getName() + "#missedSfFrames",
507                 missedSfFramesCount);
508         Trace.traceCounter(Trace.TRACE_TAG_APP, mSession.getName() + "#totalFrames",
509                 totalFramesCount);
510         Trace.traceCounter(Trace.TRACE_TAG_APP, mSession.getName() + "#maxFrameTimeMillis",
511                 (int) (maxFrameTimeNanos / NANOS_IN_MILLISECOND));
512 
513         // Trigger perfetto if necessary.
514         if (shouldTriggerPerfetto(missedFramesCount, (int) maxFrameTimeNanos)) {
515             triggerPerfetto();
516         }
517         if (mSession.logToStatsd()) {
518             FrameworkStatsLog.write(
519                     FrameworkStatsLog.UI_INTERACTION_FRAME_INFO_REPORTED,
520                     mSession.getStatsdInteractionType(),
521                     totalFramesCount,
522                     missedFramesCount,
523                     maxFrameTimeNanos, /* will be 0 if mSurfaceOnly == true */
524                     missedSfFramesCount,
525                     missedAppFramesCount);
526             notifyCujEvent(ACTION_METRICS_LOGGED);
527         }
528         if (DEBUG) {
529             Log.i(TAG, "finish: CUJ=" + mSession.getName()
530                     + " (" + mBeginVsyncId + "," + mEndVsyncId + ")"
531                     + " totalFrames=" + totalFramesCount
532                     + " missedAppFrames=" + missedAppFramesCount
533                     + " missedSfFrames=" + missedSfFramesCount
534                     + " missedFrames=" + missedFramesCount
535                     + " maxFrameTimeMillis=" + maxFrameTimeNanos / NANOS_IN_MILLISECOND);
536         }
537     }
538 
shouldTriggerPerfetto(int missedFramesCount, int maxFrameTimeNanos)539     private boolean shouldTriggerPerfetto(int missedFramesCount, int maxFrameTimeNanos) {
540         boolean overMissedFramesThreshold = mTraceThresholdMissedFrames != -1
541                 && missedFramesCount >= mTraceThresholdMissedFrames;
542         boolean overFrameTimeThreshold = !mSurfaceOnly && mTraceThresholdFrameTimeMillis != -1
543                 && maxFrameTimeNanos >= mTraceThresholdFrameTimeMillis * NANOS_IN_MILLISECOND;
544         return overMissedFramesThreshold || overFrameTimeThreshold;
545     }
546 
547     /**
548      * Remove all the registered listeners, observers and callbacks.
549      */
550     @VisibleForTesting
removeObservers()551     public void removeObservers() {
552         mSurfaceControlWrapper.removeJankStatsListener(this);
553         if (!mSurfaceOnly) {
554             // HWUI part.
555             mRendererWrapper.removeObserver(mObserver);
556             if (mSurfaceChangedCallback != null) {
557                 mViewRoot.removeSurfaceChangedCallback(mSurfaceChangedCallback);
558             }
559         }
560     }
561 
562     /**
563      * Trigger the prefetto daemon.
564      */
triggerPerfetto()565     public void triggerPerfetto() {
566         InteractionJankMonitor.getInstance().trigger(mSession);
567     }
568 
569     /**
570      * A wrapper class that we can spy FrameMetrics (a final class) in unit tests.
571      */
572     public static class FrameMetricsWrapper {
573         private final FrameMetrics mFrameMetrics;
574 
FrameMetricsWrapper()575         public FrameMetricsWrapper() {
576             mFrameMetrics = new FrameMetrics();
577         }
578 
579         /**
580          * Wrapper method.
581          * @return timing data of the metrics
582          */
getTiming()583         public long[] getTiming() {
584             return mFrameMetrics.mTimingData;
585         }
586 
587         /**
588          * Wrapper method.
589          * @param index specific index of the timing data
590          * @return the timing data of the specified index
591          */
getMetric(int index)592         public long getMetric(int index) {
593             return mFrameMetrics.getMetric(index);
594         }
595     }
596 
597     /**
598      * A wrapper class that we can spy ThreadedRenderer (a final class) in unit tests.
599      */
600     public static class ThreadedRendererWrapper {
601         private final ThreadedRenderer mRenderer;
602 
ThreadedRendererWrapper(ThreadedRenderer renderer)603         public ThreadedRendererWrapper(ThreadedRenderer renderer) {
604             mRenderer = renderer;
605         }
606 
607         /**
608          * Wrapper method.
609          * @param observer observer
610          */
addObserver(HardwareRendererObserver observer)611         public void addObserver(HardwareRendererObserver observer) {
612             mRenderer.addObserver(observer);
613         }
614 
615         /**
616          * Wrapper method.
617          * @param observer observer
618          */
removeObserver(HardwareRendererObserver observer)619         public void removeObserver(HardwareRendererObserver observer) {
620             mRenderer.removeObserver(observer);
621         }
622     }
623 
624     public static class ViewRootWrapper {
625         private final ViewRootImpl mViewRoot;
626 
ViewRootWrapper(ViewRootImpl viewRoot)627         public ViewRootWrapper(ViewRootImpl viewRoot) {
628             mViewRoot = viewRoot;
629         }
630 
addSurfaceChangedCallback(ViewRootImpl.SurfaceChangedCallback callback)631         public void addSurfaceChangedCallback(ViewRootImpl.SurfaceChangedCallback callback) {
632             mViewRoot.addSurfaceChangedCallback(callback);
633         }
634 
removeSurfaceChangedCallback(ViewRootImpl.SurfaceChangedCallback callback)635         public void removeSurfaceChangedCallback(ViewRootImpl.SurfaceChangedCallback callback) {
636             mViewRoot.removeSurfaceChangedCallback(callback);
637         }
638 
getSurfaceControl()639         public SurfaceControl getSurfaceControl() {
640             return mViewRoot.getSurfaceControl();
641         }
642     }
643 
644     public static class SurfaceControlWrapper {
645 
addJankStatsListener(SurfaceControl.OnJankDataListener listener, SurfaceControl surfaceControl)646         public void addJankStatsListener(SurfaceControl.OnJankDataListener listener,
647                 SurfaceControl surfaceControl) {
648             SurfaceControl.addJankDataListener(listener, surfaceControl);
649         }
650 
removeJankStatsListener(SurfaceControl.OnJankDataListener listener)651         public void removeJankStatsListener(SurfaceControl.OnJankDataListener listener) {
652             SurfaceControl.removeJankDataListener(listener);
653         }
654     }
655 
656     public static class ChoreographerWrapper {
657 
658         private final Choreographer mChoreographer;
659 
ChoreographerWrapper(Choreographer choreographer)660         public ChoreographerWrapper(Choreographer choreographer) {
661             mChoreographer = choreographer;
662         }
663 
getVsyncId()664         public long getVsyncId() {
665             return mChoreographer.getVsyncId();
666         }
667     }
668 
669     /**
670      * A listener that notifies cuj events.
671      */
672     public interface FrameTrackerListener {
673         /**
674          * Notify that the CUJ session was created.
675          *
676          * @param session the CUJ session
677          * @param action the specific action
678          */
onCujEvents(Session session, String action)679         void onCujEvents(Session session, String action);
680     }
681 }
682