Merge "Support surface only instrumentation" into sc-v2-dev am: d6d30bf7f6
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/15256893 Change-Id: I78d98f78086ace09d32fac4329c6b3357aed56b5
This commit is contained in:
@@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2347,8 +2347,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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user