Include the display's resolution in the Jank reports.

Tracks the resolutions of the displays and includes the appropriate
resolution in the tracked stats.

Bug: 241174561
Test: atest com.android.internal.jank
Change-Id: I57d9d02f2b9bd9cf451ee4466f3c65b1bb1cf7b4
This commit is contained in:
Pascal Muetschard
2022-08-19 12:10:30 +02:00
parent 636c721c73
commit 0e2ac808cc
6 changed files with 286 additions and 4 deletions

View File

@@ -0,0 +1,162 @@
/*
* 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);
}
}

View File

@@ -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));
}
}

View File

@@ -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();
}
}
/**

View File

@@ -0,0 +1,86 @@
/*
* 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;
}
}

View File

@@ -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 */,

View File

@@ -354,10 +354,11 @@ public class InteractionJankMonitorTest {
when(configuration.isSurfaceOnly()).thenReturn(false);
when(configuration.getView()).thenReturn(mView);
when(configuration.getHandler()).thenReturn(mView.getHandler());
when(configuration.getDisplayId()).thenReturn(42);
FrameTracker tracker = spy(new FrameTracker(monitor, session, mWorker.getThreadHandler(),
threadedRenderer, viewRoot, surfaceControl, choreographer,
new FrameMetricsWrapper(), new StatsLogWrapper(),
new FrameMetricsWrapper(), new StatsLogWrapper(null),
/* traceThresholdMissedFrames= */ 1,
/* traceThresholdFrameTimeMillis= */ -1, listener, configuration));