Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6c8bd65c authored by Pascal Muetschard's avatar Pascal Muetschard Committed by Pascal Mütschard
Browse files

Some code cleanup of InteractionJankMonitor.

- Move the CUJ constant related stuff to its own class
- Remove the Session class
- Separation of IJM and FT via event listener, breaking the dep loop
- Fix (unlikely) race problems in begin/cancel/end
- Trim out some unused stuff
- Remove some duplicated stuff (like config validation)
- Remove checks ensured by tests
- Fix a bunch of warnings
- Fix missing locks in debug overlay
- Remove the outdated DEBUG mechanism

Test: atest com.android.internal.jank
Flag: NA
Change-Id: I29b9da107313d4c07777ce50ce907c7516258807
parent c2389366
Loading
Loading
Loading
Loading
+3 −3
Original line number Diff line number Diff line
@@ -20,8 +20,8 @@ import static android.view.InsetsController.ANIMATION_TYPE_HIDE;
import static android.view.InsetsController.ANIMATION_TYPE_SHOW;

import static com.android.internal.inputmethod.InputMethodDebug.softInputDisplayReasonToString;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_IME_INSETS_HIDE_ANIMATION;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_IME_INSETS_SHOW_ANIMATION;
import static com.android.internal.jank.Cuj.CUJ_IME_INSETS_HIDE_ANIMATION;
import static com.android.internal.jank.Cuj.CUJ_IME_INSETS_SHOW_ANIMATION;
import static com.android.internal.util.LatencyTracker.ACTION_REQUEST_IME_HIDDEN;
import static com.android.internal.util.LatencyTracker.ACTION_REQUEST_IME_SHOWN;

