Merge "Send broadcast to notify the events of a CUJ session" into sc-dev am: f6ed5fc57c

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/13417775

MUST ONLY BE SUBMITTED BY AUTOMERGER

Change-Id: I0cfeee8ea4ed372f8da1e79689ea8082a2ddee48
This commit is contained in:
Wu Ahan
2021-02-24 03:04:27 +00:00
committed by Automerger Merge Worker
4 changed files with 73 additions and 19 deletions

View File

@@ -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);
}
} }

View File

@@ -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;
}
} }
} }

View File

@@ -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();
} }

View File

@@ -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();