1 // Copyright 2023 The Chromium Authors 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 package org.chromium.base.jank_tracker; 6 7 import android.content.Context; 8 import android.hardware.display.DisplayManager; 9 import android.hardware.display.DisplayManager.DisplayListener; 10 import android.os.Build.VERSION_CODES; 11 import android.view.Display; 12 import android.view.FrameMetrics; 13 import android.view.Window; 14 import android.view.Window.OnFrameMetricsAvailableListener; 15 16 import androidx.annotation.RequiresApi; 17 18 import org.chromium.base.ContextUtils; 19 import org.chromium.base.TimeUtils; 20 import org.chromium.base.TraceEvent; 21 22 import java.util.concurrent.atomic.AtomicBoolean; 23 24 /** 25 * This class receives OnFrameMetricsAvailableListener.onFrameMetricsAvailable() callbacks and 26 * records frame durations in a FrameMetricsStore instance. 27 */ 28 @RequiresApi(api = VERSION_CODES.N) 29 public class FrameMetricsListener implements OnFrameMetricsAvailableListener { 30 private class DisplayListenerBackend implements DisplayListener { startListening()31 public void startListening() { 32 Context appCtx = ContextUtils.getApplicationContext(); 33 DisplayManager displayManager = 34 (DisplayManager) appCtx.getSystemService(Context.DISPLAY_SERVICE); 35 displayManager.registerDisplayListener(this, /* handler= */ null); 36 } 37 38 @Override onDisplayAdded(int sdkDisplayId)39 public void onDisplayAdded(int sdkDisplayId) {} 40 41 @Override onDisplayRemoved(int sdkDisplayId)42 public void onDisplayRemoved(int sdkDisplayId) {} 43 44 @Override onDisplayChanged(int sdkDisplayId)45 public void onDisplayChanged(int sdkDisplayId) { 46 maybeUpdateRefreshRate(); 47 } 48 } 49 50 private DisplayListenerBackend mBackend = new DisplayListenerBackend(); 51 52 private final FrameMetricsStore mFrameMetricsStore; 53 private AtomicBoolean mIsRecording = new AtomicBoolean(false); 54 // Microseconds between each frame. 55 private long mVsyncInterval; 56 FrameMetricsListener(FrameMetricsStore frameMetricsStore)57 public FrameMetricsListener(FrameMetricsStore frameMetricsStore) { 58 mFrameMetricsStore = frameMetricsStore; 59 mBackend.startListening(); 60 maybeUpdateRefreshRate(); 61 } 62 maybeUpdateRefreshRate()63 private void maybeUpdateRefreshRate() { 64 Context appCtx = ContextUtils.getApplicationContext(); 65 DisplayManager displayManager = 66 (DisplayManager) appCtx.getSystemService(Context.DISPLAY_SERVICE); 67 Display display = displayManager.getDisplay(Display.DEFAULT_DISPLAY); 68 if (display == null) { 69 return; 70 } 71 float refreshRate = display.getRefreshRate(); 72 final long kMicrosecondsPerSecond = 1000_000L; 73 mVsyncInterval = kMicrosecondsPerSecond / ((long) refreshRate); 74 TraceEvent.instant( 75 "FrameMetricsListener.maybeUpdateRefreshRate", Long.toString(mVsyncInterval)); 76 } 77 78 /** 79 * Toggles recording into FrameMetricsStore. When recording is stopped, reports accumulated 80 * metrics. 81 * @param isRecording 82 */ setIsListenerRecording(boolean isRecording)83 public void setIsListenerRecording(boolean isRecording) { 84 mIsRecording.set(isRecording); 85 } 86 87 @RequiresApi(api = VERSION_CODES.N) 88 @Override onFrameMetricsAvailable( Window window, FrameMetrics frameMetrics, int dropCountSinceLastInvocation)89 public void onFrameMetricsAvailable( 90 Window window, FrameMetrics frameMetrics, int dropCountSinceLastInvocation) { 91 if (!mIsRecording.get()) { 92 return; 93 } 94 95 long frameTotalDurationNs = frameMetrics.getMetric(FrameMetrics.TOTAL_DURATION); 96 long frame_start_vsync_ts = frameMetrics.getMetric(FrameMetrics.VSYNC_TIMESTAMP); 97 98 try (TraceEvent e = 99 TraceEvent.scoped("onFrameMetricsAvailable", Long.toString(frameTotalDurationNs))) { 100 // FrameMetrics.DEADLINE was added in API level 31(S). 101 // TODO(b/311139161): Update RequiresApi level to Android S. 102 long deadlineNs = frameMetrics.getMetric(FrameMetrics.DEADLINE); 103 int missedVsyncs = 0; 104 if (frameTotalDurationNs >= deadlineNs) { 105 long frameDeadlineDeltaUs = 106 (frameTotalDurationNs - deadlineNs) / TimeUtils.NANOSECONDS_PER_MICROSECOND; 107 missedVsyncs = (int) ((frameDeadlineDeltaUs + mVsyncInterval) / mVsyncInterval); 108 } 109 mFrameMetricsStore.addFrameMeasurement( 110 frameTotalDurationNs, missedVsyncs, frame_start_vsync_ts); 111 } 112 } 113 } 114