@@ -758,7 +758,7 @@ public interface ImeTracker {
         * A helper method to translate animation type to CUJ type for IME animations.
         *
         * @param animType the animation type.
         * @return the integer in {@link com.android.internal.jank.InteractionJankMonitor.CujType},
         * @return the integer in {@link com.android.internal.jank.Cuj.CujType},
         * or {@code -1} if the animation type is not supported for tracking yet.
         */
        private static int getImeInsetsCujFromAnimation(@AnimationType int animType) {
+503 −0

File added.

Preview size limit exceeded, changes collapsed.

+53 −98
Original line number Diff line number Diff line
@@ -53,7 +53,6 @@ import android.view.WindowCallbacks;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.jank.DisplayRefreshRate.RefreshRate;
import com.android.internal.jank.InteractionJankMonitor.Configuration;
import com.android.internal.jank.InteractionJankMonitor.Session;
import com.android.internal.util.FrameworkStatsLog;

import java.lang.annotation.Retention;
@@ -67,7 +66,6 @@ import java.util.concurrent.TimeUnit;
public class FrameTracker extends SurfaceControl.OnJankDataListener
        implements HardwareRendererObserver.OnFrameMetricsAvailableListener {
    private static final String TAG = "FrameTracker";
    private static final boolean DEBUG = false;

    private static final long INVALID_ID = -1;
    public static final int NANOS_IN_MILLISECOND = 1_000_000;
@@ -93,20 +91,19 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
            REASON_CANCEL_NORMAL,
            REASON_CANCEL_NOT_BEGUN,
            REASON_CANCEL_SAME_VSYNC,
            REASON_CANCEL_TIMEOUT,
    })
    @Retention(RetentionPolicy.SOURCE)
    public @interface Reasons {
    }

    @VisibleForTesting
    public final InteractionJankMonitor mMonitor;
    private final HardwareRendererObserver mObserver;
    private final int mTraceThresholdMissedFrames;
    private final int mTraceThresholdFrameTimeMillis;
    private final ThreadedRendererWrapper mRendererWrapper;
    private final FrameMetricsWrapper mMetricsWrapper;
    private final SparseArray<JankInfo> mJankInfos = new SparseArray<>();
    private final Session mSession;
    private final Configuration mConfig;
    private final ViewRootWrapper mViewRoot;
    private final SurfaceControlWrapper mSurfaceControlWrapper;
    private final int mDisplayId;
@@ -197,19 +194,18 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
        }
    }

    public FrameTracker(@NonNull InteractionJankMonitor monitor, @NonNull Session session,
            @NonNull Handler handler, @Nullable ThreadedRendererWrapper renderer,
    public FrameTracker(@NonNull Configuration config,
            @Nullable ThreadedRendererWrapper renderer,
            @Nullable ViewRootWrapper viewRootWrapper,
            @NonNull SurfaceControlWrapper surfaceControlWrapper,
            @NonNull ChoreographerWrapper choreographer,
            @Nullable FrameMetricsWrapper metrics,
            @NonNull StatsLogWrapper statsLog,
            int traceThresholdMissedFrames, int traceThresholdFrameTimeMillis,
            @Nullable FrameTrackerListener listener, @NonNull Configuration config) {
        mMonitor = monitor;
            @Nullable FrameTrackerListener listener) {
        mSurfaceOnly = config.isSurfaceOnly();
        mSession = session;
        mHandler = handler;
        mConfig = config;
        mHandler = config.getHandler();
        mChoreographer = choreographer;
        mSurfaceControlWrapper = surfaceControlWrapper;
        mStatsLog = statsLog;
@@ -222,7 +218,7 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
        mObserver = mSurfaceOnly
                ? null
                : new HardwareRendererObserver(this, mMetricsWrapper.getTiming(),
                        handler, /* waitForPresentTime= */ false);
                        mHandler, /* waitForPresentTime= */ false);

        mTraceThresholdMissedFrames = traceThresholdMissedFrames;
        mTraceThresholdFrameTimeMillis = traceThresholdFrameTimeMillis;
@@ -242,7 +238,7 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
            mSurfaceChangedCallback = new ViewRootImpl.SurfaceChangedCallback() {
                @Override
                public void surfaceCreated(SurfaceControl.Transaction t) {
                    getHandler().runWithScissors(() -> {
                    mHandler.runWithScissors(() -> {
                        if (mSurfaceControl == null) {
                            mSurfaceControl = mViewRoot.getSurfaceControl();
                            if (mBeginVsyncId != INVALID_ID) {
@@ -262,13 +258,7 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener

                    // Wait a while to give the system a chance for the remaining
                    // frames to arrive, then force finish the session.
                    getHandler().postDelayed(() -> {
                        if (DEBUG) {
                            Log.d(TAG, "surfaceDestroyed: " + mSession.getName()
                                    + ", finalized=" + mMetricsFinalized
                                    + ", info=" + mJankInfos.size()
                                    + ", vsync=" + mBeginVsyncId);
                        }
                    mHandler.postDelayed(() -> {
                        if (!mMetricsFinalized) {
                            end(REASON_END_SURFACE_DESTROYED);
                            finish();
@@ -282,11 +272,6 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
        }
    }

    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
    public Handler getHandler() {
        return mHandler;
    }

    /**
     * Begin a trace session of the CUJ.
     */
@@ -300,10 +285,6 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
            mBeginVsyncId = mDeferMonitoring ? currentVsync + 1 : currentVsync;
        }
        if (mSurfaceControl != null) {
            if (DEBUG) {
                Log.d(TAG, "begin: " + mSession.getName() + ", begin=" + mBeginVsyncId
                        + ", defer=" + mDeferMonitoring + ", current=" + currentVsync);
            }
            if (mDeferMonitoring && currentVsync < mBeginVsyncId) {
                markEvent("FT#deferMonitoring", 0);
                // Normal case, we begin the instrument from the very beginning,
@@ -314,11 +295,6 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
                // there is no need to skip the frame where the begin invocation happens.
                beginInternal();
            }
        } else {
            if (DEBUG) {
                Log.d(TAG, "begin: defer beginning since the surface is not ready for CUJ="
                        + mSession.getName());
            }
        }
    }

@@ -336,8 +312,8 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
            return;
        }
        mTracingStarted = true;
        Trace.asyncTraceForTrackBegin(Trace.TRACE_TAG_APP, mSession.getName(), mSession.getName(),
                (int) mBeginVsyncId);
        String name = mConfig.getSessionName();
        Trace.asyncTraceForTrackBegin(Trace.TRACE_TAG_APP, name, name, (int) mBeginVsyncId);
        markEvent("FT#beginVsync", mBeginVsyncId);
        markEvent("FT#layerId", mSurfaceControl.getLayerId());
        mSurfaceControlWrapper.addJankStatsListener(this, mSurfaceControl);
@@ -361,15 +337,10 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
        } else if (mEndVsyncId <= mBeginVsyncId) {
            return cancel(REASON_CANCEL_SAME_VSYNC);
        } else {
            if (DEBUG) {
                Log.d(TAG, "end: " + mSession.getName()
                        + ", end=" + mEndVsyncId + ", reason=" + reason);
            }
            final String name = mConfig.getSessionName();
            markEvent("FT#end", reason);
            markEvent("FT#endVsync", mEndVsyncId);
            Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_APP, mSession.getName(),
                    (int) mBeginVsyncId);
            mSession.setReason(reason);
            Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_APP, name, (int) mBeginVsyncId);

            // We don't remove observer here,
            // will remove it when all the frame metrics in this duration are called back.
@@ -395,16 +366,16 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
                        mFlushAttempts++;
                    } else {
                        mWaitForFinishTimedOut = () -> {
                            Log.e(TAG, "force finish cuj, time out: " + mSession.getName());
                            Log.e(TAG, "force finish cuj, time out: " + name);
                            finish();
                        };
                        delay = TimeUnit.SECONDS.toMillis(10);
                    }
                    getHandler().postDelayed(mWaitForFinishTimedOut, delay);
                    mHandler.postDelayed(mWaitForFinishTimedOut, delay);
                }
            };
            getHandler().postDelayed(mWaitForFinishTimedOut, FLUSH_DELAY_MILLISECOND);
            notifyCujEvent(ACTION_SESSION_END);
            mHandler.postDelayed(mWaitForFinishTimedOut, FLUSH_DELAY_MILLISECOND);
            notifyCujEvent(ACTION_SESSION_END, reason);
            return true;
        }
    }
@@ -421,22 +392,16 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
        markEvent("FT#cancel", reason);
        // We don't need to end the trace section if it has never begun.
        if (mTracingStarted) {
            Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_APP, mSession.getName(),
                    (int) mBeginVsyncId);
            Trace.asyncTraceForTrackEnd(
                    Trace.TRACE_TAG_APP, mConfig.getSessionName(), (int) mBeginVsyncId);
        }

        // Always remove the observers in cancel call to avoid leakage.
        removeObservers();

        if (DEBUG) {
            Log.d(TAG, "cancel: " + mSession.getName() + ", begin=" + mBeginVsyncId
                    + ", end=" + mEndVsyncId + ", reason=" + reason);
        }

        mSession.setReason(reason);
        // Notify the listener the session has been cancelled.
        // We don't notify the listeners if the session never begun.
        notifyCujEvent(ACTION_SESSION_CANCEL);
        notifyCujEvent(ACTION_SESSION_CANCEL, reason);
        return true;
    }

