Merge "Send broadcast to notify the events of a CUJ session" into sc-dev
This commit is contained in:
@@ -25,6 +25,9 @@ import static android.view.SurfaceControl.JankData.JANK_SURFACEFLINGER_GPU_DEADL
|
|||||||
import static android.view.SurfaceControl.JankData.PREDICTION_ERROR;
|
import static android.view.SurfaceControl.JankData.PREDICTION_ERROR;
|
||||||
import static android.view.SurfaceControl.JankData.SURFACE_FLINGER_SCHEDULING;
|
import static android.view.SurfaceControl.JankData.SURFACE_FLINGER_SCHEDULING;
|
||||||
|
|
||||||
|
import static com.android.internal.jank.InteractionJankMonitor.ACTION_METRICS_LOGGED;
|
||||||
|
import static com.android.internal.jank.InteractionJankMonitor.ACTION_SESSION_BEGIN;
|
||||||
|
|
||||||
import android.annotation.NonNull;
|
import android.annotation.NonNull;
|
||||||
import android.annotation.Nullable;
|
import android.annotation.Nullable;
|
||||||
import android.graphics.HardwareRendererObserver;
|
import android.graphics.HardwareRendererObserver;
|
||||||
@@ -72,6 +75,7 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
|
|||||||
private long mEndVsyncId = INVALID_ID;
|
private long mEndVsyncId = INVALID_ID;
|
||||||
private boolean mMetricsFinalized;
|
private boolean mMetricsFinalized;
|
||||||
private boolean mCancelled = false;
|
private boolean mCancelled = false;
|
||||||
|
private FrameTrackerListener mListener;
|
||||||
|
|
||||||
private static class JankInfo {
|
private static class JankInfo {
|
||||||
long frameVsyncId;
|
long frameVsyncId;
|
||||||
@@ -109,7 +113,7 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
|
|||||||
@NonNull SurfaceControlWrapper surfaceControlWrapper,
|
@NonNull SurfaceControlWrapper surfaceControlWrapper,
|
||||||
@NonNull ChoreographerWrapper choreographer,
|
@NonNull ChoreographerWrapper choreographer,
|
||||||
@NonNull FrameMetricsWrapper metrics, int traceThresholdMissedFrames,
|
@NonNull FrameMetricsWrapper metrics, int traceThresholdMissedFrames,
|
||||||
int traceThresholdFrameTimeMillis) {
|
int traceThresholdFrameTimeMillis, @Nullable FrameTrackerListener listener) {
|
||||||
mSession = session;
|
mSession = session;
|
||||||
mRendererWrapper = renderer;
|
mRendererWrapper = renderer;
|
||||||
mMetricsWrapper = metrics;
|
mMetricsWrapper = metrics;
|
||||||
@@ -120,6 +124,7 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
|
|||||||
mObserver = new HardwareRendererObserver(this, mMetricsWrapper.getTiming(), handler);
|
mObserver = new HardwareRendererObserver(this, mMetricsWrapper.getTiming(), handler);
|
||||||
mTraceThresholdMissedFrames = traceThresholdMissedFrames;
|
mTraceThresholdMissedFrames = traceThresholdMissedFrames;
|
||||||
mTraceThresholdFrameTimeMillis = traceThresholdFrameTimeMillis;
|
mTraceThresholdFrameTimeMillis = traceThresholdFrameTimeMillis;
|
||||||
|
mListener = listener;
|
||||||
|
|
||||||
// If the surface isn't valid yet, wait until it's created.
|
// If the surface isn't valid yet, wait until it's created.
|
||||||
if (viewRootWrapper.getSurfaceControl().isValid()) {
|
if (viewRootWrapper.getSurfaceControl().isValid()) {
|
||||||
@@ -165,11 +170,15 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
|
|||||||
*/
|
*/
|
||||||
public synchronized void begin() {
|
public synchronized void begin() {
|
||||||
mBeginVsyncId = mChoreographer.getVsyncId() + 1;
|
mBeginVsyncId = mChoreographer.getVsyncId() + 1;
|
||||||
|
mSession.setTimeStamp(System.nanoTime());
|
||||||
Trace.beginAsyncSection(mSession.getName(), (int) mBeginVsyncId);
|
Trace.beginAsyncSection(mSession.getName(), (int) mBeginVsyncId);
|
||||||
mRendererWrapper.addObserver(mObserver);
|
mRendererWrapper.addObserver(mObserver);
|
||||||
if (mSurfaceControl != null) {
|
if (mSurfaceControl != null) {
|
||||||
mSurfaceControlWrapper.addJankStatsListener(this, mSurfaceControl);
|
mSurfaceControlWrapper.addJankStatsListener(this, mSurfaceControl);
|
||||||
}
|
}
|
||||||
|
if (mListener != null) {
|
||||||
|
mListener.onNotifyCujEvents(mSession, ACTION_SESSION_BEGIN);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -224,7 +233,6 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean isInRange(long vsyncId) {
|
private boolean isInRange(long vsyncId) {
|
||||||
|
|
||||||
// It's possible that we may miss a callback for the frame with vsyncId == mEndVsyncId.
|
// It's possible that we may miss a callback for the frame with vsyncId == mEndVsyncId.
|
||||||
// Because of that, we collect all frames even if they happen after the end so we eventually
|
// Because of that, we collect all frames even if they happen after the end so we eventually
|
||||||
// have a frame after the end with both callbacks present.
|
// have a frame after the end with both callbacks present.
|
||||||
@@ -371,6 +379,9 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
|
|||||||
missedAppFramesCount + missedSfFramesCounts,
|
missedAppFramesCount + missedSfFramesCounts,
|
||||||
maxFrameTimeNanos,
|
maxFrameTimeNanos,
|
||||||
missedSfFramesCounts);
|
missedSfFramesCounts);
|
||||||
|
if (mListener != null) {
|
||||||
|
mListener.onNotifyCujEvents(mSession, ACTION_METRICS_LOGGED);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Log.i(TAG, "FrameTracker: CUJ=" + mSession.getName()
|
Log.i(TAG, "FrameTracker: CUJ=" + mSession.getName()
|
||||||
@@ -495,4 +506,17 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
|
|||||||
return mChoreographer.getVsyncId();
|
return mChoreographer.getVsyncId();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A listener that notifies cuj events.
|
||||||
|
*/
|
||||||
|
public interface FrameTrackerListener {
|
||||||
|
/**
|
||||||
|
* Notify that the CUJ session was created.
|
||||||
|
*
|
||||||
|
* @param session the CUJ session
|
||||||
|
* @param action the specific action
|
||||||
|
*/
|
||||||
|
void onNotifyCujEvents(Session session, String action);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
package com.android.internal.jank;
|
package com.android.internal.jank;
|
||||||
|
|
||||||
|
import static android.content.Intent.FLAG_RECEIVER_REGISTERED_ONLY;
|
||||||
|
|
||||||
import static com.android.internal.jank.FrameTracker.ChoreographerWrapper;
|
import static com.android.internal.jank.FrameTracker.ChoreographerWrapper;
|
||||||
import static com.android.internal.jank.FrameTracker.SurfaceControlWrapper;
|
import static com.android.internal.jank.FrameTracker.SurfaceControlWrapper;
|
||||||
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_ALL_APPS_SCROLL;
|
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_ALL_APPS_SCROLL;
|
||||||
@@ -48,9 +50,12 @@ import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_IN
|
|||||||
|
|
||||||
import android.annotation.IntDef;
|
import android.annotation.IntDef;
|
||||||
import android.annotation.NonNull;
|
import android.annotation.NonNull;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.HandlerExecutor;
|
import android.os.HandlerExecutor;
|
||||||
import android.os.HandlerThread;
|
import android.os.HandlerThread;
|
||||||
|
import android.os.SystemProperties;
|
||||||
import android.provider.DeviceConfig;
|
import android.provider.DeviceConfig;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.util.SparseArray;
|
import android.util.SparseArray;
|
||||||
@@ -59,6 +64,7 @@ import android.view.View;
|
|||||||
|
|
||||||
import com.android.internal.annotations.VisibleForTesting;
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
import com.android.internal.jank.FrameTracker.FrameMetricsWrapper;
|
import com.android.internal.jank.FrameTracker.FrameMetricsWrapper;
|
||||||
|
import com.android.internal.jank.FrameTracker.FrameTrackerListener;
|
||||||
import com.android.internal.jank.FrameTracker.ThreadedRendererWrapper;
|
import com.android.internal.jank.FrameTracker.ThreadedRendererWrapper;
|
||||||
import com.android.internal.jank.FrameTracker.ViewRootWrapper;
|
import com.android.internal.jank.FrameTracker.ViewRootWrapper;
|
||||||
import com.android.internal.util.PerfettoTrigger;
|
import com.android.internal.util.PerfettoTrigger;
|
||||||
@@ -74,6 +80,8 @@ import java.util.concurrent.TimeUnit;
|
|||||||
*/
|
*/
|
||||||
public class InteractionJankMonitor {
|
public class InteractionJankMonitor {
|
||||||
private static final String TAG = InteractionJankMonitor.class.getSimpleName();
|
private static final String TAG = InteractionJankMonitor.class.getSimpleName();
|
||||||
|
private static final String ACTION_PREFIX = InteractionJankMonitor.class.getCanonicalName();
|
||||||
|
|
||||||
private static final String DEFAULT_WORKER_NAME = TAG + "-Worker";
|
private static final String DEFAULT_WORKER_NAME = TAG + "-Worker";
|
||||||
private static final long DEFAULT_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(5L);
|
private static final long DEFAULT_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(5L);
|
||||||
private static final String SETTINGS_ENABLED_KEY = "enabled";
|
private static final String SETTINGS_ENABLED_KEY = "enabled";
|
||||||
@@ -90,6 +98,14 @@ public class InteractionJankMonitor {
|
|||||||
private static final int DEFAULT_TRACE_THRESHOLD_MISSED_FRAMES = 3;
|
private static final int DEFAULT_TRACE_THRESHOLD_MISSED_FRAMES = 3;
|
||||||
private static final int DEFAULT_TRACE_THRESHOLD_FRAME_TIME_MILLIS = 64;
|
private static final int DEFAULT_TRACE_THRESHOLD_FRAME_TIME_MILLIS = 64;
|
||||||
|
|
||||||
|
public static final String ACTION_SESSION_BEGIN = ACTION_PREFIX + ".ACTION_SESSION_BEGIN";
|
||||||
|
public static final String ACTION_SESSION_END = ACTION_PREFIX + ".ACTION_SESSION_END";
|
||||||
|
public static final String ACTION_METRICS_LOGGED = ACTION_PREFIX + ".ACTION_METRICS_LOGGED";
|
||||||
|
public static final String BUNDLE_KEY_CUJ_NAME = ACTION_PREFIX + ".CUJ_NAME";
|
||||||
|
public static final String BUNDLE_KEY_TIMESTAMP = ACTION_PREFIX + ".TIMESTAMP";
|
||||||
|
@VisibleForTesting
|
||||||
|
public static final String PROP_NOTIFY_CUJ_EVENT = "debug.notify_cuj_events";
|
||||||
|
|
||||||
// Every value must have a corresponding entry in CUJ_STATSD_INTERACTION_TYPE.
|
// Every value must have a corresponding entry in CUJ_STATSD_INTERACTION_TYPE.
|
||||||
public static final int CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE = 0;
|
public static final int CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE = 0;
|
||||||
public static final int CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE_LOCK = 1;
|
public static final int CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE_LOCK = 1;
|
||||||
@@ -256,15 +272,28 @@ public class InteractionJankMonitor {
|
|||||||
*/
|
*/
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
public FrameTracker createFrameTracker(View v, Session session) {
|
public FrameTracker createFrameTracker(View v, Session session) {
|
||||||
|
final Context c = v.getContext().getApplicationContext();
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
|
boolean needListener = SystemProperties.getBoolean(PROP_NOTIFY_CUJ_EVENT, false);
|
||||||
|
FrameTrackerListener eventsListener =
|
||||||
|
!needListener ? null : (s, act) -> notifyEvents(c, act, s);
|
||||||
|
|
||||||
return new FrameTracker(session, mWorker.getThreadHandler(),
|
return new FrameTracker(session, mWorker.getThreadHandler(),
|
||||||
new ThreadedRendererWrapper(v.getThreadedRenderer()),
|
new ThreadedRendererWrapper(v.getThreadedRenderer()),
|
||||||
new ViewRootWrapper(v.getViewRootImpl()), new SurfaceControlWrapper(),
|
new ViewRootWrapper(v.getViewRootImpl()), new SurfaceControlWrapper(),
|
||||||
new ChoreographerWrapper(Choreographer.getInstance()), mMetrics,
|
new ChoreographerWrapper(Choreographer.getInstance()), mMetrics,
|
||||||
mTraceThresholdMissedFrames, mTraceThresholdFrameTimeMillis);
|
mTraceThresholdMissedFrames, mTraceThresholdFrameTimeMillis, eventsListener);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void notifyEvents(Context context, String action, Session session) {
|
||||||
|
Intent intent = new Intent(action);
|
||||||
|
intent.putExtra(BUNDLE_KEY_CUJ_NAME, getNameOfCuj(session.getCuj()));
|
||||||
|
intent.putExtra(BUNDLE_KEY_TIMESTAMP, session.getTimeStamp());
|
||||||
|
intent.addFlags(FLAG_RECEIVER_REGISTERED_ONLY);
|
||||||
|
context.sendBroadcast(intent);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Begin a trace session.
|
* Begin a trace session.
|
||||||
*
|
*
|
||||||
@@ -479,6 +508,7 @@ public class InteractionJankMonitor {
|
|||||||
public static class Session {
|
public static class Session {
|
||||||
@CujType
|
@CujType
|
||||||
private int mCujType;
|
private int mCujType;
|
||||||
|
private long mTimeStamp;
|
||||||
|
|
||||||
public Session(@CujType int cujType) {
|
public Session(@CujType int cujType) {
|
||||||
mCujType = cujType;
|
mCujType = cujType;
|
||||||
@@ -505,5 +535,13 @@ public class InteractionJankMonitor {
|
|||||||
public String getName() {
|
public String getName() {
|
||||||
return "J<" + getNameOfCuj(mCujType) + ">";
|
return "J<" + getNameOfCuj(mCujType) + ">";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setTimeStamp(long timeStamp) {
|
||||||
|
mTimeStamp = timeStamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getTimeStamp() {
|
||||||
|
return mTimeStamp;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -105,7 +105,8 @@ public class FrameTrackerTest {
|
|||||||
mTracker = Mockito.spy(
|
mTracker = Mockito.spy(
|
||||||
new FrameTracker(session, handler, mRenderer, mViewRootWrapper,
|
new FrameTracker(session, handler, mRenderer, mViewRootWrapper,
|
||||||
mSurfaceControlWrapper, mChoreographer, mWrapper,
|
mSurfaceControlWrapper, mChoreographer, mWrapper,
|
||||||
/*traceThresholdMissedFrames=*/ 1, /*traceThresholdFrameTimeMillis=*/ -1));
|
/*traceThresholdMissedFrames=*/ 1, /*traceThresholdFrameTimeMillis=*/ -1,
|
||||||
|
null));
|
||||||
doNothing().when(mTracker).triggerPerfetto();
|
doNothing().when(mTracker).triggerPerfetto();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ public class InteractionJankMonitorTest {
|
|||||||
new ViewRootWrapper(mView.getViewRootImpl()), new SurfaceControlWrapper(),
|
new ViewRootWrapper(mView.getViewRootImpl()), new SurfaceControlWrapper(),
|
||||||
mock(FrameTracker.ChoreographerWrapper.class),
|
mock(FrameTracker.ChoreographerWrapper.class),
|
||||||
new FrameMetricsWrapper(), /*traceThresholdMissedFrames=*/ 1,
|
new FrameMetricsWrapper(), /*traceThresholdMissedFrames=*/ 1,
|
||||||
/*traceThresholdFrameTimeMillis=*/ -1));
|
/*traceThresholdFrameTimeMillis=*/ -1, null));
|
||||||
doReturn(tracker).when(monitor).createFrameTracker(any(), any());
|
doReturn(tracker).when(monitor).createFrameTracker(any(), any());
|
||||||
|
|
||||||
// Simulate a trace session and see if begin / end are invoked.
|
// Simulate a trace session and see if begin / end are invoked.
|
||||||
@@ -123,21 +123,12 @@ public class InteractionJankMonitorTest {
|
|||||||
@Test
|
@Test
|
||||||
public void testCheckInitState() {
|
public void testCheckInitState() {
|
||||||
InteractionJankMonitor monitor = new InteractionJankMonitor(mWorker);
|
InteractionJankMonitor monitor = new InteractionJankMonitor(mWorker);
|
||||||
|
View view = new View(mActivity);
|
||||||
|
assertThat(view.isAttachedToWindow()).isFalse();
|
||||||
|
|
||||||
// Should return false if invoking begin / end without init invocation.
|
// Should return false if the view passed in is not attached to window yet.
|
||||||
assertThat(monitor.begin(mView, CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE)).isFalse();
|
assertThat(monitor.begin(view, CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE)).isFalse();
|
||||||
assertThat(monitor.end(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE)).isFalse();
|
assertThat(monitor.end(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE)).isFalse();
|
||||||
|
|
||||||
// Everything should be fine if invoking init first.
|
|
||||||
boolean thrown = false;
|
|
||||||
try {
|
|
||||||
assertThat(monitor.begin(mView, CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE)).isTrue();
|
|
||||||
assertThat(monitor.end(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE)).isTrue();
|
|
||||||
} catch (Exception ex) {
|
|
||||||
thrown = true;
|
|
||||||
} finally {
|
|
||||||
assertThat(thrown).isFalse();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -152,7 +143,7 @@ public class InteractionJankMonitorTest {
|
|||||||
new ViewRootWrapper(mView.getViewRootImpl()), new SurfaceControlWrapper(),
|
new ViewRootWrapper(mView.getViewRootImpl()), new SurfaceControlWrapper(),
|
||||||
mock(FrameTracker.ChoreographerWrapper.class),
|
mock(FrameTracker.ChoreographerWrapper.class),
|
||||||
new FrameMetricsWrapper(), /*traceThresholdMissedFrames=*/ 1,
|
new FrameMetricsWrapper(), /*traceThresholdMissedFrames=*/ 1,
|
||||||
/*traceThresholdFrameTimeMillis=*/ -1));
|
/*traceThresholdFrameTimeMillis=*/ -1, null));
|
||||||
doReturn(tracker).when(monitor).createFrameTracker(any(), any());
|
doReturn(tracker).when(monitor).createFrameTracker(any(), any());
|
||||||
|
|
||||||
assertThat(monitor.begin(mView, session.getCuj())).isTrue();
|
assertThat(monitor.begin(mView, session.getCuj())).isTrue();
|
||||||
|
|||||||
Reference in New Issue
Block a user