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

Commit 7d3ae07e authored by Pascal Mütschard's avatar Pascal Mütschard Committed by Android (Google) Code Review
Browse files

Merge "Include the display's resolution in the Jank reports."

parents 5c828a8a 0e2ac808
Loading
Loading
Loading
Loading
+162 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.internal.jank;

import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__DISPLAY_RESOLUTION__FHD;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__DISPLAY_RESOLUTION__HD;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__DISPLAY_RESOLUTION__QHD;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__DISPLAY_RESOLUTION__SD;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__DISPLAY_RESOLUTION__UNKNOWN_RESOLUTION;

import android.annotation.IntDef;
import android.annotation.Nullable;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManagerGlobal;
import android.os.Handler;
import android.util.SparseArray;
import android.view.DisplayInfo;

import com.android.internal.annotations.VisibleForTesting;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

/**
* A class that tracks the display resolutions.
* @hide
*/
public class DisplayResolutionTracker {
    private static final String TAG = DisplayResolutionTracker.class.getSimpleName();

    public static final int RESOLUTION_UNKNOWN =
            UIINTERACTION_FRAME_INFO_REPORTED__DISPLAY_RESOLUTION__UNKNOWN_RESOLUTION;
    public static final int RESOLUTION_SD =
            UIINTERACTION_FRAME_INFO_REPORTED__DISPLAY_RESOLUTION__SD;
    public static final int RESOLUTION_HD =
            UIINTERACTION_FRAME_INFO_REPORTED__DISPLAY_RESOLUTION__HD;
    public static final int RESOLUTION_FHD =
            UIINTERACTION_FRAME_INFO_REPORTED__DISPLAY_RESOLUTION__FHD;
    public static final int RESOLUTION_QHD =
            UIINTERACTION_FRAME_INFO_REPORTED__DISPLAY_RESOLUTION__QHD;

    /** @hide */
    @IntDef({
        RESOLUTION_UNKNOWN,
        RESOLUTION_SD,
        RESOLUTION_HD,
        RESOLUTION_FHD,
        RESOLUTION_QHD,
    })
    @Retention(RetentionPolicy.SOURCE)
    public @interface Resolution {
    }

    private final DisplayInterface mManager;
    private final SparseArray<Integer> mResolutions = new SparseArray<>();
    private final Object mLock = new Object();

    public DisplayResolutionTracker(@Nullable Handler handler) {
        this(DisplayInterface.getDefault(handler));
    }

    @VisibleForTesting
    public DisplayResolutionTracker(DisplayInterface manager) {
        mManager = manager;
        mManager.registerDisplayListener(new DisplayManager.DisplayListener() {
            @Override
            public void onDisplayAdded(int displayId) {
                updateDisplay(displayId);
            }

            @Override
            public void onDisplayChanged(int displayId) {
                updateDisplay(displayId);
            }

            @Override
            public void onDisplayRemoved(int displayId) {
                // Not in the event mask below, won't be called.
            }
        });
    }

    private void updateDisplay(int displayId) {
        DisplayInfo info = mManager.getDisplayInfo(displayId);
        @Resolution int resolution = getResolution(info);

        synchronized (mLock) {
            mResolutions.put(displayId, resolution);
        }
    }

    /**
     * Returns the (cached) resolution of the display with the given ID.
     */
    @Resolution
    public int getResolution(int displayId) {
        return mResolutions.get(displayId, RESOLUTION_UNKNOWN);
    }

    /**
     * Returns the resolution of the given {@link DisplayInfo}.
     */
    @VisibleForTesting
    @Resolution
    public static int getResolution(DisplayInfo info) {
        int smaller = Math.min(info.logicalWidth, info.logicalHeight);
        int larger = Math.max(info.logicalWidth, info.logicalHeight);
        if (smaller < 720 || larger < 1280) {
            return RESOLUTION_SD;
        } else if (smaller < 1080 || larger < 1920) {
            return RESOLUTION_HD;
        } else if (smaller < 1440 || larger < 2560) {
            return RESOLUTION_FHD;
        } else {
            return RESOLUTION_QHD;
        }
    }