@@ -455,13 +420,13 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
                        "The length of the trace event description <%s> exceeds %d",
                        event, MAX_LENGTH_EVENT_DESC));
            }
            Trace.instantForTrack(Trace.TRACE_TAG_APP, mSession.getName(), event);
            Trace.instantForTrack(Trace.TRACE_TAG_APP, mConfig.getSessionName(), event);
        }
    }

    private void notifyCujEvent(String action) {
    private void notifyCujEvent(String action, @Reasons int reason) {
        if (mListener == null) return;
        mListener.onCujEvents(mSession, action);
        mListener.onCujEvents(this, action, reason);
    }

    @Override
@@ -496,7 +461,7 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
     */
    @VisibleForTesting
    public void postCallback(Runnable callback) {
        getHandler().post(callback);
        mHandler.post(callback);
    }

    @Nullable
@@ -587,13 +552,15 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
        if (mMetricsFinalized || mCancelled) return;
        mMetricsFinalized = true;

        getHandler().removeCallbacks(mWaitForFinishTimedOut);
        mHandler.removeCallbacks(mWaitForFinishTimedOut);
        mWaitForFinishTimedOut = null;
        markEvent("FT#finish", mJankInfos.size());

        // The tracing has been ended, remove the observer, see if need to trigger perfetto.
        removeObservers();

        final String name = mConfig.getSessionName();

        int totalFramesCount = 0;
        long maxFrameTimeNanos = 0;
        int missedFramesCount = 0;
@@ -616,7 +583,7 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
                totalFramesCount++;
                boolean missedFrame = false;
                if ((info.jankType & JANK_APP_DEADLINE_MISSED) != 0) {
                    Log.w(TAG, "Missed App frame:" + info + ", CUJ=" + mSession.getName());
                    Log.w(TAG, "Missed App frame:" + info + ", CUJ=" + name);
                    missedAppFramesCount++;
                    missedFrame = true;
                }
@@ -625,7 +592,7 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
                        || (info.jankType & JANK_SURFACEFLINGER_GPU_DEADLINE_MISSED) != 0
                        || (info.jankType & SURFACE_FLINGER_SCHEDULING) != 0
                        || (info.jankType & PREDICTION_ERROR) != 0) {
                    Log.w(TAG, "Missed SF frame:" + info + ", CUJ=" + mSession.getName());
                    Log.w(TAG, "Missed SF frame:" + info + ", CUJ=" + name);
                    missedSfFramesCount++;
                    missedFrame = true;
                }
