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

Commit 320858c5 authored by Ahan Wu's avatar Ahan Wu
Browse files

Send broadcast to notify the events of a CUJ session

Send broadcast to notify the CUJ events to who is interested in.

Bug: 171912046
Test: atest FrameworksCoreTests:InteractionJankMonitorTest
Test: atest FrameworksCoreTests:FrameTrackerTest
Change-Id: I91ab025385c635d15d7be6f7f12597e8900e2e5d
parent 318641ee
Loading
Loading
Loading
Loading
+26 −2
Original line number Diff line number Diff line
@@ -25,6 +25,9 @@ import static android.view.SurfaceControl.JankData.JANK_SURFACEFLINGER_GPU_DEADL
import static android.view.SurfaceControl.JankData.PREDICTION_ERROR;
import static android.view.SurfaceControl.JankData.SURFACE_FLINGER_SCHEDULING;

import static com.android.internal.jank.InteractionJankMonitor.ACTION_METRICS_LOGGED;
import static com.android.internal.jank.InteractionJankMonitor.ACTION_SESSION_BEGIN;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.HardwareRendererObserver;
@@ -72,6 +75,7 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
    private long mEndVsyncId = INVALID_ID;
    private boolean mMetricsFinalized;
    private boolean mCancelled = false;
    private FrameTrackerListener mListener;

    private static class JankInfo {
        long frameVsyncId;
@@ -109,7 +113,7 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
            @NonNull SurfaceControlWrapper surfaceControlWrapper,
            @NonNull ChoreographerWrapper choreographer,
            @NonNull FrameMetricsWrapper metrics, int traceThresholdMissedFrames,
            int traceThresholdFrameTimeMillis) {
            int traceThresholdFrameTimeMillis, @Nullable FrameTrackerListener listener) {
        mSession = session;
        mRendererWrapper = renderer;
        mMetricsWrapper = metrics;
@@ -120,6 +124,7 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
        mObserver = new HardwareRendererObserver(this, mMetricsWrapper.getTiming(), handler);
        mTraceThresholdMissedFrames = traceThresholdMissedFrames;
        mTraceThresholdFrameTimeMillis = traceThresholdFrameTimeMillis;
        mListener = listener;

        // If the surface isn't valid yet, wait until it's created.
        if (viewRootWrapper.getSurfaceControl().isValid()) {
@@ -165,11 +170,15 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
     */
    public synchronized void begin() {
        mBeginVsyncId = mChoreographer.getVsyncId() + 1;
        mSession.setTimeStamp(System.nanoTime());
        Trace.beginAsyncSection(mSession.getName(), (int) mBeginVsyncId);
        mRendererWrapper.addObserver(mObserver);
        if (mSurfaceControl != null) {
            mSurfaceControlWrapper.addJankStatsListener(this, mSurfaceControl);
        }
        if (mListener != null) {
            mListener.onNotifyCujEvents(mSession, ACTION_SESSION_BEGIN);
        }
    }

    /**
@@ -224,7 +233,6 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
    }

    private boolean isInRange(long vsyncId) {

        // It's possible that we may miss a callback for the frame with vsyncId == mEndVsyncId.
        // Because of that, we collect all frames even if they happen after the end so we eventually
        // have a frame after the end with both callbacks present.
@@ -371,6 +379,9 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
                    missedAppFramesCount + missedSfFramesCounts,
                    maxFrameTimeNanos,
                    missedSfFramesCounts);
            if (mListener != null) {
                mListener.onNotifyCujEvents(mSession, ACTION_METRICS_LOGGED);
            }
        }
        if (DEBUG) {
            Log.i(TAG, "FrameTracker: CUJ=" + mSession.getName()
@@ -495,4 +506,17 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
            return mChoreographer.getVsyncId();
        }
    }

    /**
     * A listener that notifies cuj events.
     */
    public interface FrameTrackerListener {
        /**
         * Notify that the CUJ session was created.
         *
         * @param session the CUJ session
         * @param action the specific action
         */
        void onNotifyCujEvents(Session session, String action);
    }
}
+39 −1
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package com.android.internal.jank;

import static android.content.Intent.FLAG_RECEIVER_REGISTERED_ONLY;

import static com.android.internal.jank.FrameTracker.ChoreographerWrapper;
import static com.android.internal.jank.FrameTracker.SurfaceControlWrapper;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_ALL_APPS_SCROLL;
@@ -48,9 +50,12 @@ import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_IN

import android.annotation.IntDef;
import android.annotation.NonNull;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.HandlerExecutor;
import android.os.HandlerThread;
import android.os.SystemProperties;
import android.provider.DeviceConfig;
import android.util.Log;
import android.util.SparseArray;
@@ -59,6 +64,7 @@ import android.view.View;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.jank.FrameTracker.FrameMetricsWrapper;
import com.android.internal.jank.FrameTracker.FrameTrackerListener;
import com.android.internal.jank.FrameTracker.ThreadedRendererWrapper;
import com.android.internal.jank.FrameTracker.ViewRootWrapper;
import com.android.internal.util.PerfettoTrigger;
@@ -74,6 +80,8 @@ import java.util.concurrent.TimeUnit;
 */
public class InteractionJankMonitor {
    private static final String TAG = InteractionJankMonitor.class.getSimpleName();
    private static final String ACTION_PREFIX = InteractionJankMonitor.class.getCanonicalName();

    private static final String DEFAULT_WORKER_NAME = TAG + "-Worker";
    private static final long DEFAULT_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(5L);
    private static final String SETTINGS_ENABLED_KEY = "enabled";
@@ -90,6 +98,14 @@ public class InteractionJankMonitor {
    private static final int DEFAULT_TRACE_THRESHOLD_MISSED_FRAMES = 3;
    private static final int DEFAULT_TRACE_THRESHOLD_FRAME_TIME_MILLIS = 64;

    public static final String ACTION_SESSION_BEGIN = ACTION_PREFIX + ".ACTION_SESSION_BEGIN";
    public static final String ACTION_SESSION_END = ACTION_PREFIX + ".ACTION_SESSION_END";
    public static final String ACTION_METRICS_LOGGED = ACTION_PREFIX + ".ACTION_METRICS_LOGGED";
    public static final String BUNDLE_KEY_CUJ_NAME = ACTION_PREFIX + ".CUJ_NAME";
    public static final String BUNDLE_KEY_TIMESTAMP = ACTION_PREFIX + ".TIMESTAMP";
    @VisibleForTesting
    public static final String PROP_NOTIFY_CUJ_EVENT = "debug.notify_cuj_events";

    // Every value must have a corresponding entry in CUJ_STATSD_INTERACTION_TYPE.
    public static final int CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE = 0;
    public static final int CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE_LOCK = 1;
@@ -256,15 +272,28 @@ public class InteractionJankMonitor {
     */
    @VisibleForTesting
    public FrameTracker createFrameTracker(View v, Session session) {
        final Context c = v.getContext().getApplicationContext();
        synchronized (this) {
            boolean needListener = SystemProperties.getBoolean(PROP_NOTIFY_CUJ_EVENT, false);
            FrameTrackerListener eventsListener =
                    !needListener ? null : (s, act) -> notifyEvents(c, act, s);

            return new FrameTracker(session, mWorker.getThreadHandler(),
                    new ThreadedRendererWrapper(v.getThreadedRenderer()),
                    new ViewRootWrapper(v.getViewRootImpl()), new SurfaceControlWrapper(),
                    new ChoreographerWrapper(Choreographer.getInstance()), mMetrics,
                    mTraceThresholdMissedFrames, mTraceThresholdFrameTimeMillis);
                    mTraceThresholdMissedFrames, mTraceThresholdFrameTimeMillis, eventsListener);
        }
    }

    private void notifyEvents(Context context, String action, Session session) {
        Intent intent = new Intent(action);
        intent.putExtra(BUNDLE_KEY_CUJ_NAME, getNameOfCuj(session.getCuj()));
        intent.putExtra(BUNDLE_KEY_TIMESTAMP, session.getTimeStamp());
        intent.addFlags(FLAG_RECEIVER_REGISTERED_ONLY);
        context.sendBroadcast(intent);
    }

    /**
     * Begin a trace session.
     *
@@ -479,6 +508,7 @@ public class InteractionJankMonitor {
    public static class Session {
        @CujType
        private int mCujType;
        private long mTimeStamp;

        public Session(@CujType int cujType) {
            mCujType = cujType;
@@ -505,5 +535,13 @@ public class InteractionJankMonitor {
        public String getName() {
            return "J<" + getNameOfCuj(mCujType) + ">";
        }

        public void setTimeStamp(long timeStamp) {
            mTimeStamp = timeStamp;
        }

        public long getTimeStamp() {
            return mTimeStamp;
        }
    }
}
+2 −1
Original line number Diff line number Diff line
@@ -105,7 +105,8 @@ public class FrameTrackerTest {
        mTracker = Mockito.spy(
                new FrameTracker(session, handler, mRenderer, mViewRootWrapper,
                        mSurfaceControlWrapper, mChoreographer, mWrapper,
                        /*traceThresholdMissedFrames=*/ 1, /*traceThresholdFrameTimeMillis=*/ -1));
                        /*traceThresholdMissedFrames=*/ 1, /*traceThresholdFrameTimeMillis=*/ -1,
                        null));
        doNothing().when(mTracker).triggerPerfetto();
    }