    /**
     * Wrapper around the final {@link DisplayManagerGlobal} class.
     * @hide
     */
    @VisibleForTesting
    public interface DisplayInterface {
        /** Reurns an implementation wrapping {@link DisplayManagerGlobal}. */
        static DisplayInterface getDefault(@Nullable Handler handler) {
            DisplayManagerGlobal manager = DisplayManagerGlobal.getInstance();
            return new DisplayInterface() {
                @Override
                public void registerDisplayListener(DisplayManager.DisplayListener listener) {
                    manager.registerDisplayListener(listener, handler,
                            DisplayManager.EVENT_FLAG_DISPLAY_ADDED
                                    | DisplayManager.EVENT_FLAG_DISPLAY_CHANGED);
                }

                @Override
                public DisplayInfo getDisplayInfo(int displayId) {
                    return manager.getDisplayInfo(displayId);
                }
            };
        }

        /** {@see DisplayManagerGlobal#registerDisplayListener} */
        void registerDisplayListener(DisplayManager.DisplayListener listener);
        /** {@see DisplayManagerGlobal#getDisplayInfo} */
        DisplayInfo getDisplayInfo(int displayId);
    }
}
+13 −2
Original line number Diff line number Diff line
@@ -100,6 +100,7 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
    private final Session mSession;
    private final ViewRootWrapper mViewRoot;
    private final SurfaceControlWrapper mSurfaceControlWrapper;
    private final int mDisplayId;
    private final ViewRootImpl.SurfaceChangedCallback mSurfaceChangedCallback;
    private final Handler mHandler;
    private final ChoreographerWrapper mChoreographer;