@@ -646,7 +613,7 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
                if (!mSurfaceOnly && !info.hwuiCallbackFired) {
                    markEvent("FT#MissedHWUICallback", info.frameVsyncId);
                    Log.w(TAG, "Missing HWUI jank callback for vsyncId: " + info.frameVsyncId
                            + ", CUJ=" + mSession.getName());
                            + ", CUJ=" + name);
                }
            }
            if (!mSurfaceOnly && info.hwuiCallbackFired) {
@@ -654,7 +621,7 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
                if (!info.surfaceControlCallbackFired) {
                    markEvent("FT#MissedSFCallback", info.frameVsyncId);
                    Log.w(TAG, "Missing SF jank callback for vsyncId: " + info.frameVsyncId
                            + ", CUJ=" + mSession.getName());
                            + ", CUJ=" + name);
                }
            }
        }
@@ -662,29 +629,26 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
                maxSuccessiveMissedFramesCount, successiveMissedFramesCount);

        // Log the frame stats as counters to make them easily accessible in traces.
        Trace.traceCounter(Trace.TRACE_TAG_APP, mSession.getName() + "#missedFrames",
                missedFramesCount);
        Trace.traceCounter(Trace.TRACE_TAG_APP, mSession.getName() + "#missedAppFrames",
                missedAppFramesCount);
        Trace.traceCounter(Trace.TRACE_TAG_APP, mSession.getName() + "#missedSfFrames",
                missedSfFramesCount);
        Trace.traceCounter(Trace.TRACE_TAG_APP, mSession.getName() + "#totalFrames",
                totalFramesCount);
        Trace.traceCounter(Trace.TRACE_TAG_APP, mSession.getName() + "#maxFrameTimeMillis",
        Trace.traceCounter(Trace.TRACE_TAG_APP, name + "#missedFrames", missedFramesCount);
        Trace.traceCounter(Trace.TRACE_TAG_APP, name + "#missedAppFrames", missedAppFramesCount);
        Trace.traceCounter(Trace.TRACE_TAG_APP, name + "#missedSfFrames", missedSfFramesCount);
        Trace.traceCounter(Trace.TRACE_TAG_APP, name + "#totalFrames", totalFramesCount);
        Trace.traceCounter(Trace.TRACE_TAG_APP, name + "#maxFrameTimeMillis",
                (int) (maxFrameTimeNanos / NANOS_IN_MILLISECOND));
        Trace.traceCounter(Trace.TRACE_TAG_APP, mSession.getName() + "#maxSuccessiveMissedFrames",
        Trace.traceCounter(Trace.TRACE_TAG_APP, name + "#maxSuccessiveMissedFrames",
                maxSuccessiveMissedFramesCount);

        // Trigger perfetto if necessary.
        if (shouldTriggerPerfetto(missedFramesCount, (int) maxFrameTimeNanos)) {
            triggerPerfetto();
        if (mListener != null
                && shouldTriggerPerfetto(missedFramesCount, (int) maxFrameTimeNanos)) {
            mListener.triggerPerfetto(mConfig);
        }
        if (mSession.logToStatsd()) {
        if (mConfig.logToStatsd()) {
            mStatsLog.write(
                    FrameworkStatsLog.UI_INTERACTION_FRAME_INFO_REPORTED,
                    mDisplayId,
                    refreshRate,
                    mSession.getStatsdInteractionType(),
                    mConfig.getStatsdInteractionType(),
                    totalFramesCount,
                    missedFramesCount,
                    maxFrameTimeNanos, /* will be 0 if mSurfaceOnly == true */
@@ -692,16 +656,6 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
                    missedAppFramesCount,
                    maxSuccessiveMissedFramesCount);
        }
        if (DEBUG) {
            Log.i(TAG, "finish: CUJ=" + mSession.getName()
                    + " (" + mBeginVsyncId + "," + mEndVsyncId + ")"
                    + " totalFrames=" + totalFramesCount
                    + " missedAppFrames=" + missedAppFramesCount
                    + " missedSfFrames=" + missedSfFramesCount
                    + " missedFrames=" + missedFramesCount
                    + " maxFrameTimeMillis=" + maxFrameTimeNanos / NANOS_IN_MILLISECOND
                    + " maxSuccessiveMissedFramesCount=" + maxSuccessiveMissedFramesCount);
        }
    }

    ThreadedRendererWrapper getThreadedRenderer() {
@@ -736,13 +690,6 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
        }
    }

    /**
     * Trigger the prefetto daemon.
     */
    public void triggerPerfetto() {
        mMonitor.trigger(mSession);
    }

    /**
     * A wrapper class that we can spy FrameMetrics (a final class) in unit tests.
     */
@@ -895,9 +842,17 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
        /**
         * Notify that the CUJ session was created.
         *
         * @param session the CUJ session
         * @param tracker the tracker
         * @param action the specific action
         * @param reason the reason for the action
         */
        void onCujEvents(FrameTracker tracker, String action, @Reasons int reason);

        /**
         * Notify that the Perfetto trace should be triggered.
         *
         * @param config the tracker configuration
         */
        void onCujEvents(Session session, String action);
        void triggerPerfetto(Configuration config);
    }
}
+273 −839