+6 −15
Original line number Diff line number Diff line
@@ -96,7 +96,7 @@ public class InteractionJankMonitorTest {
                new ViewRootWrapper(mView.getViewRootImpl()), new SurfaceControlWrapper(),
                mock(FrameTracker.ChoreographerWrapper.class),
                new FrameMetricsWrapper(), /*traceThresholdMissedFrames=*/ 1,
                /*traceThresholdFrameTimeMillis=*/ -1));
                /*traceThresholdFrameTimeMillis=*/ -1, null));
        doReturn(tracker).when(monitor).createFrameTracker(any(), any());

        // Simulate a trace session and see if begin / end are invoked.
@@ -123,21 +123,12 @@ public class InteractionJankMonitorTest {
    @Test
    public void testCheckInitState() {
        InteractionJankMonitor monitor = new InteractionJankMonitor(mWorker);
        View view = new View(mActivity);
        assertThat(view.isAttachedToWindow()).isFalse();

        // Should return false if invoking begin / end without init invocation.
        assertThat(monitor.begin(mView, CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE)).isFalse();
        // Should return false if the view passed in is not attached to window yet.
        assertThat(monitor.begin(view, CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE)).isFalse();
        assertThat(monitor.end(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE)).isFalse();

        // Everything should be fine if invoking init first.
        boolean thrown = false;
        try {
            assertThat(monitor.begin(mView, CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE)).isTrue();
            assertThat(monitor.end(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE)).isTrue();
        } catch (Exception ex) {
            thrown = true;
        } finally {
            assertThat(thrown).isFalse();
        }
    }

    @Test
@@ -152,7 +143,7 @@ public class InteractionJankMonitorTest {
                new ViewRootWrapper(mView.getViewRootImpl()), new SurfaceControlWrapper(),
                mock(FrameTracker.ChoreographerWrapper.class),
                new FrameMetricsWrapper(), /*traceThresholdMissedFrames=*/ 1,
                /*traceThresholdFrameTimeMillis=*/ -1));
                /*traceThresholdFrameTimeMillis=*/ -1, null));
        doReturn(tracker).when(monitor).createFrameTracker(any(), any());

        assertThat(monitor.begin(mView, session.getCuj())).isTrue();