Support surface only instrumentation

- Support surface only instrumentation.
- Add enums for pip and wallpaper instrumentation.

Bug: 170708749
Test: atest InteractionJankMonitorTest
Test: atest FrameTrackerTest
Change-Id: Iec1eb82d1a4756a7b85c8fbdd05f4132a74c613c
This commit is contained in:
Ahan Wu
2021-07-15 20:08:13 -08:00
parent 81e7de6251
commit 7141ab2619
8 changed files with 515 additions and 218 deletions

View File

@@ -45,6 +45,7 @@ import android.view.ThreadedRenderer;
import android.view.ViewRootImpl; import android.view.ViewRootImpl;
import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.jank.InteractionJankMonitor.Configuration;
import com.android.internal.jank.InteractionJankMonitor.Session; import com.android.internal.jank.InteractionJankMonitor.Session;
import com.android.internal.util.FrameworkStatsLog; import com.android.internal.util.FrameworkStatsLog;
@@ -69,6 +70,7 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
static final int REASON_CANCEL_NORMAL = 16; static final int REASON_CANCEL_NORMAL = 16;
static final int REASON_CANCEL_NOT_BEGUN = 17; static final int REASON_CANCEL_NOT_BEGUN = 17;
static final int REASON_CANCEL_SAME_VSYNC = 18; static final int REASON_CANCEL_SAME_VSYNC = 18;
static final int REASON_CANCEL_TIMEOUT = 19;
/** @hide */ /** @hide */
@IntDef({ @IntDef({
@@ -97,6 +99,9 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
private final Handler mHandler; private final Handler mHandler;
private final ChoreographerWrapper mChoreographer; private final ChoreographerWrapper mChoreographer;
@VisibleForTesting
public final boolean mSurfaceOnly;
private long mBeginVsyncId = INVALID_ID; private long mBeginVsyncId = INVALID_ID;
private long mEndVsyncId = INVALID_ID; private long mEndVsyncId = INVALID_ID;
private boolean mMetricsFinalized; private boolean mMetricsFinalized;
@@ -136,71 +141,86 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
} }
public FrameTracker(@NonNull Session session, @NonNull Handler handler, public FrameTracker(@NonNull Session session, @NonNull Handler handler,
@NonNull ThreadedRendererWrapper renderer, @NonNull ViewRootWrapper viewRootWrapper, @Nullable ThreadedRendererWrapper renderer, @Nullable ViewRootWrapper viewRootWrapper,
@NonNull SurfaceControlWrapper surfaceControlWrapper, @NonNull SurfaceControlWrapper surfaceControlWrapper,
@NonNull ChoreographerWrapper choreographer, @NonNull ChoreographerWrapper choreographer,
@NonNull FrameMetricsWrapper metrics, int traceThresholdMissedFrames, @Nullable FrameMetricsWrapper metrics,
int traceThresholdFrameTimeMillis, @Nullable FrameTrackerListener listener) { int traceThresholdMissedFrames, int traceThresholdFrameTimeMillis,
@Nullable FrameTrackerListener listener, @NonNull Configuration config) {
mSurfaceOnly = config.isSurfaceOnly();
mSession = session; mSession = session;
mRendererWrapper = renderer; mHandler = handler;
mMetricsWrapper = metrics;
mViewRoot = viewRootWrapper;
mChoreographer = choreographer; mChoreographer = choreographer;
mSurfaceControlWrapper = surfaceControlWrapper; mSurfaceControlWrapper = surfaceControlWrapper;
mHandler = handler;
mObserver = new HardwareRendererObserver( // HWUI instrumentation init.
this, mMetricsWrapper.getTiming(), handler, false /*waitForPresentTime*/); mRendererWrapper = mSurfaceOnly ? null : renderer;
mMetricsWrapper = mSurfaceOnly ? null : metrics;
mViewRoot = mSurfaceOnly ? null : viewRootWrapper;
mObserver = mSurfaceOnly
? null
: new HardwareRendererObserver(this, mMetricsWrapper.getTiming(),
handler, /* waitForPresentTime= */ false);
mTraceThresholdMissedFrames = traceThresholdMissedFrames; mTraceThresholdMissedFrames = traceThresholdMissedFrames;
mTraceThresholdFrameTimeMillis = traceThresholdFrameTimeMillis; mTraceThresholdFrameTimeMillis = traceThresholdFrameTimeMillis;
mListener = listener; mListener = listener;
// If the surface isn't valid yet, wait until it's created. if (mSurfaceOnly) {
if (viewRootWrapper.getSurfaceControl().isValid()) { mSurfaceControl = config.getSurfaceControl();
mSurfaceControl = viewRootWrapper.getSurfaceControl(); mSurfaceChangedCallback = null;
} else {
// HWUI instrumentation init.
// If the surface isn't valid yet, wait until it's created.
if (mViewRoot.getSurfaceControl().isValid()) {
mSurfaceControl = mViewRoot.getSurfaceControl();
mSurfaceChangedCallback = null;
} else {
mSurfaceChangedCallback = new ViewRootImpl.SurfaceChangedCallback() {
@Override
public void surfaceCreated(SurfaceControl.Transaction t) {
synchronized (FrameTracker.this) {
if (mSurfaceControl == null) {
mSurfaceControl = mViewRoot.getSurfaceControl();
if (mBeginVsyncId != INVALID_ID) {
mSurfaceControlWrapper.addJankStatsListener(
FrameTracker.this, mSurfaceControl);
postTraceStartMarker();
}
}
}
}
@Override
public void surfaceReplaced(SurfaceControl.Transaction t) {
}
@Override
public void surfaceDestroyed() {
// Wait a while to give the system a chance for the remaining
// frames to arrive, then force finish the session.
mHandler.postDelayed(() -> {
synchronized (FrameTracker.this) {
if (DEBUG) {
Log.d(TAG, "surfaceDestroyed: " + mSession.getName()
+ ", finalized=" + mMetricsFinalized
+ ", info=" + mJankInfos.size()
+ ", vsync=" + mBeginVsyncId + "-" + mEndVsyncId);
}
if (!mMetricsFinalized) {
end(REASON_END_SURFACE_DESTROYED);
finish(mJankInfos.size() - 1);
}
}
}, 50);
}
};
// This callback has a reference to FrameTracker,
// remember to remove it to avoid leakage.
mViewRoot.addSurfaceChangedCallback(mSurfaceChangedCallback);
}
} }
mSurfaceChangedCallback = new ViewRootImpl.SurfaceChangedCallback() {
@Override
public void surfaceCreated(SurfaceControl.Transaction t) {
synchronized (FrameTracker.this) {
if (mSurfaceControl == null) {
mSurfaceControl = viewRootWrapper.getSurfaceControl();
if (mBeginVsyncId != INVALID_ID) {
mSurfaceControlWrapper.addJankStatsListener(
FrameTracker.this, mSurfaceControl);
postTraceStartMarker();
}
}
}
}
@Override
public void surfaceReplaced(SurfaceControl.Transaction t) {
}
@Override
public void surfaceDestroyed() {
// Wait a while to give the system a chance for the remaining frames to arrive, then
// force finish the session.
mHandler.postDelayed(() -> {
synchronized (FrameTracker.this) {
if (DEBUG) {
Log.d(TAG, "surfaceDestroyed: " + mSession.getName()
+ ", finalized=" + mMetricsFinalized
+ ", info=" + mJankInfos.size()
+ ", vsync=" + mBeginVsyncId + "-" + mEndVsyncId);
}
if (!mMetricsFinalized) {
end(REASON_END_SURFACE_DESTROYED);
finish(mJankInfos.size() - 1);
}
}
}, 50);
}
};
// This callback has a reference to FrameTracker, remember to remove it to avoid leakage.
viewRootWrapper.addSurfaceChangedCallback(mSurfaceChangedCallback);
} }
/** /**
@@ -208,16 +228,16 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
*/ */
public synchronized void begin() { public synchronized void begin() {
mBeginVsyncId = mChoreographer.getVsyncId() + 1; mBeginVsyncId = mChoreographer.getVsyncId() + 1;
if (mSurfaceControl != null) {
postTraceStartMarker();
}
mRendererWrapper.addObserver(mObserver);
if (DEBUG) { if (DEBUG) {
Log.d(TAG, "begin: " + mSession.getName() + ", begin=" + mBeginVsyncId); Log.d(TAG, "begin: " + mSession.getName() + ", begin=" + mBeginVsyncId);
} }
if (mSurfaceControl != null) { if (mSurfaceControl != null) {
postTraceStartMarker();
mSurfaceControlWrapper.addJankStatsListener(this, mSurfaceControl); mSurfaceControlWrapper.addJankStatsListener(this, mSurfaceControl);
} }
if (!mSurfaceOnly) {
mRendererWrapper.addObserver(mObserver);
}
if (mListener != null) { if (mListener != null) {
mListener.onCujEvents(mSession, ACTION_SESSION_BEGIN); mListener.onCujEvents(mSession, ACTION_SESSION_BEGIN);
} }
@@ -273,11 +293,12 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
* Cancel the trace session of the CUJ. * Cancel the trace session of the CUJ.
*/ */
public synchronized void cancel(@Reasons int reason) { public synchronized void cancel(@Reasons int reason) {
mCancelled = true;
// We don't need to end the trace section if it never begun. // We don't need to end the trace section if it never begun.
if (mTracingStarted) { if (mTracingStarted) {
Trace.endAsyncSection(mSession.getName(), (int) mBeginVsyncId); Trace.endAsyncSection(mSession.getName(), (int) mBeginVsyncId);
} }
mCancelled = true;
// Always remove the observers in cancel call to avoid leakage. // Always remove the observers in cancel call to avoid leakage.
removeObservers(); removeObservers();
@@ -377,7 +398,7 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
for (int i = mJankInfos.size() - 1; i >= 0; i--) { for (int i = mJankInfos.size() - 1; i >= 0; i--) {
JankInfo info = mJankInfos.valueAt(i); JankInfo info = mJankInfos.valueAt(i);
if (info.frameVsyncId >= mEndVsyncId) { if (info.frameVsyncId >= mEndVsyncId) {
if (info.hwuiCallbackFired && info.surfaceControlCallbackFired) { if (isLastIndexCandidate(info)) {
lastIndex = i; lastIndex = i;
} }
} else { } else {
@@ -395,6 +416,12 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
finish(indexOnOrAfterEnd); finish(indexOnOrAfterEnd);
} }
private boolean isLastIndexCandidate(JankInfo info) {
return mSurfaceOnly
? info.surfaceControlCallbackFired
: info.hwuiCallbackFired && info.surfaceControlCallbackFired;
}
private void finish(int indexOnOrAfterEnd) { private void finish(int indexOnOrAfterEnd) {
mMetricsFinalized = true; mMetricsFinalized = true;
@@ -410,7 +437,8 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
for (int i = 0; i <= indexOnOrAfterEnd; i++) { for (int i = 0; i <= indexOnOrAfterEnd; i++) {
JankInfo info = mJankInfos.valueAt(i); JankInfo info = mJankInfos.valueAt(i);
if (info.isFirstFrame) { final boolean isFirstDrawn = !mSurfaceOnly && info.isFirstFrame;
if (isFirstDrawn) {
continue; continue;
} }
if (info.surfaceControlCallbackFired) { if (info.surfaceControlCallbackFired) {
@@ -435,11 +463,11 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
} }
// TODO (b/174755489): Early latch currently gets fired way too often, so we have // TODO (b/174755489): Early latch currently gets fired way too often, so we have
// to ignore it for now. // to ignore it for now.
if (!info.hwuiCallbackFired) { if (!mSurfaceOnly && !info.hwuiCallbackFired) {
Log.w(TAG, "Missing HWUI jank callback for vsyncId: " + info.frameVsyncId); Log.w(TAG, "Missing HWUI jank callback for vsyncId: " + info.frameVsyncId);
} }
} }
if (info.hwuiCallbackFired) { if (!mSurfaceOnly && info.hwuiCallbackFired) {
maxFrameTimeNanos = Math.max(info.totalDurationNanos, maxFrameTimeNanos); maxFrameTimeNanos = Math.max(info.totalDurationNanos, maxFrameTimeNanos);
if (!info.surfaceControlCallbackFired) { if (!info.surfaceControlCallbackFired) {
Log.w(TAG, "Missing SF jank callback for vsyncId: " + info.frameVsyncId); Log.w(TAG, "Missing SF jank callback for vsyncId: " + info.frameVsyncId);
@@ -462,7 +490,7 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
// Trigger perfetto if necessary. // Trigger perfetto if necessary.
boolean overMissedFramesThreshold = mTraceThresholdMissedFrames != -1 boolean overMissedFramesThreshold = mTraceThresholdMissedFrames != -1
&& missedFramesCount >= mTraceThresholdMissedFrames; && missedFramesCount >= mTraceThresholdMissedFrames;
boolean overFrameTimeThreshold = mTraceThresholdFrameTimeMillis != -1 boolean overFrameTimeThreshold = !mSurfaceOnly && mTraceThresholdFrameTimeMillis != -1
&& maxFrameTimeNanos >= mTraceThresholdFrameTimeMillis * NANOS_IN_MILLISECOND; && maxFrameTimeNanos >= mTraceThresholdFrameTimeMillis * NANOS_IN_MILLISECOND;
if (overMissedFramesThreshold || overFrameTimeThreshold) { if (overMissedFramesThreshold || overFrameTimeThreshold) {
triggerPerfetto(); triggerPerfetto();
@@ -473,7 +501,7 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
mSession.getStatsdInteractionType(), mSession.getStatsdInteractionType(),
totalFramesCount, totalFramesCount,
missedFramesCount, missedFramesCount,
maxFrameTimeNanos, maxFrameTimeNanos, /* will be 0 if mSurfaceOnly == true */
missedSfFramesCount, missedSfFramesCount,
missedAppFramesCount); missedAppFramesCount);
if (mListener != null) { if (mListener != null) {
@@ -496,10 +524,13 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
*/ */
@VisibleForTesting @VisibleForTesting
public void removeObservers() { public void removeObservers() {
mRendererWrapper.removeObserver(mObserver);
mSurfaceControlWrapper.removeJankStatsListener(this); mSurfaceControlWrapper.removeJankStatsListener(this);
if (mSurfaceChangedCallback != null) { if (!mSurfaceOnly) {
mViewRoot.removeSurfaceChangedCallback(mSurfaceChangedCallback); // HWUI part.
mRendererWrapper.removeObserver(mObserver);
if (mSurfaceChangedCallback != null) {
mViewRoot.removeSurfaceChangedCallback(mSurfaceChangedCallback);
}
} }
} }

View File

@@ -18,11 +18,11 @@ package com.android.internal.jank;
import static android.content.Intent.FLAG_RECEIVER_REGISTERED_ONLY; import static android.content.Intent.FLAG_RECEIVER_REGISTERED_ONLY;
import static com.android.internal.jank.FrameTracker.ChoreographerWrapper;
import static com.android.internal.jank.FrameTracker.REASON_CANCEL_NORMAL; import static com.android.internal.jank.FrameTracker.REASON_CANCEL_NORMAL;
import static com.android.internal.jank.FrameTracker.REASON_CANCEL_NOT_BEGUN; import static com.android.internal.jank.FrameTracker.REASON_CANCEL_NOT_BEGUN;
import static com.android.internal.jank.FrameTracker.REASON_CANCEL_TIMEOUT;
import static com.android.internal.jank.FrameTracker.REASON_END_NORMAL; import static com.android.internal.jank.FrameTracker.REASON_END_NORMAL;
import static com.android.internal.jank.FrameTracker.SurfaceControlWrapper; import static com.android.internal.jank.FrameTracker.REASON_END_UNKNOWN;
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;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_CLOSE_TO_HOME; import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_CLOSE_TO_HOME;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_CLOSE_TO_PIP; import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_CLOSE_TO_PIP;
@@ -41,6 +41,7 @@ import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_IN
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_TRANSITION_TO_AOD; import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_TRANSITION_TO_AOD;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_UNLOCK_ANIMATION; import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_UNLOCK_ANIMATION;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__NOTIFICATION_SHADE_SWIPE; import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__NOTIFICATION_SHADE_SWIPE;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__PIP_TRANSITION;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SETTINGS_PAGE_SCROLL; import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SETTINGS_PAGE_SCROLL;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH; import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_HISTORY_BUTTON; import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_HISTORY_BUTTON;
@@ -58,6 +59,7 @@ import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_IN
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_ROW_SWIPE; import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_ROW_SWIPE;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_SCROLL_FLING; import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_SCROLL_FLING;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP; import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__WALLPAPER_TRANSITION;
import android.annotation.IntDef; import android.annotation.IntDef;
import android.annotation.NonNull; import android.annotation.NonNull;
@@ -72,11 +74,15 @@ import android.text.TextUtils;
import android.util.Log; import android.util.Log;
import android.util.SparseArray; import android.util.SparseArray;
import android.view.Choreographer; import android.view.Choreographer;
import android.view.SurfaceControl;
import android.view.View; import android.view.View;
import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.jank.FrameTracker.ChoreographerWrapper;
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.FrameTrackerListener;
import com.android.internal.jank.FrameTracker.Reasons;
import com.android.internal.jank.FrameTracker.SurfaceControlWrapper;
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;
@@ -103,7 +109,7 @@ public class InteractionJankMonitor {
private static final String ACTION_PREFIX = InteractionJankMonitor.class.getCanonicalName(); 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(2L);
private static final String SETTINGS_ENABLED_KEY = "enabled"; private static final String SETTINGS_ENABLED_KEY = "enabled";
private static final String SETTINGS_SAMPLING_INTERVAL_KEY = "sampling_interval"; private static final String SETTINGS_SAMPLING_INTERVAL_KEY = "sampling_interval";
private static final String SETTINGS_THRESHOLD_MISSED_FRAMES_KEY = private static final String SETTINGS_THRESHOLD_MISSED_FRAMES_KEY =
@@ -163,6 +169,8 @@ public class InteractionJankMonitor {
public static final int CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE = 32; public static final int CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE = 32;
public static final int CUJ_SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON = 33; public static final int CUJ_SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON = 33;
public static final int CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP = 34; public static final int CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP = 34;
public static final int CUJ_PIP_TRANSITION = 35;
public static final int CUJ_WALLPAPER_TRANSITION = 36;
private static final int NO_STATSD_LOGGING = -1; private static final int NO_STATSD_LOGGING = -1;
@@ -206,6 +214,8 @@ public class InteractionJankMonitor {
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_QS_TILE, UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_QS_TILE,
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON, UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON,
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP, UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP,
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__PIP_TRANSITION,
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__WALLPAPER_TRANSITION,
}; };
private static volatile InteractionJankMonitor sInstance; private static volatile InteractionJankMonitor sInstance;
@@ -213,10 +223,10 @@ public class InteractionJankMonitor {
private final DeviceConfig.OnPropertiesChangedListener mPropertiesChangedListener = private final DeviceConfig.OnPropertiesChangedListener mPropertiesChangedListener =
this::updateProperties; this::updateProperties;
private FrameMetricsWrapper mMetrics; private final FrameMetricsWrapper mMetrics;
private SparseArray<FrameTracker> mRunningTrackers; private final SparseArray<FrameTracker> mRunningTrackers;
private SparseArray<Runnable> mTimeoutActions; private final SparseArray<Runnable> mTimeoutActions;
private HandlerThread mWorker; private final HandlerThread mWorker;
private boolean mEnabled = DEFAULT_ENABLED; private boolean mEnabled = DEFAULT_ENABLED;
private int mSamplingInterval = DEFAULT_SAMPLING_INTERVAL; private int mSamplingInterval = DEFAULT_SAMPLING_INTERVAL;
@@ -260,6 +270,8 @@ public class InteractionJankMonitor {
CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE, CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE,
CUJ_SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON, CUJ_SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON,
CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP, CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP,
CUJ_PIP_TRANSITION,
CUJ_WALLPAPER_TRANSITION,
}) })
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
public @interface CujType { public @interface CujType {
@@ -310,24 +322,31 @@ public class InteractionJankMonitor {
} }
/** /**
* Create a {@link FrameTracker} instance. * Creates a {@link FrameTracker} instance.
* *
* @param config the config used in instrumenting
* @param session the session associates with this tracker * @param session the session associates with this tracker
* @return instance of the FrameTracker * @return instance of the FrameTracker
*/ */
@VisibleForTesting @VisibleForTesting
public FrameTracker createFrameTracker(Configuration conf, Session session) { public FrameTracker createFrameTracker(Configuration config, Session session) {
final View v = conf.mView; final View view = config.mView;
final Context c = v.getContext().getApplicationContext(); final ThreadedRendererWrapper threadedRenderer =
final ThreadedRendererWrapper r = new ThreadedRendererWrapper(v.getThreadedRenderer()); view == null ? null : new ThreadedRendererWrapper(view.getThreadedRenderer());
final ViewRootWrapper vr = new ViewRootWrapper(v.getViewRootImpl()); final ViewRootWrapper viewRoot =
final SurfaceControlWrapper sc = new SurfaceControlWrapper(); view == null ? null : new ViewRootWrapper(view.getViewRootImpl());
final ChoreographerWrapper cg = new ChoreographerWrapper(Choreographer.getInstance());
final SurfaceControlWrapper surfaceControl = new SurfaceControlWrapper();
final ChoreographerWrapper choreographer =
new ChoreographerWrapper(Choreographer.getInstance());
synchronized (this) { synchronized (this) {
FrameTrackerListener eventsListener = (s, act) -> handleCujEvents(c, act, s); FrameTrackerListener eventsListener =
return new FrameTracker(session, mWorker.getThreadHandler(), r, vr, sc, cg, mMetrics, (s, act) -> handleCujEvents(config.getContext(), act, s);
mTraceThresholdMissedFrames, mTraceThresholdFrameTimeMillis, eventsListener); return new FrameTracker(session, mWorker.getThreadHandler(),
threadedRenderer, viewRoot, surfaceControl, choreographer, mMetrics,
mTraceThresholdMissedFrames, mTraceThresholdFrameTimeMillis,
eventsListener, config);
} }
} }
@@ -376,7 +395,7 @@ public class InteractionJankMonitor {
} }
/** /**
* Begin a trace session. * Begins a trace session.
* *
* @param v an attached view. * @param v an attached view.
* @param cujType the specific {@link InteractionJankMonitor.CujType}. * @param cujType the specific {@link InteractionJankMonitor.CujType}.
@@ -385,8 +404,7 @@ public class InteractionJankMonitor {
public boolean begin(View v, @CujType int cujType) { public boolean begin(View v, @CujType int cujType) {
try { try {
return beginInternal( return beginInternal(
new Configuration.Builder(cujType) Configuration.Builder.withView(cujType, v)
.setView(v)
.build()); .build());
} catch (IllegalArgumentException ex) { } catch (IllegalArgumentException ex) {
Log.d(TAG, "Build configuration failed!", ex); Log.d(TAG, "Build configuration failed!", ex);
@@ -395,7 +413,7 @@ public class InteractionJankMonitor {
} }
/** /**
* Begin a trace session. * Begins a trace session.
* *
* @param builder the builder of the configurations for instrumenting the CUJ. * @param builder the builder of the configurations for instrumenting the CUJ.
* @return boolean true if the tracker is started successfully, false otherwise. * @return boolean true if the tracker is started successfully, false otherwise.
@@ -431,48 +449,60 @@ public class InteractionJankMonitor {
tracker.begin(); tracker.begin();
// Cancel the trace if we don't get an end() call in specified duration. // Cancel the trace if we don't get an end() call in specified duration.
Runnable timeoutAction = () -> cancel(cujType); scheduleTimeoutAction(
mTimeoutActions.put(cujType, timeoutAction); cujType, conf.mTimeout, () -> cancel(cujType, REASON_CANCEL_TIMEOUT));
mWorker.getThreadHandler().postDelayed(timeoutAction, conf.mTimeout);
return true; return true;
} }
} }
/** /**
* End a trace session. * Schedules a timeout action.
* @param cuj cuj type
* @param timeout duration to timeout
* @param action action once timeout
*/
@VisibleForTesting
public void scheduleTimeoutAction(@CujType int cuj, long timeout, Runnable action) {
mTimeoutActions.put(cuj, action);
mWorker.getThreadHandler().postDelayed(action, timeout);
}
/**
* Ends a trace session.
* *
* @param cujType the specific {@link InteractionJankMonitor.CujType}. * @param cujType the specific {@link InteractionJankMonitor.CujType}.
* @return boolean true if the tracker is ended successfully, false otherwise. * @return boolean true if the tracker is ended successfully, false otherwise.
*/ */
public boolean end(@CujType int cujType) { public boolean end(@CujType int cujType) {
//TODO (163505250): This should be no-op if not in droid food rom.
synchronized (this) { synchronized (this) {
// remove the timeout action first. // remove the timeout action first.
removeTimeout(cujType); removeTimeout(cujType);
FrameTracker tracker = getTracker(cujType); FrameTracker tracker = getTracker(cujType);
// Skip this call since we haven't started a trace yet. // Skip this call since we haven't started a trace yet.
if (tracker == null) return false; if (tracker == null) return false;
tracker.end(FrameTracker.REASON_END_NORMAL); tracker.end(REASON_END_NORMAL);
removeTracker(cujType); removeTracker(cujType);
return true; return true;
} }
} }
/** /**
* Cancel the trace session. * Cancels the trace session.
* *
* @return boolean true if the tracker is cancelled successfully, false otherwise. * @return boolean true if the tracker is cancelled successfully, false otherwise.
*/ */
public boolean cancel(@CujType int cujType) { public boolean cancel(@CujType int cujType) {
//TODO (163505250): This should be no-op if not in droid food rom. return cancel(cujType, REASON_CANCEL_NORMAL);
}
boolean cancel(@CujType int cujType, @Reasons int reason) {
synchronized (this) { synchronized (this) {
// remove the timeout action first. // remove the timeout action first.
removeTimeout(cujType); removeTimeout(cujType);
FrameTracker tracker = getTracker(cujType); FrameTracker tracker = getTracker(cujType);
// Skip this call since we haven't started a trace yet. // Skip this call since we haven't started a trace yet.
if (tracker == null) return false; if (tracker == null) return false;
tracker.cancel(FrameTracker.REASON_CANCEL_NORMAL); tracker.cancel(reason);
removeTracker(cujType); removeTracker(cujType);
return true; return true;
} }
@@ -509,7 +539,7 @@ public class InteractionJankMonitor {
} }
/** /**
* Trigger the perfetto daemon to collect and upload data. * Triggers the perfetto daemon to collect and upload data.
*/ */
@VisibleForTesting @VisibleForTesting
public void trigger(Session session) { public void trigger(Session session) {
@@ -608,6 +638,10 @@ public class InteractionJankMonitor {
return "SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON"; return "SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON";
case CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP: case CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP:
return "STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP"; return "STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP";
case CUJ_PIP_TRANSITION:
return "PIP_TRANSITION";
case CUJ_WALLPAPER_TRANSITION:
return "WALLPAPER_TRANSITION";
} }
return "UNKNOWN"; return "UNKNOWN";
} }
@@ -618,32 +652,64 @@ public class InteractionJankMonitor {
*/ */
public static class Configuration { public static class Configuration {
private final View mView; private final View mView;
private final Context mContext;
private final long mTimeout; private final long mTimeout;
private final String mTag; private final String mTag;
private final boolean mSurfaceOnly;
private final SurfaceControl mSurfaceControl;
private final @CujType int mCujType; private final @CujType int mCujType;
/** /**
* A builder for building Configuration. <br/> * A builder for building Configuration. {@link #setView(View)} is essential
* if {@link #setSurfaceOnly(boolean)} is not set, otherwise both
* {@link #setSurfaceControl(SurfaceControl)} and {@link #setContext(Context)}
* are necessary<br/>
* <b>It may refer to an attached view, don't use static reference for any purpose.</b> * <b>It may refer to an attached view, don't use static reference for any purpose.</b>
*/ */
public static class Builder { public static class Builder {
private View mAttrView = null; private View mAttrView = null;
private Context mAttrContext = null;
private long mAttrTimeout = DEFAULT_TIMEOUT_MS; private long mAttrTimeout = DEFAULT_TIMEOUT_MS;
private String mAttrTag = ""; private String mAttrTag = "";
private boolean mAttrSurfaceOnly;
private SurfaceControl mAttrSurfaceControl;
private @CujType int mAttrCujType; private @CujType int mAttrCujType;
/** /**
* Creates a builder which instruments only surface.
* @param cuj The enum defined in {@link InteractionJankMonitor.CujType}. * @param cuj The enum defined in {@link InteractionJankMonitor.CujType}.
* @param context context
* @param surfaceControl surface control
* @return builder
*/ */
public Builder(@CujType int cuj) { public static Builder withSurface(@CujType int cuj, @NonNull Context context,
@NonNull SurfaceControl surfaceControl) {
return new Builder(cuj)
.setContext(context)
.setSurfaceControl(surfaceControl)
.setSurfaceOnly(true);
}
/**
* Creates a builder which instruments both surface and view.
* @param cuj The enum defined in {@link InteractionJankMonitor.CujType}.
* @param view view
* @return builder
*/
public static Builder withView(@CujType int cuj, @NonNull View view) {
return new Builder(cuj).setView(view);
}
private Builder(@CujType int cuj) {
mAttrCujType = cuj; mAttrCujType = cuj;
} }
/** /**
* Specifies a view, must be set if {@link #setSurfaceOnly(boolean)} is set to false.
* @param view an attached view * @param view an attached view
* @return builder * @return builder
*/ */
public Builder setView(@NonNull View view) { private Builder setView(@NonNull View view) {
mAttrView = view; mAttrView = view;
return this; return this;
} }
@@ -669,20 +735,56 @@ public class InteractionJankMonitor {
} }
/** /**
* Build the {@link Configuration} instance * Indicates if only instrument with surface,
* if true, must also setup with {@link #setContext(Context)}
* and {@link #setSurfaceControl(SurfaceControl)}.
* @param surfaceOnly true if only instrument with surface, false otherwise
* @return builder Surface only builder.
*/
private Builder setSurfaceOnly(boolean surfaceOnly) {
mAttrSurfaceOnly = surfaceOnly;
return this;
}
/**
* Specifies a context, must set if {@link #setSurfaceOnly(boolean)} is set.
*/
private Builder setContext(Context context) {
mAttrContext = context;
return this;
}
/**
* Specifies a surface control, must be set if {@link #setSurfaceOnly(boolean)} is set.
*/
private Builder setSurfaceControl(SurfaceControl surfaceControl) {
mAttrSurfaceControl = surfaceControl;
return this;
}
/**
* Builds the {@link Configuration} instance
* @return the instance of {@link Configuration} * @return the instance of {@link Configuration}
* @throws IllegalArgumentException if any invalid attribute is set * @throws IllegalArgumentException if any invalid attribute is set
*/ */
public Configuration build() throws IllegalArgumentException { public Configuration build() throws IllegalArgumentException {
return new Configuration(mAttrCujType, mAttrView, mAttrTag, mAttrTimeout); return new Configuration(
mAttrCujType, mAttrView, mAttrTag, mAttrTimeout,
mAttrSurfaceOnly, mAttrContext, mAttrSurfaceControl);
} }
} }
private Configuration(@CujType int cuj, View view, String tag, long timeout) { private Configuration(@CujType int cuj, View view, String tag, long timeout,
boolean surfaceOnly, Context context, SurfaceControl surfaceControl) {
mCujType = cuj; mCujType = cuj;
mTag = tag; mTag = tag;
mTimeout = timeout; mTimeout = timeout;
mView = view; mView = view;
mSurfaceOnly = surfaceOnly;
mContext = context != null
? context
: (view != null ? view.getContext().getApplicationContext() : null);
mSurfaceControl = surfaceControl;
validate(); validate();
} }
@@ -698,14 +800,47 @@ public class InteractionJankMonitor {
shouldThrow = true; shouldThrow = true;
msg.append("Invalid timeout value; "); msg.append("Invalid timeout value; ");
} }
if (mView == null || !mView.isAttachedToWindow()) { if (mSurfaceOnly) {
shouldThrow = true; if (mContext == null) {
msg.append("Null view or view is not attached yet; "); shouldThrow = true;
msg.append("Must pass in a context if only instrument surface; ");
}
if (mSurfaceControl == null || !mSurfaceControl.isValid()) {
shouldThrow = true;
msg.append("Must pass in a valid surface control if only instrument surface; ");
}
} else {
if (mView == null || !mView.isAttachedToWindow()) {
shouldThrow = true;
msg.append("Null view or unattached view while instrumenting view; ");
}
} }
if (shouldThrow) { if (shouldThrow) {
throw new IllegalArgumentException(msg.toString()); throw new IllegalArgumentException(msg.toString());
} }
} }
/**
* @return true if only instrumenting surface, false otherwise
*/
public boolean isSurfaceOnly() {
return mSurfaceOnly;
}
/**
* @return the surafce control which is instrumenting
*/
public SurfaceControl getSurfaceControl() {
return mSurfaceControl;
}
View getView() {
return mView;
}
Context getContext() {
return mContext;
}
} }
/** /**
@@ -715,8 +850,8 @@ public class InteractionJankMonitor {
@CujType @CujType
private final int mCujType; private final int mCujType;
private final long mTimeStamp; private final long mTimeStamp;
@FrameTracker.Reasons @Reasons
private int mReason = FrameTracker.REASON_END_UNKNOWN; private int mReason = REASON_END_UNKNOWN;
private final boolean mShouldNotify; private final boolean mShouldNotify;
private final String mName; private final String mName;
@@ -756,15 +891,15 @@ public class InteractionJankMonitor {
return mTimeStamp; return mTimeStamp;
} }
public void setReason(@FrameTracker.Reasons int reason) { public void setReason(@Reasons int reason) {
mReason = reason; mReason = reason;
} }
public int getReason() { public @Reasons int getReason() {
return mReason; return mReason;
} }
/** Determine if should notify the receivers of cuj events */ /** Determines if should notify the receivers of cuj events */
public boolean shouldNotify() { public boolean shouldNotify() {
return mShouldNotify; return mShouldNotify;
} }

View File

@@ -23,6 +23,7 @@ import static android.view.SurfaceControl.JankData.JANK_SURFACEFLINGER_DEADLINE_
import static com.android.internal.jank.FrameTracker.SurfaceControlWrapper; import static com.android.internal.jank.FrameTracker.SurfaceControlWrapper;
import static com.android.internal.jank.FrameTracker.ViewRootWrapper; import static com.android.internal.jank.FrameTracker.ViewRootWrapper;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE; import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_WALLPAPER_TRANSITION;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
@@ -50,6 +51,7 @@ import androidx.test.rule.ActivityTestRule;
import com.android.internal.jank.FrameTracker.ChoreographerWrapper; import com.android.internal.jank.FrameTracker.ChoreographerWrapper;
import com.android.internal.jank.FrameTracker.FrameMetricsWrapper; import com.android.internal.jank.FrameTracker.FrameMetricsWrapper;
import com.android.internal.jank.FrameTracker.ThreadedRendererWrapper; import com.android.internal.jank.FrameTracker.ThreadedRendererWrapper;
import com.android.internal.jank.InteractionJankMonitor.Configuration;
import com.android.internal.jank.InteractionJankMonitor.Session; import com.android.internal.jank.InteractionJankMonitor.Session;
import org.junit.Before; import org.junit.Before;
@@ -69,7 +71,6 @@ public class FrameTrackerTest {
public ActivityTestRule<ViewAttachTestActivity> mRule = public ActivityTestRule<ViewAttachTestActivity> mRule =
new ActivityTestRule<>(ViewAttachTestActivity.class); new ActivityTestRule<>(ViewAttachTestActivity.class);
private FrameTracker mTracker;
private ThreadedRendererWrapper mRenderer; private ThreadedRendererWrapper mRenderer;
private FrameMetricsWrapper mWrapper; private FrameMetricsWrapper mWrapper;
private SurfaceControlWrapper mSurfaceControlWrapper; private SurfaceControlWrapper mSurfaceControlWrapper;
@@ -85,7 +86,6 @@ public class FrameTrackerTest {
View view = mActivity.getWindow().getDecorView(); View view = mActivity.getWindow().getDecorView();
assertThat(view.isAttachedToWindow()).isTrue(); assertThat(view.isAttachedToWindow()).isTrue();
Handler handler = mRule.getActivity().getMainThreadHandler();
mWrapper = Mockito.spy(new FrameMetricsWrapper()); mWrapper = Mockito.spy(new FrameMetricsWrapper());
mRenderer = Mockito.spy(new ThreadedRendererWrapper(view.getThreadedRenderer())); mRenderer = Mockito.spy(new ThreadedRendererWrapper(view.getThreadedRenderer()));
doNothing().when(mRenderer).addObserver(any()); doNothing().when(mRenderer).addObserver(any());
@@ -103,229 +103,355 @@ public class FrameTrackerTest {
mListenerCapture.capture()); mListenerCapture.capture());
mChoreographer = mock(ChoreographerWrapper.class); mChoreographer = mock(ChoreographerWrapper.class);
}
Session session = new Session(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, CUJ_POSTFIX); private FrameTracker spyFrameTracker(int cuj, String postfix, boolean surfaceOnly) {
mTracker = Mockito.spy( Handler handler = mRule.getActivity().getMainThreadHandler();
Session session = new Session(cuj, postfix);
Configuration config = mock(Configuration.class);
when(config.isSurfaceOnly()).thenReturn(surfaceOnly);
when(config.getSurfaceControl()).thenReturn(mSurfaceControl);
FrameTracker frameTracker = 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,
null)); /* traceThresholdFrameTimeMillis= */ -1,
doNothing().when(mTracker).triggerPerfetto(); /* FrameTrackerListener= */ null, config));
doNothing().when(mTracker).postTraceStartMarker(); doNothing().when(frameTracker).triggerPerfetto();
doNothing().when(frameTracker).postTraceStartMarker();
return frameTracker;
} }
@Test @Test
public void testOnlyFirstWindowFrameOverThreshold() { public void testOnlyFirstWindowFrameOverThreshold() {
FrameTracker tracker = spyFrameTracker(
CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, CUJ_POSTFIX, /* surfaceOnly= */ false);
// Just provide current timestamp anytime mWrapper asked for VSYNC_TIMESTAMP // Just provide current timestamp anytime mWrapper asked for VSYNC_TIMESTAMP
when(mWrapper.getMetric(FrameMetrics.VSYNC_TIMESTAMP)) when(mWrapper.getMetric(FrameMetrics.VSYNC_TIMESTAMP))
.then(unusedInvocation -> System.nanoTime()); .then(unusedInvocation -> System.nanoTime());
when(mChoreographer.getVsyncId()).thenReturn(100L); when(mChoreographer.getVsyncId()).thenReturn(100L);
mTracker.begin(); tracker.begin();
verify(mRenderer, only()).addObserver(any()); verify(mRenderer, only()).addObserver(any());
// send first frame with a long duration - should not be taken into account // send first frame with a long duration - should not be taken into account
sendFirstWindowFrame(100, JANK_APP_DEADLINE_MISSED, 100L); sendFirstWindowFrame(tracker, 100, JANK_APP_DEADLINE_MISSED, 100L);
// send another frame with a short duration - should not be considered janky // send another frame with a short duration - should not be considered janky
sendFirstWindowFrame(5, JANK_NONE, 101L); sendFirstWindowFrame(tracker, 5, JANK_NONE, 101L);
// end the trace session, the last janky frame is after the end() so is discarded. // end the trace session, the last janky frame is after the end() so is discarded.
when(mChoreographer.getVsyncId()).thenReturn(102L); when(mChoreographer.getVsyncId()).thenReturn(102L);
mTracker.end(FrameTracker.REASON_END_NORMAL); tracker.end(FrameTracker.REASON_END_NORMAL);
sendFrame(5, JANK_NONE, 102L); sendFrame(tracker, 5, JANK_NONE, 102L);
sendFrame(500, JANK_APP_DEADLINE_MISSED, 103L); sendFrame(tracker, 500, JANK_APP_DEADLINE_MISSED, 103L);
verify(mTracker).removeObservers(); verify(tracker).removeObservers();
verify(mTracker, never()).triggerPerfetto(); verify(tracker, never()).triggerPerfetto();
} }
@Test @Test
public void testSfJank() { public void testSfJank() {
FrameTracker tracker = spyFrameTracker(
CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, CUJ_POSTFIX, /* surfaceOnly= */ false);
when(mChoreographer.getVsyncId()).thenReturn(100L); when(mChoreographer.getVsyncId()).thenReturn(100L);
mTracker.begin(); tracker.begin();
verify(mRenderer, only()).addObserver(any()); verify(mRenderer, only()).addObserver(any());
// send first frame - not janky // send first frame - not janky
sendFrame(4, JANK_NONE, 100L); sendFrame(tracker, 4, JANK_NONE, 100L);
// send another frame - should be considered janky // send another frame - should be considered janky
sendFrame(40, JANK_SURFACEFLINGER_DEADLINE_MISSED, 101L); sendFrame(tracker, 40, JANK_SURFACEFLINGER_DEADLINE_MISSED, 101L);
// end the trace session // end the trace session
when(mChoreographer.getVsyncId()).thenReturn(102L); when(mChoreographer.getVsyncId()).thenReturn(102L);
mTracker.end(FrameTracker.REASON_END_NORMAL); tracker.end(FrameTracker.REASON_END_NORMAL);
sendFrame(4, JANK_NONE, 102L); sendFrame(tracker, 4, JANK_NONE, 102L);
verify(mTracker).removeObservers(); verify(tracker).removeObservers();
// We detected a janky frame - trigger Perfetto // We detected a janky frame - trigger Perfetto
verify(mTracker).triggerPerfetto(); verify(tracker).triggerPerfetto();
} }
@Test @Test
public void testFirstFrameJankyNoTrigger() { public void testFirstFrameJankyNoTrigger() {
FrameTracker tracker = spyFrameTracker(
CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, CUJ_POSTFIX, /* surfaceOnly= */ false);
when(mChoreographer.getVsyncId()).thenReturn(100L); when(mChoreographer.getVsyncId()).thenReturn(100L);
mTracker.begin(); tracker.begin();
verify(mRenderer, only()).addObserver(any()); verify(mRenderer, only()).addObserver(any());
// send first frame - janky // send first frame - janky
sendFrame(40, JANK_APP_DEADLINE_MISSED, 100L); sendFrame(tracker, 40, JANK_APP_DEADLINE_MISSED, 100L);
// send another frame - not jank // send another frame - not jank
sendFrame(4, JANK_NONE, 101L); sendFrame(tracker, 4, JANK_NONE, 101L);
// end the trace session // end the trace session
when(mChoreographer.getVsyncId()).thenReturn(102L); when(mChoreographer.getVsyncId()).thenReturn(102L);
mTracker.end(FrameTracker.REASON_END_NORMAL); tracker.end(FrameTracker.REASON_END_NORMAL);
sendFrame(4, JANK_NONE, 102L); sendFrame(tracker, 4, JANK_NONE, 102L);
verify(mTracker).removeObservers(); verify(tracker).removeObservers();
// We detected a janky frame - trigger Perfetto // We detected a janky frame - trigger Perfetto
verify(mTracker, never()).triggerPerfetto(); verify(tracker, never()).triggerPerfetto();
} }
@Test @Test
public void testOtherFrameOverThreshold() { public void testOtherFrameOverThreshold() {
FrameTracker tracker = spyFrameTracker(
CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, CUJ_POSTFIX, /* surfaceOnly= */ false);
when(mChoreographer.getVsyncId()).thenReturn(100L); when(mChoreographer.getVsyncId()).thenReturn(100L);
mTracker.begin(); tracker.begin();
verify(mRenderer, only()).addObserver(any()); verify(mRenderer, only()).addObserver(any());
// send first frame - not janky // send first frame - not janky
sendFrame(4, JANK_NONE, 100L); sendFrame(tracker, 4, JANK_NONE, 100L);
// send another frame - should be considered janky // send another frame - should be considered janky
sendFrame(40, JANK_APP_DEADLINE_MISSED, 101L); sendFrame(tracker, 40, JANK_APP_DEADLINE_MISSED, 101L);
// end the trace session // end the trace session
when(mChoreographer.getVsyncId()).thenReturn(102L); when(mChoreographer.getVsyncId()).thenReturn(102L);
mTracker.end(FrameTracker.REASON_END_NORMAL); tracker.end(FrameTracker.REASON_END_NORMAL);
sendFrame(4, JANK_NONE, 102L); sendFrame(tracker, 4, JANK_NONE, 102L);
verify(mTracker).removeObservers(); verify(tracker).removeObservers();
// We detected a janky frame - trigger Perfetto // We detected a janky frame - trigger Perfetto
verify(mTracker).triggerPerfetto(); verify(tracker).triggerPerfetto();
} }
@Test @Test
public void testLastFrameOverThresholdBeforeEnd() { public void testLastFrameOverThresholdBeforeEnd() {
FrameTracker tracker = spyFrameTracker(
CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, CUJ_POSTFIX, /* surfaceOnly= */ false);
when(mChoreographer.getVsyncId()).thenReturn(100L); when(mChoreographer.getVsyncId()).thenReturn(100L);
mTracker.begin(); tracker.begin();
verify(mRenderer, only()).addObserver(any()); verify(mRenderer, only()).addObserver(any());
// send first frame - not janky // send first frame - not janky
sendFrame(4, JANK_NONE, 100L); sendFrame(tracker, 4, JANK_NONE, 100L);
// send another frame - not janky // send another frame - not janky
sendFrame(4, JANK_NONE, 101L); sendFrame(tracker, 4, JANK_NONE, 101L);
// end the trace session, simulate one more valid callback came after the end call. // end the trace session, simulate one more valid callback came after the end call.
when(mChoreographer.getVsyncId()).thenReturn(102L); when(mChoreographer.getVsyncId()).thenReturn(102L);
mTracker.end(FrameTracker.REASON_END_NORMAL); tracker.end(FrameTracker.REASON_END_NORMAL);
sendFrame(50, JANK_APP_DEADLINE_MISSED, 102L); sendFrame(tracker, 50, JANK_APP_DEADLINE_MISSED, 102L);
// One more callback with VSYNC after the end() vsync id. // One more callback with VSYNC after the end() vsync id.
sendFrame(4, JANK_NONE, 103L); sendFrame(tracker, 4, JANK_NONE, 103L);
verify(mTracker).removeObservers(); verify(tracker).removeObservers();
// We detected a janky frame - trigger Perfetto // We detected a janky frame - trigger Perfetto
verify(mTracker).triggerPerfetto(); verify(tracker).triggerPerfetto();
} }
@Test @Test
public void testBeginCancel() { public void testBeginCancel() {
FrameTracker tracker = spyFrameTracker(
CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, CUJ_POSTFIX, /* surfaceOnly= */ false);
when(mChoreographer.getVsyncId()).thenReturn(100L); when(mChoreographer.getVsyncId()).thenReturn(100L);
mTracker.begin(); tracker.begin();
verify(mRenderer).addObserver(any()); verify(mRenderer).addObserver(any());
// First frame - not janky // First frame - not janky
sendFrame(4, JANK_NONE, 100L); sendFrame(tracker, 4, JANK_NONE, 100L);
// normal frame - not janky // normal frame - not janky
sendFrame(4, JANK_NONE, 101L); sendFrame(tracker, 4, JANK_NONE, 101L);
// a janky frame // a janky frame
sendFrame(50, JANK_APP_DEADLINE_MISSED, 102L); sendFrame(tracker, 50, JANK_APP_DEADLINE_MISSED, 102L);
mTracker.cancel(FrameTracker.REASON_CANCEL_NORMAL); tracker.cancel(FrameTracker.REASON_CANCEL_NORMAL);
verify(mTracker).removeObservers(); verify(tracker).removeObservers();
// Since the tracker has been cancelled, shouldn't trigger perfetto. // Since the tracker has been cancelled, shouldn't trigger perfetto.
verify(mTracker, never()).triggerPerfetto(); verify(tracker, never()).triggerPerfetto();
} }
@Test @Test
public void testCancelIfEndVsyncIdEqualsToBeginVsyncId() { public void testCancelIfEndVsyncIdEqualsToBeginVsyncId() {
FrameTracker tracker = spyFrameTracker(
CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, CUJ_POSTFIX, /* surfaceOnly= */ false);
when(mChoreographer.getVsyncId()).thenReturn(100L); when(mChoreographer.getVsyncId()).thenReturn(100L);
mTracker.begin(); tracker.begin();
verify(mRenderer, only()).addObserver(any()); verify(mRenderer, only()).addObserver(any());
// end the trace session // end the trace session
when(mChoreographer.getVsyncId()).thenReturn(101L); when(mChoreographer.getVsyncId()).thenReturn(101L);
mTracker.end(FrameTracker.REASON_END_NORMAL); tracker.end(FrameTracker.REASON_END_NORMAL);
// Since the begin vsync id (101) equals to the end vsync id (101), will be treat as cancel. // Since the begin vsync id (101) equals to the end vsync id (101), will be treat as cancel.
verify(mTracker).cancel(FrameTracker.REASON_CANCEL_SAME_VSYNC); verify(tracker).cancel(FrameTracker.REASON_CANCEL_SAME_VSYNC);
// Observers should be removed in this case, or FrameTracker object will be leaked. // Observers should be removed in this case, or FrameTracker object will be leaked.
verify(mTracker).removeObservers(); verify(tracker).removeObservers();
// Should never trigger Perfetto since it is a cancel. // Should never trigger Perfetto since it is a cancel.
verify(mTracker, never()).triggerPerfetto(); verify(tracker, never()).triggerPerfetto();
} }
@Test @Test
public void testCancelIfEndVsyncIdLessThanBeginVsyncId() { public void testCancelIfEndVsyncIdLessThanBeginVsyncId() {
FrameTracker tracker = spyFrameTracker(
CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, CUJ_POSTFIX, /* surfaceOnly= */ false);
when(mChoreographer.getVsyncId()).thenReturn(100L); when(mChoreographer.getVsyncId()).thenReturn(100L);
mTracker.begin(); tracker.begin();
verify(mRenderer, only()).addObserver(any()); verify(mRenderer, only()).addObserver(any());
// end the trace session at the same vsync id, end vsync id will less than the begin one. // end the trace session at the same vsync id, end vsync id will less than the begin one.
// Because the begin vsync id is supposed to the next frame, // Because the begin vsync id is supposed to the next frame,
mTracker.end(FrameTracker.REASON_END_NORMAL); tracker.end(FrameTracker.REASON_END_NORMAL);
// The begin vsync id (101) is larger than the end one (100), will be treat as cancel. // The begin vsync id (101) is larger than the end one (100), will be treat as cancel.
verify(mTracker).cancel(FrameTracker.REASON_CANCEL_SAME_VSYNC); verify(tracker).cancel(FrameTracker.REASON_CANCEL_SAME_VSYNC);
// Observers should be removed in this case, or FrameTracker object will be leaked. // Observers should be removed in this case, or FrameTracker object will be leaked.
verify(mTracker).removeObservers(); verify(tracker).removeObservers();
// Should never trigger Perfetto since it is a cancel. // Should never trigger Perfetto since it is a cancel.
verify(mTracker, never()).triggerPerfetto(); verify(tracker, never()).triggerPerfetto();
} }
@Test @Test
public void testCancelWhenSessionNeverBegun() { public void testCancelWhenSessionNeverBegun() {
mTracker.cancel(FrameTracker.REASON_CANCEL_NORMAL); FrameTracker tracker = spyFrameTracker(
verify(mTracker).removeObservers(); CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, CUJ_POSTFIX, /* surfaceOnly= */ false);
tracker.cancel(FrameTracker.REASON_CANCEL_NORMAL);
verify(tracker).removeObservers();
} }
@Test @Test
public void testEndWhenSessionNeverBegun() { public void testEndWhenSessionNeverBegun() {
mTracker.end(FrameTracker.REASON_END_NORMAL); FrameTracker tracker = spyFrameTracker(
verify(mTracker).removeObservers(); CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, CUJ_POSTFIX, /* surfaceOnly= */ false);
tracker.end(FrameTracker.REASON_END_NORMAL);
verify(tracker).removeObservers();
} }
private void sendFirstWindowFrame(long durationMillis, @Test
public void testSurfaceOnlyOtherFrameJanky() {
FrameTracker tracker = spyFrameTracker(
CUJ_WALLPAPER_TRANSITION, CUJ_POSTFIX, /* surfaceOnly= */ true);
when(mChoreographer.getVsyncId()).thenReturn(100L);
tracker.begin();
verify(mSurfaceControlWrapper).addJankStatsListener(any(), any());
// First frame - not janky
sendFrame(tracker, JANK_NONE, 100L);
// normal frame - not janky
sendFrame(tracker, JANK_NONE, 101L);
// a janky frame
sendFrame(tracker, JANK_APP_DEADLINE_MISSED, 102L);
when(mChoreographer.getVsyncId()).thenReturn(102L);
tracker.end(FrameTracker.REASON_CANCEL_NORMAL);
// an extra frame to trigger finish
sendFrame(tracker, JANK_NONE, 103L);
verify(mSurfaceControlWrapper).removeJankStatsListener(any());
verify(tracker).triggerPerfetto();
}
@Test
public void testSurfaceOnlyFirstFrameJanky() {
FrameTracker tracker = spyFrameTracker(
CUJ_WALLPAPER_TRANSITION, CUJ_POSTFIX, /* surfaceOnly= */ true);
when(mChoreographer.getVsyncId()).thenReturn(100L);
tracker.begin();
verify(mSurfaceControlWrapper).addJankStatsListener(any(), any());
// First frame - janky
sendFrame(tracker, JANK_APP_DEADLINE_MISSED, 100L);
// normal frame - not janky
sendFrame(tracker, JANK_NONE, 101L);
// normal frame - not janky
sendFrame(tracker, JANK_NONE, 102L);
when(mChoreographer.getVsyncId()).thenReturn(102L);
tracker.end(FrameTracker.REASON_CANCEL_NORMAL);
// an extra frame to trigger finish
sendFrame(tracker, JANK_NONE, 103L);
verify(mSurfaceControlWrapper).removeJankStatsListener(any());
verify(tracker, never()).triggerPerfetto();
}
@Test
public void testSurfaceOnlyLastFrameJanky() {
FrameTracker tracker = spyFrameTracker(
CUJ_WALLPAPER_TRANSITION, CUJ_POSTFIX, /* surfaceOnly= */ true);
when(mChoreographer.getVsyncId()).thenReturn(100L);
tracker.begin();
verify(mSurfaceControlWrapper).addJankStatsListener(any(), any());
// First frame - not janky
sendFrame(tracker, JANK_NONE, 100L);
// normal frame - not janky
sendFrame(tracker, JANK_NONE, 101L);
// normal frame - not janky
sendFrame(tracker, JANK_NONE, 102L);
when(mChoreographer.getVsyncId()).thenReturn(102L);
tracker.end(FrameTracker.REASON_CANCEL_NORMAL);
// janky frame, should be ignored, trigger finish
sendFrame(tracker, JANK_APP_DEADLINE_MISSED, 103L);
verify(mSurfaceControlWrapper).removeJankStatsListener(any());
verify(tracker, never()).triggerPerfetto();
}
private void sendFirstWindowFrame(FrameTracker tracker, long durationMillis,
@JankType int jankType, long vsyncId) { @JankType int jankType, long vsyncId) {
sendFrame(durationMillis, jankType, vsyncId, true /* firstWindowFrame */); sendFrame(tracker, durationMillis, jankType, vsyncId, /* firstWindowFrame= */ true);
} }
private void sendFrame(long durationMillis, private void sendFrame(FrameTracker tracker, long durationMillis,
@JankType int jankType, long vsyncId) { @JankType int jankType, long vsyncId) {
sendFrame(durationMillis, jankType, vsyncId, false /* firstWindowFrame */); sendFrame(tracker, durationMillis, jankType, vsyncId, /* firstWindowFrame= */ false);
} }
private void sendFrame(long durationMillis, /**
* Used for surface only test.
*/
private void sendFrame(FrameTracker tracker, @JankType int jankType, long vsyncId) {
sendFrame(tracker, /* durationMillis= */ -1,
jankType, vsyncId, /* firstWindowFrame= */ false);
}
private void sendFrame(FrameTracker tracker, long durationMillis,
@JankType int jankType, long vsyncId, boolean firstWindowFrame) { @JankType int jankType, long vsyncId, boolean firstWindowFrame) {
when(mWrapper.getTiming()).thenReturn(new long[] { 0, vsyncId }); if (!tracker.mSurfaceOnly) {
doReturn(firstWindowFrame ? 1L : 0L).when(mWrapper) when(mWrapper.getTiming()).thenReturn(new long[]{0, vsyncId});
.getMetric(FrameMetrics.FIRST_DRAW_FRAME); doReturn(firstWindowFrame ? 1L : 0L).when(mWrapper)
doReturn(TimeUnit.MILLISECONDS.toNanos(durationMillis)) .getMetric(FrameMetrics.FIRST_DRAW_FRAME);
.when(mWrapper).getMetric(FrameMetrics.TOTAL_DURATION); doReturn(TimeUnit.MILLISECONDS.toNanos(durationMillis))
mTracker.onFrameMetricsAvailable(0); .when(mWrapper).getMetric(FrameMetrics.TOTAL_DURATION);
tracker.onFrameMetricsAvailable(0);
}
mListenerCapture.getValue().onJankDataAvailable(new JankData[] { mListenerCapture.getValue().onJankDataAvailable(new JankData[] {
new JankData(vsyncId, jankType) new JankData(vsyncId, jankType)
}); });

View File

@@ -16,8 +16,6 @@
package com.android.internal.jank; package com.android.internal.jank;
import static com.android.internal.jank.FrameTracker.SurfaceControlWrapper;
import static com.android.internal.jank.FrameTracker.ViewRootWrapper;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE; import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_TO_STATSD_INTERACTION_TYPE; import static com.android.internal.jank.InteractionJankMonitor.CUJ_TO_STATSD_INTERACTION_TYPE;
@@ -25,17 +23,17 @@ import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage; import static com.google.common.truth.Truth.assertWithMessage;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy; import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.os.Handler; import android.os.Handler;
import android.os.HandlerThread; import android.os.HandlerThread;
import android.os.Message;
import android.provider.DeviceConfig; import android.provider.DeviceConfig;
import android.view.View; import android.view.View;
import android.view.ViewAttachTestActivity; import android.view.ViewAttachTestActivity;
@@ -43,8 +41,12 @@ import android.view.ViewAttachTestActivity;
import androidx.test.filters.SmallTest; import androidx.test.filters.SmallTest;
import androidx.test.rule.ActivityTestRule; import androidx.test.rule.ActivityTestRule;
import com.android.internal.jank.FrameTracker.ChoreographerWrapper;
import com.android.internal.jank.FrameTracker.FrameMetricsWrapper; import com.android.internal.jank.FrameTracker.FrameMetricsWrapper;
import com.android.internal.jank.FrameTracker.SurfaceControlWrapper;
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.InteractionJankMonitor.Configuration;
import com.android.internal.jank.InteractionJankMonitor.Session; import com.android.internal.jank.InteractionJankMonitor.Session;
import org.junit.Before; import org.junit.Before;
@@ -92,12 +94,15 @@ public class InteractionJankMonitorTest {
verify(mWorker).start(); verify(mWorker).start();
Session session = new Session(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, CUJ_POSTFIX); Session session = new Session(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, CUJ_POSTFIX);
Configuration config = mock(Configuration.class);
when(config.isSurfaceOnly()).thenReturn(false);
FrameTracker tracker = spy(new FrameTracker(session, mWorker.getThreadHandler(), FrameTracker tracker = spy(new FrameTracker(session, mWorker.getThreadHandler(),
new ThreadedRendererWrapper(mView.getThreadedRenderer()), new ThreadedRendererWrapper(mView.getThreadedRenderer()),
new ViewRootWrapper(mView.getViewRootImpl()), new SurfaceControlWrapper(), new ViewRootWrapper(mView.getViewRootImpl()),
mock(FrameTracker.ChoreographerWrapper.class), new SurfaceControlWrapper(), mock(ChoreographerWrapper.class),
new FrameMetricsWrapper(), /*traceThresholdMissedFrames=*/ 1, new FrameMetricsWrapper(),
/*traceThresholdFrameTimeMillis=*/ -1, null)); /* traceThresholdMissedFrames= */ 1, /* traceThresholdFrameTimeMillis= */ -1,
/* FrameTrackerListener */ null, config));
doReturn(tracker).when(monitor).createFrameTracker(any(), any()); doReturn(tracker).when(monitor).createFrameTracker(any(), any());
doNothing().when(tracker).triggerPerfetto(); doNothing().when(tracker).triggerPerfetto();
doNothing().when(tracker).postTraceStartMarker(); doNothing().when(tracker).postTraceStartMarker();
@@ -138,28 +143,30 @@ public class InteractionJankMonitorTest {
public void testBeginCancel() { public void testBeginCancel() {
InteractionJankMonitor monitor = spy(new InteractionJankMonitor(mWorker)); InteractionJankMonitor monitor = spy(new InteractionJankMonitor(mWorker));
ArgumentCaptor<Message> captor = ArgumentCaptor.forClass(Message.class); ArgumentCaptor<Runnable> captor = ArgumentCaptor.forClass(Runnable.class);
Session session = new Session(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, CUJ_POSTFIX); Session session = new Session(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, CUJ_POSTFIX);
Configuration config = mock(Configuration.class);
when(config.isSurfaceOnly()).thenReturn(false);
FrameTracker tracker = spy(new FrameTracker(session, mWorker.getThreadHandler(), FrameTracker tracker = spy(new FrameTracker(session, mWorker.getThreadHandler(),
new ThreadedRendererWrapper(mView.getThreadedRenderer()), new ThreadedRendererWrapper(mView.getThreadedRenderer()),
new ViewRootWrapper(mView.getViewRootImpl()), new SurfaceControlWrapper(), new ViewRootWrapper(mView.getViewRootImpl()),
mock(FrameTracker.ChoreographerWrapper.class), new SurfaceControlWrapper(), mock(FrameTracker.ChoreographerWrapper.class),
new FrameMetricsWrapper(), /*traceThresholdMissedFrames=*/ 1, new FrameMetricsWrapper(),
/*traceThresholdFrameTimeMillis=*/ -1, null)); /* traceThresholdMissedFrames= */ 1, /* traceThresholdFrameTimeMillis= */ -1,
/* FrameTrackerListener */ null, config));
doReturn(tracker).when(monitor).createFrameTracker(any(), any()); doReturn(tracker).when(monitor).createFrameTracker(any(), any());
doNothing().when(tracker).triggerPerfetto(); doNothing().when(tracker).triggerPerfetto();
doNothing().when(tracker).postTraceStartMarker(); doNothing().when(tracker).postTraceStartMarker();
assertThat(monitor.begin(mView, session.getCuj())).isTrue(); assertThat(monitor.begin(mView, session.getCuj())).isTrue();
verify(tracker).begin(); verify(tracker).begin();
verify(mWorker.getThreadHandler(), atLeastOnce()).sendMessageAtTime(captor.capture(), verify(monitor).scheduleTimeoutAction(anyInt(), anyLong(), captor.capture());
anyLong()); Runnable runnable = captor.getValue();
Runnable runnable = captor.getValue().getCallback();
assertThat(runnable).isNotNull(); assertThat(runnable).isNotNull();
mWorker.getThreadHandler().removeCallbacks(runnable); mWorker.getThreadHandler().removeCallbacks(runnable);
runnable.run(); runnable.run();
verify(tracker).cancel(FrameTracker.REASON_CANCEL_NORMAL); verify(tracker).cancel(FrameTracker.REASON_CANCEL_TIMEOUT);
} }
@Test @Test

View File

@@ -80,8 +80,7 @@ public final class InteractionJankMonitorWrapper {
public static void begin(View v, @CujType int cujType, long timeout) { public static void begin(View v, @CujType int cujType, long timeout) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) return; if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) return;
Configuration.Builder builder = Configuration.Builder builder =
new Configuration.Builder(cujType) Configuration.Builder.withView(cujType, v)
.setView(v)
.setTimeout(timeout); .setTimeout(timeout);
InteractionJankMonitor.getInstance().begin(builder); InteractionJankMonitor.getInstance().begin(builder);
} }

View File

@@ -2324,8 +2324,8 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable,
} }
private Configuration.Builder createInteractionJankMonitorConf(String tag) { private Configuration.Builder createInteractionJankMonitorConf(String tag) {
return new Configuration.Builder(CUJ_LOCKSCREEN_UNLOCK_ANIMATION) return Configuration.Builder.withView(CUJ_LOCKSCREEN_UNLOCK_ANIMATION,
.setView(mKeyguardViewControllerLazy.get().getViewRootImpl().getView()) mKeyguardViewControllerLazy.get().getViewRootImpl().getView())
.setTag(tag); .setTag(tag);
} }

View File

@@ -521,8 +521,8 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
@Override @Override
public void onAnimationStart(Animator animation) { public void onAnimationStart(Animator animation) {
mWasCancelled = false; mWasCancelled = false;
Configuration.Builder builder = new Configuration.Builder(getCujType(isAppearing)) Configuration.Builder builder = Configuration.Builder
.setView(ActivatableNotificationView.this); .withView(getCujType(isAppearing), ActivatableNotificationView.this);
InteractionJankMonitor.getInstance().begin(builder); InteractionJankMonitor.getInstance().begin(builder);
} }

View File

@@ -1397,8 +1397,7 @@ public abstract class PanelViewController {
private void beginJankMonitoring(int cuj) { private void beginJankMonitoring(int cuj) {
InteractionJankMonitor.Configuration.Builder builder = InteractionJankMonitor.Configuration.Builder builder =
new InteractionJankMonitor.Configuration.Builder(cuj) InteractionJankMonitor.Configuration.Builder.withView(cuj, mView)
.setView(mView)
.setTag(isFullyCollapsed() ? "Expand" : "Collapse"); .setTag(isFullyCollapsed() ? "Expand" : "Collapse");
InteractionJankMonitor.getInstance().begin(builder); InteractionJankMonitor.getInstance().begin(builder);
} }