File changed.

Preview size limit exceeded, changes collapsed.

+45 −42
Original line number Diff line number Diff line
@@ -34,7 +34,6 @@ import android.view.WindowCallbacks;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.jank.FrameTracker.Reasons;
import com.android.internal.jank.InteractionJankMonitor.CujType;

/**
 * An overlay that uses WindowCallbacks to draw the names of all running CUJs to the window
@@ -94,14 +93,14 @@ class InteractionMonitorDebugOverlay implements WindowCallbacks {
    }

    @UiThread
    private boolean attachViewRootIfNeeded(FrameTracker tracker) {
        FrameTracker.ViewRootWrapper viewRoot = tracker.getViewRoot();
    private boolean attachViewRootIfNeeded(InteractionJankMonitor.RunningTracker tracker) {
        FrameTracker.ViewRootWrapper viewRoot = tracker.mTracker.getViewRoot();
        if (mViewRoot == null && viewRoot != null) {
            // Add a trace marker so we can identify traces that were captured while the debug
            // overlay was enabled. Traces that use the debug overlay should NOT be used for
            // performance analysis.
            Trace.asyncTraceForTrackBegin(Trace.TRACE_TAG_APP, TRACK_NAME, "DEBUG_OVERLAY_DRAW", 0);
            mHandler = tracker.getHandler();
            mHandler = tracker.mConfig.getHandler();
            mViewRoot = viewRoot;
            mHandler.runWithScissors(() -> viewRoot.addWindowCallbacks(this),
                    InteractionJankMonitor.EXECUTOR_TASK_TIMEOUT);
@@ -111,11 +110,12 @@ class InteractionMonitorDebugOverlay implements WindowCallbacks {
        return false;
    }

    @GuardedBy("mLock")
    private float getWidthOfLongestCujName(int cujFontSize) {
        mDebugPaint.setTextSize(cujFontSize);
        float maxLength = 0;
        for (int i = 0; i < mRunningCujs.size(); i++) {
            String cujName = InteractionJankMonitor.getNameOfCuj(mRunningCujs.keyAt(i));
            String cujName = Cuj.getNameOfCuj(mRunningCujs.keyAt(i));
            float textLength = mDebugPaint.measureText(cujName);
            if (textLength > maxLength) {
                maxLength = textLength;
@@ -149,8 +149,8 @@ class InteractionMonitorDebugOverlay implements WindowCallbacks {
    }

    @UiThread
    void onTrackerRemoved(@CujType int removedCuj, @Reasons int reason,
                          SparseArray<FrameTracker> runningTrackers) {
    void onTrackerRemoved(@Cuj.CujType int removedCuj, @Reasons int reason,
                          SparseArray<InteractionJankMonitor.RunningTracker> runningTrackers) {
        synchronized (mLock) {
            mRunningCujs.put(removedCuj, reason);
            // If REASON_STILL_RUNNING is not in mRunningCujs, then all CUJs have ended
@@ -164,7 +164,7 @@ class InteractionMonitorDebugOverlay implements WindowCallbacks {
                    // trackers
                    for (int i = 0; i < runningTrackers.size(); i++) {
                        if (mViewRoot.equals(
                                runningTrackers.valueAt(i).getViewRoot())) {
                                runningTrackers.valueAt(i).mTracker.getViewRoot())) {
                            needsNewViewRoot = false;
                            break;
                        }
@@ -185,7 +185,7 @@ class InteractionMonitorDebugOverlay implements WindowCallbacks {
    }

    @UiThread
    void onTrackerAdded(@CujType int addedCuj, FrameTracker tracker) {
    void onTrackerAdded(@Cuj.CujType int addedCuj, InteractionJankMonitor.RunningTracker tracker) {
        synchronized (mLock) {
            // Use REASON_STILL_RUNNING (not technically one of the '@Reasons') to indicate the CUJ
            // is still running
@@ -230,6 +230,8 @@ class InteractionMonitorDebugOverlay implements WindowCallbacks {
        int cujFontSize = dipToPx(18);
        final float cujNameTextHeight = getTextHeight(cujFontSize);
        final float packageNameTextHeight = getTextHeight(packageNameFontSize);

        synchronized (mLock) {
            float maxLength = getWidthOfLongestCujName(cujFontSize);

            final int dx = (int) ((w - maxLength) / 2f);
@@ -262,9 +264,10 @@ class InteractionMonitorDebugOverlay implements WindowCallbacks {
                    mDebugPaint.setColor(Color.RED);
                    mDebugPaint.setStrikeThruText(true);
                }
            String cujName = InteractionJankMonitor.getNameOfCuj(mRunningCujs.keyAt(i));
                String cujName = Cuj.getNameOfCuj(mRunningCujs.keyAt(i));
                canvas.translate(0, cujNameTextHeight);
                canvas.drawText(cujName, 0, 0, mDebugPaint);
            }
        }
    }
}
Loading