@@ -213,6 +214,7 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
        mTraceThresholdMissedFrames = traceThresholdMissedFrames;
        mTraceThresholdFrameTimeMillis = traceThresholdFrameTimeMillis;
        mListener = listener;
        mDisplayId = config.getDisplayId();

        if (mSurfaceOnly) {
            mSurfaceControl = config.getSurfaceControl();
@@ -625,6 +627,7 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
        if (mSession.logToStatsd()) {
            mStatsLog.write(
                    FrameworkStatsLog.UI_INTERACTION_FRAME_INFO_REPORTED,
                    mDisplayId,
                    mSession.getStatsdInteractionType(),
                    totalFramesCount,
                    missedFramesCount,
@@ -785,9 +788,17 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
    }

    public static class StatsLogWrapper {
        public void write(int code,
        private final DisplayResolutionTracker mDisplayResolutionTracker;

        public StatsLogWrapper(DisplayResolutionTracker displayResolutionTracker) {
            mDisplayResolutionTracker = displayResolutionTracker;
        }

        /** {@see FrameworkStatsLog#write) */
        public void write(int code, int displayId,
                int arg1, long arg2, long arg3, long arg4, long arg5, long arg6, long arg7) {
            FrameworkStatsLog.write(code, arg1, arg2, arg3, arg4, arg5, arg6, arg7);
            FrameworkStatsLog.write(code, arg1, arg2, arg3, arg4, arg5, arg6, arg7,
                    mDisplayResolutionTracker.getResolution(displayId));
        }
    }

+12 −1
Original line number Diff line number Diff line
@@ -306,6 +306,7 @@ public class InteractionJankMonitor {
    @GuardedBy("mLock")
    private final SparseArray<Runnable> mTimeoutActions;
    private final HandlerThread mWorker;
    private final DisplayResolutionTracker mDisplayResolutionTracker;
    private final Object mLock = new Object();

    private volatile boolean mEnabled = DEFAULT_ENABLED;
@@ -408,6 +409,7 @@ public class InteractionJankMonitor {
        mWorker = worker;
        mWorker.start();
        mSamplingInterval = DEFAULT_SAMPLING_INTERVAL;
        mDisplayResolutionTracker = new DisplayResolutionTracker(worker.getThreadHandler());

        // Post initialization to the background in case we're running on the main
        // thread.
@@ -443,7 +445,8 @@ public class InteractionJankMonitor {
        final FrameMetricsWrapper frameMetrics = new FrameMetricsWrapper();

        return new FrameTracker(this, session, config.getHandler(), threadedRenderer, viewRoot,
                surfaceControl, choreographer, frameMetrics, new FrameTracker.StatsLogWrapper(),
                surfaceControl, choreographer, frameMetrics,
                new FrameTracker.StatsLogWrapper(mDisplayResolutionTracker),
                mTraceThresholdMissedFrames, mTraceThresholdFrameTimeMillis,
                eventsListener, config);
    }
@@ -1098,6 +1101,14 @@ public class InteractionJankMonitor {
        public Handler getHandler() {
            return mHandler;
        }

        /**
         * @return the ID of the display this interaction in on.
         */
        @VisibleForTesting
        public int getDisplayId() {
            return (mSurfaceOnly ? mContext.getDisplay() : mView.getDisplay()).getDisplayId();
        }
    }

    /**
+86 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.internal.jank;

import static com.android.internal.jank.DisplayResolutionTracker.getResolution;

import static com.google.common.truth.Truth.assertThat;

import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import android.hardware.display.DisplayManager;
import android.view.DisplayInfo;

import androidx.test.filters.SmallTest;

import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;

@SmallTest
public class DisplayResolutionTrackerTest {
    private static final DisplayInfo SD = makeDisplayInfo(800, 600);
    private static final DisplayInfo HD = makeDisplayInfo(720, 1280);
    private static final DisplayInfo FHD = makeDisplayInfo(2340, 1080);
    private static final DisplayInfo QHD = makeDisplayInfo(3120, 1440);

    private DisplayResolutionTracker.DisplayInterface mDisplayManager;
    private ArgumentCaptor<DisplayManager.DisplayListener> mListenerCaptor;
    private DisplayResolutionTracker mTracker;

    @Before
    public void setup() throws Exception {
        mDisplayManager = mock(DisplayResolutionTracker.DisplayInterface.class);
        mListenerCaptor = ArgumentCaptor.forClass(DisplayManager.DisplayListener.class);

        mTracker = new DisplayResolutionTracker(mDisplayManager);

        verify(mDisplayManager).registerDisplayListener(mListenerCaptor.capture());
    }

    @Test
    public void testResolutionMapping() {
        assertThat(getResolution(SD)).isEqualTo(DisplayResolutionTracker.RESOLUTION_SD);
        assertThat(getResolution(HD)).isEqualTo(DisplayResolutionTracker.RESOLUTION_HD);
        assertThat(getResolution(FHD)).isEqualTo(DisplayResolutionTracker.RESOLUTION_FHD);
        assertThat(getResolution(QHD)).isEqualTo(DisplayResolutionTracker.RESOLUTION_QHD);
    }

    @Test
    public void testResolutionUpdatesOnDisplayChanges() throws Exception {
        assertThat(mTracker.getResolution(42))
                .isEqualTo(DisplayResolutionTracker.RESOLUTION_UNKNOWN);

        when(mDisplayManager.getDisplayInfo(42)).thenReturn(FHD, QHD);

        mListenerCaptor.getValue().onDisplayAdded(42);
        assertThat(mTracker.getResolution(42))
                .isEqualTo(DisplayResolutionTracker.RESOLUTION_FHD);
        mListenerCaptor.getValue().onDisplayChanged(42);
        assertThat(mTracker.getResolution(42))
                .isEqualTo(DisplayResolutionTracker.RESOLUTION_QHD);
    }

    private static DisplayInfo makeDisplayInfo(int width, int height) {
        DisplayInfo info = new DisplayInfo();
        info.logicalWidth = width;
        info.logicalHeight = height;
        return info;
    }
}
+11 −0
Original line number Diff line number Diff line
@@ -124,6 +124,7 @@ public class FrameTrackerTest {
        when(config.isSurfaceOnly()).thenReturn(surfaceOnly);
        when(config.getSurfaceControl()).thenReturn(mSurfaceControl);
        when(config.shouldDeferMonitor()).thenReturn(true);
        when(config.getDisplayId()).thenReturn(42);
        View view = mRule.getActivity().getWindow().getDecorView();
        Handler spyHandler = spy(new Handler(handler.getLooper()));
        when(config.getView()).thenReturn(surfaceOnly ? null : view);
@@ -168,6 +169,7 @@ public class FrameTrackerTest {
        verify(tracker).removeObservers();
        verify(tracker, never()).triggerPerfetto();
        verify(mStatsLog).write(eq(UI_INTERACTION_FRAME_INFO_REPORTED),
                eq(42), /* displayId */
                eq(CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE]),
                eq(2L) /* totalFrames */,
                eq(0L) /* missedFrames */,
@@ -204,6 +206,7 @@ public class FrameTrackerTest {
        verify(tracker).triggerPerfetto();

        verify(mStatsLog).write(eq(UI_INTERACTION_FRAME_INFO_REPORTED),
                eq(42), /* displayId */
                eq(CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE]),
                eq(2L) /* totalFrames */,
                eq(1L) /* missedFrames */,
@@ -240,6 +243,7 @@ public class FrameTrackerTest {
        verify(tracker, never()).triggerPerfetto();

        verify(mStatsLog).write(eq(UI_INTERACTION_FRAME_INFO_REPORTED),
                eq(42), /* displayId */
                eq(CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE]),
                eq(2L) /* totalFrames */,
                eq(0L) /* missedFrames */,
@@ -276,6 +280,7 @@ public class FrameTrackerTest {
        verify(tracker).triggerPerfetto();

        verify(mStatsLog).write(eq(UI_INTERACTION_FRAME_INFO_REPORTED),
                eq(42), /* displayId */
                eq(CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE]),
                eq(2L) /* totalFrames */,
                eq(1L) /* missedFrames */,
@@ -315,6 +320,7 @@ public class FrameTrackerTest {
        verify(tracker).triggerPerfetto();

        verify(mStatsLog).write(eq(UI_INTERACTION_FRAME_INFO_REPORTED),
                eq(42), /* displayId */
                eq(CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE]),
                eq(2L) /* totalFrames */,
                eq(1L) /* missedFrames */,
@@ -356,6 +362,7 @@ public class FrameTrackerTest {
        verify(tracker).removeObservers();
        verify(tracker, never()).triggerPerfetto();
        verify(mStatsLog).write(eq(UI_INTERACTION_FRAME_INFO_REPORTED),
                eq(42), /* displayId */
                eq(CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE]),
                eq(2L) /* totalFrames */,
                eq(0L) /* missedFrames */,
@@ -483,6 +490,7 @@ public class FrameTrackerTest {
        verify(tracker).triggerPerfetto();

        verify(mStatsLog).write(eq(UI_INTERACTION_FRAME_INFO_REPORTED),
                eq(42), /* displayId */
                eq(CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_WALLPAPER_TRANSITION]),
                eq(2L) /* totalFrames */,
                eq(1L) /* missedFrames */,
@@ -519,6 +527,7 @@ public class FrameTrackerTest {
        verify(tracker, never()).triggerPerfetto();

        verify(mStatsLog).write(eq(UI_INTERACTION_FRAME_INFO_REPORTED),
                eq(42), /* displayId */
                eq(CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_WALLPAPER_TRANSITION]),
                eq(2L) /* totalFrames */,
                eq(0L) /* missedFrames */,
@@ -555,6 +564,7 @@ public class FrameTrackerTest {
        verify(tracker, never()).triggerPerfetto();

        verify(mStatsLog).write(eq(UI_INTERACTION_FRAME_INFO_REPORTED),
                eq(42), /* displayId */
                eq(CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_WALLPAPER_TRANSITION]),
                eq(2L) /* totalFrames */,
                eq(0L) /* missedFrames */,
@@ -585,6 +595,7 @@ public class FrameTrackerTest {
        verify(mSurfaceControlWrapper).removeJankStatsListener(any());
        verify(tracker).triggerPerfetto();
        verify(mStatsLog).write(eq(UI_INTERACTION_FRAME_INFO_REPORTED),
                eq(42), /* displayId */
                eq(CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_WALLPAPER_TRANSITION]),
                eq(6L) /* totalFrames */,
                eq(5L) /* missedFrames */,
Loading