Loading core/java/com/android/internal/jank/InteractionJankMonitor.java +2 −2 Original line number Diff line number Diff line Loading @@ -543,7 +543,7 @@ public class InteractionJankMonitor { mRunningTrackers.put(cuj, tracker); if (mDebugOverlay != null) { mDebugOverlay.onTrackerAdded(cuj); mDebugOverlay.onTrackerAdded(cuj, tracker.mTracker.hashCode()); } return tracker; Loading Loading @@ -578,7 +578,7 @@ public class InteractionJankMonitor { running.mConfig.getHandler().removeCallbacks(running.mTimeoutAction); mRunningTrackers.remove(cuj); if (mDebugOverlay != null) { mDebugOverlay.onTrackerRemoved(cuj, reason); mDebugOverlay.onTrackerRemoved(cuj, reason, tracker.hashCode()); } return false; } Loading core/java/com/android/internal/jank/InteractionMonitorDebugOverlay.java +112 −48 Original line number Diff line number Diff line Loading @@ -41,13 +41,14 @@ import android.os.Handler; import android.os.Trace; import android.util.DisplayMetrics; import android.util.Log; import android.util.SparseIntArray; import android.view.Display; import android.view.View; import android.view.WindowManager; import com.android.internal.jank.FrameTracker.Reasons; import java.util.ArrayList; /** * An overlay that uses WindowCallbacks to draw the names of all running CUJs to the window * associated with one of the CUJs being tracked. There's no guarantee which window it will Loading @@ -68,13 +69,14 @@ import com.android.internal.jank.FrameTracker.Reasons; class InteractionMonitorDebugOverlay { private static final String TAG = "InteractionMonitorDebug"; private static final int REASON_STILL_RUNNING = -1000; private static final long HIDE_OVERLAY_DELAY = 2000L; // Sparse array where the key in the CUJ and the value is the session status, or null if // it's currently running private final Application mCurrentApplication; private final Handler mUiThread; private final DebugOverlayView mDebugOverlayView; private final WindowManager mWindowManager; private final SparseIntArray mRunningCujs = new SparseIntArray(); private final ArrayList<TrackerState> mRunningCujs = new ArrayList<>(); InteractionMonitorDebugOverlay(@NonNull Application currentApplication, @NonNull @UiThread Handler uiThread, @ColorInt int bgColor, double yOffset) { Loading @@ -94,8 +96,7 @@ class InteractionMonitorDebugOverlay { | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE, PixelFormat.TRANSLUCENT); | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE, PixelFormat.TRANSLUCENT); lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS | WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION; Loading @@ -116,30 +117,53 @@ class InteractionMonitorDebugOverlay { mWindowManager.addView(mDebugOverlayView, lp); } private final Runnable mHideOverlayRunnable = new Runnable() { @Override public void run() { mRunningCujs.clear(); mDebugOverlayView.setVisibility(INVISIBLE); } }; @AnyThread void onTrackerAdded(@Cuj.CujType int addedCuj) { void onTrackerAdded(@Cuj.CujType int addedCuj, int cookie) { mUiThread.removeCallbacks(mHideOverlayRunnable); mUiThread.post(() -> { String cujName = Cuj.getNameOfCuj(addedCuj); Log.i(TAG, cujName + " started"); // Use REASON_STILL_RUNNING (not technically one of the '@Reasons') to indicate the CUJ // is still running mRunningCujs.put(addedCuj, REASON_STILL_RUNNING); Log.i(TAG, cujName + " started (cookie=" + cookie + ")"); mRunningCujs.add(new TrackerState(addedCuj, cookie)); mDebugOverlayView.setVisibility(VISIBLE); mDebugOverlayView.invalidate(); }); } @AnyThread void onTrackerRemoved(@Cuj.CujType int removedCuj, @Reasons int reason) { void onTrackerRemoved(@Cuj.CujType int removedCuj, @Reasons int reason, int cookie) { mUiThread.post(() -> { mRunningCujs.put(removedCuj, reason); TrackerState foundTracker = null; boolean allTrackersEnded = true; for (int i = 0; i < mRunningCujs.size(); i++) { TrackerState tracker = mRunningCujs.get(i); if (tracker.mCuj == removedCuj && tracker.mCookie == cookie) { foundTracker = tracker; } else { // If none of the trackers have REASON_STILL_RUNNING status, then // all CUJs have ended allTrackersEnded = allTrackersEnded && tracker.mState != REASON_STILL_RUNNING; } } if (foundTracker != null) { foundTracker.mState = reason; } String cujName = Cuj.getNameOfCuj(removedCuj); Log.i(TAG, cujName + (reason == REASON_END_NORMAL ? " ended" : " cancelled")); // If REASON_STILL_RUNNING is not in mRunningCujs, then all CUJs have ended if (mRunningCujs.indexOfValue(REASON_STILL_RUNNING) < 0) { Log.i(TAG, cujName + (reason == REASON_END_NORMAL ? " ended" : " cancelled") + " (cookie=" + cookie + ")"); if (allTrackersEnded) { Log.i(TAG, "All CUJs ended"); mRunningCujs.clear(); mDebugOverlayView.setVisibility(INVISIBLE); mUiThread.postDelayed(mHideOverlayRunnable, HIDE_OVERLAY_DELAY); } mDebugOverlayView.invalidate(); }); Loading @@ -152,6 +176,21 @@ class InteractionMonitorDebugOverlay { }); } @AnyThread private static class TrackerState { final int mCookie; final int mCuj; int mState; private TrackerState(int cuj, int cookie) { mCuj = cuj; mCookie = cookie; // Use REASON_STILL_RUNNING (not technically one of the '@Reasons') to indicate the CUJ // is still running mState = REASON_STILL_RUNNING; } } @UiThread private class DebugOverlayView extends View { private static final String TRACK_NAME = "InteractionJankMonitor"; Loading @@ -164,7 +203,15 @@ class InteractionMonitorDebugOverlay { private final float mDensity; private final Paint mDebugPaint; private final Paint.FontMetrics mDebugFontMetrics; private final String mPackageName; private final String mPackageNameText; final int mPadding; final int mPackageNameFontSize; final int mCujFontSize; final float mCujNameTextHeight; final float mCujStatusWidth; final float mPackageNameTextHeight; final float mPackageNameWidth; private DebugOverlayView(Context context, @ColorInt int bgColor, double yOffset) { super(context); Loading @@ -176,7 +223,14 @@ class InteractionMonitorDebugOverlay { mDebugPaint = new Paint(); mDebugPaint.setAntiAlias(false); mDebugFontMetrics = new Paint.FontMetrics(); mPackageName = mCurrentApplication.getPackageName(); mPackageNameText = "package:" + mCurrentApplication.getPackageName(); mPadding = dipToPx(5); mPackageNameFontSize = dipToPx(12); mCujFontSize = dipToPx(18); mCujNameTextHeight = getTextHeight(mCujFontSize); mCujStatusWidth = mCujNameTextHeight * 1.2f; mPackageNameTextHeight = getTextHeight(mPackageNameFontSize); mPackageNameWidth = getWidthOfText(mPackageNameText, mPackageNameFontSize); } private int dipToPx(int dip) { Loading @@ -189,11 +243,16 @@ class InteractionMonitorDebugOverlay { return mDebugFontMetrics.descent - mDebugFontMetrics.ascent; } private float getWidthOfText(String text, int fontSize) { mDebugPaint.setTextSize(fontSize); return mDebugPaint.measureText(text); } private float getWidthOfLongestCujName(int cujFontSize) { mDebugPaint.setTextSize(cujFontSize); float maxLength = 0; for (int i = 0; i < mRunningCujs.size(); i++) { String cujName = Cuj.getNameOfCuj(mRunningCujs.keyAt(i)); String cujName = Cuj.getNameOfCuj(mRunningCujs.get(i).mCuj); float textLength = mDebugPaint.measureText(cujName); if (textLength > maxLength) { maxLength = textLength; Loading @@ -211,47 +270,52 @@ class InteractionMonitorDebugOverlay { // performance analysis. Trace.asyncTraceForTrackBegin(Trace.TRACE_TAG_APP, TRACK_NAME, "DEBUG_OVERLAY_DRAW", 0); final int padding = dipToPx(5); final int h = getHeight(); final int w = getWidth(); final int dy = (int) (h * mYOffset); int packageNameFontSize = dipToPx(12); int cujFontSize = dipToPx(18); final float cujNameTextHeight = getTextHeight(cujFontSize); final float packageNameTextHeight = getTextHeight(packageNameFontSize); float maxLength = getWidthOfLongestCujName(cujFontSize); float maxLength = Math.max(mPackageNameWidth, getWidthOfLongestCujName(mCujFontSize)) + mCujStatusWidth; final int dx = (int) ((w - maxLength) / 2f); canvas.translate(dx, dy); // Draw background rectangle for displaying the text showing the CUJ name mDebugPaint.setColor(mBgColor); canvas.drawRect(-padding * 2, // more padding on top so we can draw the package name -padding, padding * 2 + maxLength, padding * 2 + packageNameTextHeight + cujNameTextHeight * mRunningCujs.size(), mDebugPaint); mDebugPaint.setTextSize(packageNameFontSize); canvas.drawRect(-mPadding * 2, // more padding on top so we can draw the package name -mPadding, mPadding * 2 + maxLength, mPadding * 2 + mPackageNameTextHeight + mCujNameTextHeight * mRunningCujs.size(), mDebugPaint); mDebugPaint.setTextSize(mPackageNameFontSize); mDebugPaint.setColor(Color.BLACK); mDebugPaint.setStrikeThruText(false); canvas.translate(0, packageNameTextHeight); canvas.drawText("package:" + mPackageName, 0, 0, mDebugPaint); mDebugPaint.setTextSize(cujFontSize); canvas.translate(0, mPackageNameTextHeight); canvas.drawText(mPackageNameText, 0, 0, mDebugPaint); mDebugPaint.setTextSize(mCujFontSize); // Draw text for CUJ names for (int i = 0; i < mRunningCujs.size(); i++) { int status = mRunningCujs.valueAt(i); if (status == REASON_STILL_RUNNING) { TrackerState tracker = mRunningCujs.get(i); int status = tracker.mState; String statusText = switch (status) { case REASON_STILL_RUNNING -> { mDebugPaint.setColor(Color.BLACK); mDebugPaint.setStrikeThruText(false); } else if (status == REASON_END_NORMAL) { yield "☐"; // BALLOT BOX } case REASON_END_NORMAL -> { mDebugPaint.setColor(Color.GRAY); mDebugPaint.setStrikeThruText(false); } else { yield "✅"; // WHITE HEAVY CHECK MARK } default -> { // Cancelled, or otherwise ended for a bad reason mDebugPaint.setColor(Color.RED); mDebugPaint.setStrikeThruText(true); yield "❌"; // CROSS MARK } String cujName = Cuj.getNameOfCuj(mRunningCujs.keyAt(i)); canvas.translate(0, cujNameTextHeight); canvas.drawText(cujName, 0, 0, mDebugPaint); }; String cujName = Cuj.getNameOfCuj(tracker.mCuj); canvas.translate(0, mCujNameTextHeight); canvas.drawText(statusText, 0, 0, mDebugPaint); canvas.drawText(cujName, mCujStatusWidth, 0, mDebugPaint); } Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_APP, TRACK_NAME, 0); } Loading Loading
core/java/com/android/internal/jank/InteractionJankMonitor.java +2 −2 Original line number Diff line number Diff line Loading @@ -543,7 +543,7 @@ public class InteractionJankMonitor { mRunningTrackers.put(cuj, tracker); if (mDebugOverlay != null) { mDebugOverlay.onTrackerAdded(cuj); mDebugOverlay.onTrackerAdded(cuj, tracker.mTracker.hashCode()); } return tracker; Loading Loading @@ -578,7 +578,7 @@ public class InteractionJankMonitor { running.mConfig.getHandler().removeCallbacks(running.mTimeoutAction); mRunningTrackers.remove(cuj); if (mDebugOverlay != null) { mDebugOverlay.onTrackerRemoved(cuj, reason); mDebugOverlay.onTrackerRemoved(cuj, reason, tracker.hashCode()); } return false; } Loading
core/java/com/android/internal/jank/InteractionMonitorDebugOverlay.java +112 −48 Original line number Diff line number Diff line Loading @@ -41,13 +41,14 @@ import android.os.Handler; import android.os.Trace; import android.util.DisplayMetrics; import android.util.Log; import android.util.SparseIntArray; import android.view.Display; import android.view.View; import android.view.WindowManager; import com.android.internal.jank.FrameTracker.Reasons; import java.util.ArrayList; /** * An overlay that uses WindowCallbacks to draw the names of all running CUJs to the window * associated with one of the CUJs being tracked. There's no guarantee which window it will Loading @@ -68,13 +69,14 @@ import com.android.internal.jank.FrameTracker.Reasons; class InteractionMonitorDebugOverlay { private static final String TAG = "InteractionMonitorDebug"; private static final int REASON_STILL_RUNNING = -1000; private static final long HIDE_OVERLAY_DELAY = 2000L; // Sparse array where the key in the CUJ and the value is the session status, or null if // it's currently running private final Application mCurrentApplication; private final Handler mUiThread; private final DebugOverlayView mDebugOverlayView; private final WindowManager mWindowManager; private final SparseIntArray mRunningCujs = new SparseIntArray(); private final ArrayList<TrackerState> mRunningCujs = new ArrayList<>(); InteractionMonitorDebugOverlay(@NonNull Application currentApplication, @NonNull @UiThread Handler uiThread, @ColorInt int bgColor, double yOffset) { Loading @@ -94,8 +96,7 @@ class InteractionMonitorDebugOverlay { | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE, PixelFormat.TRANSLUCENT); | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE, PixelFormat.TRANSLUCENT); lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS | WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION; Loading @@ -116,30 +117,53 @@ class InteractionMonitorDebugOverlay { mWindowManager.addView(mDebugOverlayView, lp); } private final Runnable mHideOverlayRunnable = new Runnable() { @Override public void run() { mRunningCujs.clear(); mDebugOverlayView.setVisibility(INVISIBLE); } }; @AnyThread void onTrackerAdded(@Cuj.CujType int addedCuj) { void onTrackerAdded(@Cuj.CujType int addedCuj, int cookie) { mUiThread.removeCallbacks(mHideOverlayRunnable); mUiThread.post(() -> { String cujName = Cuj.getNameOfCuj(addedCuj); Log.i(TAG, cujName + " started"); // Use REASON_STILL_RUNNING (not technically one of the '@Reasons') to indicate the CUJ // is still running mRunningCujs.put(addedCuj, REASON_STILL_RUNNING); Log.i(TAG, cujName + " started (cookie=" + cookie + ")"); mRunningCujs.add(new TrackerState(addedCuj, cookie)); mDebugOverlayView.setVisibility(VISIBLE); mDebugOverlayView.invalidate(); }); } @AnyThread void onTrackerRemoved(@Cuj.CujType int removedCuj, @Reasons int reason) { void onTrackerRemoved(@Cuj.CujType int removedCuj, @Reasons int reason, int cookie) { mUiThread.post(() -> { mRunningCujs.put(removedCuj, reason); TrackerState foundTracker = null; boolean allTrackersEnded = true; for (int i = 0; i < mRunningCujs.size(); i++) { TrackerState tracker = mRunningCujs.get(i); if (tracker.mCuj == removedCuj && tracker.mCookie == cookie) { foundTracker = tracker; } else { // If none of the trackers have REASON_STILL_RUNNING status, then // all CUJs have ended allTrackersEnded = allTrackersEnded && tracker.mState != REASON_STILL_RUNNING; } } if (foundTracker != null) { foundTracker.mState = reason; } String cujName = Cuj.getNameOfCuj(removedCuj); Log.i(TAG, cujName + (reason == REASON_END_NORMAL ? " ended" : " cancelled")); // If REASON_STILL_RUNNING is not in mRunningCujs, then all CUJs have ended if (mRunningCujs.indexOfValue(REASON_STILL_RUNNING) < 0) { Log.i(TAG, cujName + (reason == REASON_END_NORMAL ? " ended" : " cancelled") + " (cookie=" + cookie + ")"); if (allTrackersEnded) { Log.i(TAG, "All CUJs ended"); mRunningCujs.clear(); mDebugOverlayView.setVisibility(INVISIBLE); mUiThread.postDelayed(mHideOverlayRunnable, HIDE_OVERLAY_DELAY); } mDebugOverlayView.invalidate(); }); Loading @@ -152,6 +176,21 @@ class InteractionMonitorDebugOverlay { }); } @AnyThread private static class TrackerState { final int mCookie; final int mCuj; int mState; private TrackerState(int cuj, int cookie) { mCuj = cuj; mCookie = cookie; // Use REASON_STILL_RUNNING (not technically one of the '@Reasons') to indicate the CUJ // is still running mState = REASON_STILL_RUNNING; } } @UiThread private class DebugOverlayView extends View { private static final String TRACK_NAME = "InteractionJankMonitor"; Loading @@ -164,7 +203,15 @@ class InteractionMonitorDebugOverlay { private final float mDensity; private final Paint mDebugPaint; private final Paint.FontMetrics mDebugFontMetrics; private final String mPackageName; private final String mPackageNameText; final int mPadding; final int mPackageNameFontSize; final int mCujFontSize; final float mCujNameTextHeight; final float mCujStatusWidth; final float mPackageNameTextHeight; final float mPackageNameWidth; private DebugOverlayView(Context context, @ColorInt int bgColor, double yOffset) { super(context); Loading @@ -176,7 +223,14 @@ class InteractionMonitorDebugOverlay { mDebugPaint = new Paint(); mDebugPaint.setAntiAlias(false); mDebugFontMetrics = new Paint.FontMetrics(); mPackageName = mCurrentApplication.getPackageName(); mPackageNameText = "package:" + mCurrentApplication.getPackageName(); mPadding = dipToPx(5); mPackageNameFontSize = dipToPx(12); mCujFontSize = dipToPx(18); mCujNameTextHeight = getTextHeight(mCujFontSize); mCujStatusWidth = mCujNameTextHeight * 1.2f; mPackageNameTextHeight = getTextHeight(mPackageNameFontSize); mPackageNameWidth = getWidthOfText(mPackageNameText, mPackageNameFontSize); } private int dipToPx(int dip) { Loading @@ -189,11 +243,16 @@ class InteractionMonitorDebugOverlay { return mDebugFontMetrics.descent - mDebugFontMetrics.ascent; } private float getWidthOfText(String text, int fontSize) { mDebugPaint.setTextSize(fontSize); return mDebugPaint.measureText(text); } private float getWidthOfLongestCujName(int cujFontSize) { mDebugPaint.setTextSize(cujFontSize); float maxLength = 0; for (int i = 0; i < mRunningCujs.size(); i++) { String cujName = Cuj.getNameOfCuj(mRunningCujs.keyAt(i)); String cujName = Cuj.getNameOfCuj(mRunningCujs.get(i).mCuj); float textLength = mDebugPaint.measureText(cujName); if (textLength > maxLength) { maxLength = textLength; Loading @@ -211,47 +270,52 @@ class InteractionMonitorDebugOverlay { // performance analysis. Trace.asyncTraceForTrackBegin(Trace.TRACE_TAG_APP, TRACK_NAME, "DEBUG_OVERLAY_DRAW", 0); final int padding = dipToPx(5); final int h = getHeight(); final int w = getWidth(); final int dy = (int) (h * mYOffset); int packageNameFontSize = dipToPx(12); int cujFontSize = dipToPx(18); final float cujNameTextHeight = getTextHeight(cujFontSize); final float packageNameTextHeight = getTextHeight(packageNameFontSize); float maxLength = getWidthOfLongestCujName(cujFontSize); float maxLength = Math.max(mPackageNameWidth, getWidthOfLongestCujName(mCujFontSize)) + mCujStatusWidth; final int dx = (int) ((w - maxLength) / 2f); canvas.translate(dx, dy); // Draw background rectangle for displaying the text showing the CUJ name mDebugPaint.setColor(mBgColor); canvas.drawRect(-padding * 2, // more padding on top so we can draw the package name -padding, padding * 2 + maxLength, padding * 2 + packageNameTextHeight + cujNameTextHeight * mRunningCujs.size(), mDebugPaint); mDebugPaint.setTextSize(packageNameFontSize); canvas.drawRect(-mPadding * 2, // more padding on top so we can draw the package name -mPadding, mPadding * 2 + maxLength, mPadding * 2 + mPackageNameTextHeight + mCujNameTextHeight * mRunningCujs.size(), mDebugPaint); mDebugPaint.setTextSize(mPackageNameFontSize); mDebugPaint.setColor(Color.BLACK); mDebugPaint.setStrikeThruText(false); canvas.translate(0, packageNameTextHeight); canvas.drawText("package:" + mPackageName, 0, 0, mDebugPaint); mDebugPaint.setTextSize(cujFontSize); canvas.translate(0, mPackageNameTextHeight); canvas.drawText(mPackageNameText, 0, 0, mDebugPaint); mDebugPaint.setTextSize(mCujFontSize); // Draw text for CUJ names for (int i = 0; i < mRunningCujs.size(); i++) { int status = mRunningCujs.valueAt(i); if (status == REASON_STILL_RUNNING) { TrackerState tracker = mRunningCujs.get(i); int status = tracker.mState; String statusText = switch (status) { case REASON_STILL_RUNNING -> { mDebugPaint.setColor(Color.BLACK); mDebugPaint.setStrikeThruText(false); } else if (status == REASON_END_NORMAL) { yield "☐"; // BALLOT BOX } case REASON_END_NORMAL -> { mDebugPaint.setColor(Color.GRAY); mDebugPaint.setStrikeThruText(false); } else { yield "✅"; // WHITE HEAVY CHECK MARK } default -> { // Cancelled, or otherwise ended for a bad reason mDebugPaint.setColor(Color.RED); mDebugPaint.setStrikeThruText(true); yield "❌"; // CROSS MARK } String cujName = Cuj.getNameOfCuj(mRunningCujs.keyAt(i)); canvas.translate(0, cujNameTextHeight); canvas.drawText(cujName, 0, 0, mDebugPaint); }; String cujName = Cuj.getNameOfCuj(tracker.mCuj); canvas.translate(0, mCujNameTextHeight); canvas.drawText(statusText, 0, 0, mDebugPaint); canvas.drawText(cujName, mCujStatusWidth, 0, mDebugPaint); } Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_APP, TRACK_NAME, 0); } Loading