Handle DISPLAY_EVENT_FRAME_RATE_OVERRIDE
When getting DISPLAY_EVENT_FRAME_RATE_OVERRIDE from SurfaceFlinger,
expose the overridden frame rate to the relevant application
if the current refresh rate allows that.
Bug: 169271059
Bug: 169271062
Bug: 170503758
Test: manual test using SF backdoor
adb shell service call SurfaceFlinger 1039 i32 <uid> f <refresh rate>
Change-Id: I6ae1a98e6ca13e9d3d095a5713a6b0ca99652256
diff --git a/services/core/java/com/android/server/display/DisplayDeviceInfo.java b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
index fe6500e..468d825 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceInfo.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
@@ -22,6 +22,7 @@
import android.view.Display;
import android.view.DisplayAddress;
import android.view.DisplayCutout;
+import android.view.DisplayEventReceiver;
import android.view.Surface;
import java.util.Arrays;
@@ -333,6 +334,9 @@
*/
public String ownerPackageName;
+ public DisplayEventReceiver.FrameRateOverride[] frameRateOverrides =
+ new DisplayEventReceiver.FrameRateOverride[0];
+
public void setAssumedDensityForExternalDisplay(int width, int height) {
densityDpi = Math.min(width, height) * DisplayMetrics.DENSITY_XHIGH / 1080;
// Technically, these values should be smaller than the apparent density
@@ -386,7 +390,8 @@
|| !Objects.equals(address, other.address)
|| !Objects.equals(deviceProductInfo, other.deviceProductInfo)
|| ownerUid != other.ownerUid
- || !Objects.equals(ownerPackageName, other.ownerPackageName)) {
+ || !Objects.equals(ownerPackageName, other.ownerPackageName)
+ || !Objects.equals(frameRateOverrides, other.frameRateOverrides)) {
diff |= DIFF_OTHER;
}
return diff;
@@ -425,6 +430,7 @@
state = other.state;
ownerUid = other.ownerUid;
ownerPackageName = other.ownerPackageName;
+ frameRateOverrides = other.frameRateOverrides;
}
// For debugging purposes
@@ -461,6 +467,10 @@
sb.append(", owner ").append(ownerPackageName);
sb.append(" (uid ").append(ownerUid).append(")");
}
+ sb.append(", frameRateOverride ");
+ for (DisplayEventReceiver.FrameRateOverride frameRateOverride : frameRateOverrides) {
+ sb.append(frameRateOverride).append(" ");
+ }
sb.append(flagsToString(flags));
sb.append("}");
return sb.toString();
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 004e481..3ccd208 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -37,6 +37,9 @@
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.AppOpsManager;
+import android.app.compat.CompatChanges;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledSince;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.ParceledListSlice;
@@ -83,7 +86,9 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
+import android.sysprop.DisplayProperties;
import android.text.TextUtils;
+import android.util.ArraySet;
import android.util.EventLog;
import android.util.IntArray;
import android.util.Pair;
@@ -92,6 +97,7 @@
import android.util.SparseIntArray;
import android.util.Spline;
import android.view.Display;
+import android.view.DisplayEventReceiver;
import android.view.DisplayInfo;
import android.view.IDisplayFoldListener;
import android.view.Surface;
@@ -114,6 +120,7 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Optional;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.Consumer;
@@ -181,6 +188,7 @@
private static final int MSG_REQUEST_TRAVERSAL = 4;
private static final int MSG_UPDATE_VIEWPORT = 5;
private static final int MSG_LOAD_BRIGHTNESS_CONFIGURATION = 6;
+ private static final int MSG_DELIVER_DISPLAY_EVENT_FRAME_RATE_OVERRIDE = 7;
private final Context mContext;
private final DisplayManagerHandler mHandler;
@@ -357,6 +365,30 @@
// Received notifications of the display-fold action
private DisplayFoldListener mDisplayFoldListener;
+ private final boolean mAllowNonNativeRefreshRateOverride;
+
+ private static final float THRESHOLD_FOR_REFRESH_RATES_DIVIDERS = 0.1f;
+
+ /**
+ * Applications use {@link android.view.Display#getRefreshRate} and
+ * {@link android.view.Display.Mode#getRefreshRate} to know what is the display refresh rate.
+ * Starting with Android S, the platform might throttle down applications frame rate to a
+ * divisor of the refresh rate if it is more preferable (for example if the application called
+ * to {@link android.view.Surface#setFrameRate}).
+ * Applications will experience {@link android.view.Choreographer#postFrameCallback} callbacks
+ * and backpressure at the throttled frame rate.
+ *
+ * {@link android.view.Display#getRefreshRate} will always return the application frame rate
+ * and not the physical display refresh rate to allow applications to do frame pacing correctly.
+ *
+ * {@link android.view.Display.Mode#getRefreshRate} will return the application frame rate if
+ * compiled to a previous release and starting with Android S it will return the physical
+ * display refresh rate.
+ */
+ @ChangeId
+ @EnabledSince(targetSdkVersion = android.os.Build.VERSION_CODES.S)
+ static final long DISPLAY_MODE_RETURNS_PHYSICAL_REFRESH_RATE = 170503758L;
+
public DisplayManagerService(Context context) {
this(context, new Injector());
}
@@ -389,6 +421,7 @@
mCurrentUserId = UserHandle.USER_SYSTEM;
ColorSpace[] colorSpaces = SurfaceControl.getCompositionColorSpaces();
mWideColorSpace = colorSpaces[1];
+ mAllowNonNativeRefreshRateOverride = mInjector.getAllowNonNativeRefreshRateOverride();
mSystemReady = false;
}
@@ -677,11 +710,82 @@
Settings.Secure.MINIMAL_POST_PROCESSING_ALLOWED, 1, UserHandle.USER_CURRENT) != 0;
}
+ private DisplayInfo getDisplayInfoForFrameRateOverride(DisplayEventReceiver.FrameRateOverride[]
+ frameRateOverrides, DisplayInfo info, int callingUid) {
+ float frameRateHz = 0;
+ for (DisplayEventReceiver.FrameRateOverride frameRateOverride : frameRateOverrides) {
+ if (frameRateOverride.uid == callingUid) {
+ frameRateHz = frameRateOverride.frameRateHz;
+ break;
+ }
+ }
+ if (frameRateHz == 0) {
+ return info;
+ }
+
+ // Override the refresh rate only if it is a divider of the current
+ // refresh rate. This calculation needs to be in sync with the native code
+ // in RefreshRateConfigs::getRefreshRateDividerForUid
+ Display.Mode currentMode = info.getMode();
+ float numPeriods = currentMode.getRefreshRate() / frameRateHz;
+ float numPeriodsRound = Math.round(numPeriods);
+ if (Math.abs(numPeriods - numPeriodsRound) > THRESHOLD_FOR_REFRESH_RATES_DIVIDERS) {
+ return info;
+ }
+ frameRateHz = currentMode.getRefreshRate() / numPeriodsRound;
+
+ DisplayInfo overriddenInfo = new DisplayInfo();
+ overriddenInfo.copyFrom(info);
+ for (Display.Mode mode : info.supportedModes) {
+ if (!mode.equalsExceptRefreshRate(currentMode)) {
+ continue;
+ }
+
+ if (mode.getRefreshRate() >= frameRateHz - THRESHOLD_FOR_REFRESH_RATES_DIVIDERS
+ && mode.getRefreshRate()
+ <= frameRateHz + THRESHOLD_FOR_REFRESH_RATES_DIVIDERS) {
+ if (DEBUG) {
+ Slog.d(TAG, "found matching modeId " + mode.getModeId());
+ }
+ overriddenInfo.refreshRateOverride = mode.getRefreshRate();
+
+ if (!CompatChanges.isChangeEnabled(DISPLAY_MODE_RETURNS_PHYSICAL_REFRESH_RATE,
+ callingUid)) {
+ overriddenInfo.modeId = mode.getModeId();
+ }
+ return overriddenInfo;
+ }
+ }
+
+ if (mAllowNonNativeRefreshRateOverride) {
+ overriddenInfo.refreshRateOverride = frameRateHz;
+ if (!CompatChanges.isChangeEnabled(DISPLAY_MODE_RETURNS_PHYSICAL_REFRESH_RATE,
+ callingUid)) {
+ overriddenInfo.supportedModes = Arrays.copyOf(info.supportedModes,
+ info.supportedModes.length + 1);
+ overriddenInfo.supportedModes[overriddenInfo.supportedModes.length - 1] =
+ new Display.Mode(Display.DISPLAY_MODE_ID_FOR_FRAME_RATE_OVERRIDE,
+ currentMode.getPhysicalWidth(), currentMode.getPhysicalHeight(),
+ overriddenInfo.refreshRateOverride);
+ overriddenInfo.modeId =
+ overriddenInfo.supportedModes[overriddenInfo.supportedModes.length - 1]
+ .getModeId();
+ }
+ return overriddenInfo;
+ }
+
+
+
+ return info;
+ }
+
private DisplayInfo getDisplayInfoInternal(int displayId, int callingUid) {
synchronized (mSyncRoot) {
LogicalDisplay display = mLogicalDisplayMapper.getLocked(displayId);
if (display != null) {
- DisplayInfo info = display.getDisplayInfoLocked();
+ DisplayInfo info =
+ getDisplayInfoForFrameRateOverride(display.getFrameRateOverrides(),
+ display.getDisplayInfoLocked(), callingUid);
if (info.hasAccess(callingUid)
|| isUidPresentOnDisplayInternal(callingUid, displayId)) {
return info;
@@ -691,14 +795,15 @@
}
}
- private void registerCallbackInternal(IDisplayManagerCallback callback, int callingPid) {
+ private void registerCallbackInternal(IDisplayManagerCallback callback, int callingPid,
+ int callingUid) {
synchronized (mSyncRoot) {
if (mCallbacks.get(callingPid) != null) {
throw new SecurityException("The calling process has already "
+ "registered an IDisplayManagerCallback.");
}
- CallbackRecord record = new CallbackRecord(callingPid, callback);
+ CallbackRecord record = new CallbackRecord(callingPid, callingUid, callback);
try {
IBinder binder = callback.asBinder();
binder.linkToDeath(record, 0);
@@ -1034,6 +1139,16 @@
scheduleTraversalLocked(false);
}
+ private void handleLogicalDisplayFrameRateOverridesChangedLocked(
+ @NonNull LogicalDisplay display) {
+ final int displayId = display.getDisplayIdLocked();
+ // We don't bother invalidating the display info caches here because any changes to the
+ // display info will trigger a cache invalidation inside of LogicalDisplay before we hit
+ // this point.
+ sendDisplayEventFrameRateOverrideLocked(displayId);
+ scheduleTraversalLocked(false);
+ }
+
private void handleLogicalDisplayRemovedLocked(@NonNull LogicalDisplay display) {
final int displayId = display.getDisplayIdLocked();
mDisplayPowerControllers.delete(displayId);
@@ -1545,6 +1660,12 @@
mHandler.sendMessage(msg);
}
+ private void sendDisplayEventFrameRateOverrideLocked(int displayId) {
+ Message msg = mHandler.obtainMessage(MSG_DELIVER_DISPLAY_EVENT_FRAME_RATE_OVERRIDE,
+ displayId, DisplayManagerGlobal.EVENT_DISPLAY_CHANGED);
+ mHandler.sendMessage(msg);
+ }
+
// Requests that performTraversals be called at a
// later time to apply changes to surfaces and displays.
private void scheduleTraversalLocked(boolean inTraversal) {
@@ -1558,7 +1679,7 @@
// Runs on Handler thread.
// Delivers display event notifications to callbacks.
- private void deliverDisplayEvent(int displayId, int event) {
+ private void deliverDisplayEvent(int displayId, ArraySet<Integer> uids, int event) {
if (DEBUG) {
Slog.d(TAG, "Delivering display event: displayId="
+ displayId + ", event=" + event);
@@ -1570,12 +1691,14 @@
count = mCallbacks.size();
mTempCallbacks.clear();
for (int i = 0; i < count; i++) {
- mTempCallbacks.add(mCallbacks.valueAt(i));
+ if (uids == null || uids.contains(mCallbacks.valueAt(i).mUid)) {
+ mTempCallbacks.add(mCallbacks.valueAt(i));
+ }
}
}
// After releasing the lock, send the notifications out.
- for (int i = 0; i < count; i++) {
+ for (int i = 0; i < mTempCallbacks.size(); i++) {
mTempCallbacks.get(i).notifyDisplayEventAsync(displayId, event);
}
mTempCallbacks.clear();
@@ -1691,6 +1814,11 @@
long getDefaultDisplayDelayTimeout() {
return WAIT_FOR_DEFAULT_DISPLAY_TIMEOUT;
}
+
+ boolean getAllowNonNativeRefreshRateOverride() {
+ return DisplayProperties
+ .debug_allow_non_native_refresh_rate_override().orElse(false);
+ }
}
@VisibleForTesting
@@ -1760,7 +1888,7 @@
break;
case MSG_DELIVER_DISPLAY_EVENT:
- deliverDisplayEvent(msg.arg1, msg.arg2);
+ deliverDisplayEvent(msg.arg1, null, msg.arg2);
break;
case MSG_REQUEST_TRAVERSAL:
@@ -1787,6 +1915,17 @@
case MSG_LOAD_BRIGHTNESS_CONFIGURATION:
loadBrightnessConfiguration();
break;
+
+ case MSG_DELIVER_DISPLAY_EVENT_FRAME_RATE_OVERRIDE:
+ ArraySet<Integer> uids;
+ synchronized (mSyncRoot) {
+ int displayId = msg.arg1;
+ LogicalDisplay display = mLogicalDisplayMapper.getLocked(displayId);
+ uids = display.getPendingFrameRateOverrideUids();
+ display.clearPendingFrameRateOverrideUids();
+ }
+ deliverDisplayEvent(msg.arg1, uids, msg.arg2);
+ break;
}
}
}
@@ -1810,6 +1949,10 @@
case LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_SWAPPED:
handleLogicalDisplaySwappedLocked(display);
break;
+
+ case LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_FRAME_RATE_OVERRIDES_CHANGED:
+ handleLogicalDisplayFrameRateOverridesChangedLocked(display);
+ break;
}
}
@@ -1823,12 +1966,14 @@
private final class CallbackRecord implements DeathRecipient {
public final int mPid;
+ public final int mUid;
private final IDisplayManagerCallback mCallback;
public boolean mWifiDisplayScanRequested;
- public CallbackRecord(int pid, IDisplayManagerCallback callback) {
+ CallbackRecord(int pid, int uid, IDisplayManagerCallback callback) {
mPid = pid;
+ mUid = uid;
mCallback = callback;
}
@@ -1918,9 +2063,10 @@
}
final int callingPid = Binder.getCallingPid();
+ final int callingUid = Binder.getCallingUid();
final long token = Binder.clearCallingIdentity();
try {
- registerCallbackInternal(callback, callingPid);
+ registerCallbackInternal(callback, callingPid, callingUid);
} finally {
Binder.restoreCallingIdentity(token);
}
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index f657858..74ea2d7 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -208,6 +208,9 @@
private DisplayDeviceConfig mDisplayDeviceConfig;
+ private DisplayEventReceiver.FrameRateOverride[] mFrameRateOverrides =
+ new DisplayEventReceiver.FrameRateOverride[0];
+
LocalDisplayDevice(IBinder displayToken, long physicalDisplayId,
SurfaceControl.DisplayInfo info, SurfaceControl.DisplayConfig[] configs,
int activeConfigId, SurfaceControl.DesiredDisplayConfigSpecs configSpecs,
@@ -625,6 +628,8 @@
mInfo.name = getContext().getResources().getString(
com.android.internal.R.string.display_manager_hdmi_display_name);
}
+ mInfo.frameRateOverrides = mFrameRateOverrides;
+
// The display is trusted since it is created by system.
mInfo.flags |= DisplayDeviceInfo.FLAG_TRUSTED;
}
@@ -882,6 +887,13 @@
}
}
+ public void onFrameRateOverridesChanged(
+ DisplayEventReceiver.FrameRateOverride[] overrides) {
+ if (updateFrameRateOverridesLocked(overrides)) {
+ updateDeviceInfoLocked();
+ }
+ }
+
public boolean updateActiveModeLocked(int activeConfigId) {
if (mActiveConfigId == activeConfigId) {
return false;
@@ -895,6 +907,16 @@
return true;
}
+ public boolean updateFrameRateOverridesLocked(
+ DisplayEventReceiver.FrameRateOverride[] overrides) {
+ if (overrides.equals(mFrameRateOverrides)) {
+ return false;
+ }
+
+ mFrameRateOverrides = overrides;
+ return true;
+ }
+
public void requestColorModeLocked(int colorMode) {
if (mActiveColorMode == colorMode) {
return;
@@ -1102,23 +1124,39 @@
public interface DisplayEventListener {
void onHotplug(long timestampNanos, long physicalDisplayId, boolean connected);
void onConfigChanged(long timestampNanos, long physicalDisplayId, int configId);
+ void onFrameRateOverridesChanged(long timestampNanos, long physicalDisplayId,
+ DisplayEventReceiver.FrameRateOverride[] overrides);
+
}
public static final class ProxyDisplayEventReceiver extends DisplayEventReceiver {
private final DisplayEventListener mListener;
ProxyDisplayEventReceiver(Looper looper, DisplayEventListener listener) {
- super(looper, VSYNC_SOURCE_APP, CONFIG_CHANGED_EVENT_DISPATCH);
+ super(looper, VSYNC_SOURCE_APP,
+ EVENT_REGISTRATION_CONFIG_CHANGED_FLAG
+ | EVENT_REGISTRATION_FRAME_RATE_OVERRIDE_FLAG);
mListener = listener;
}
+
+ @Override
public void onHotplug(long timestampNanos, long physicalDisplayId, boolean connected) {
mListener.onHotplug(timestampNanos, physicalDisplayId, connected);
}
+
+ @Override
public void onConfigChanged(long timestampNanos, long physicalDisplayId, int configId) {
mListener.onConfigChanged(timestampNanos, physicalDisplayId, configId);
}
+
+ @Override
+ public void onFrameRateOverridesChanged(long timestampNanos, long physicalDisplayId,
+ DisplayEventReceiver.FrameRateOverride[] overrides) {
+ mListener.onFrameRateOverridesChanged(timestampNanos, physicalDisplayId, overrides);
+ }
}
private final class LocalDisplayEventListener implements DisplayEventListener {
+ @Override
public void onHotplug(long timestampNanos, long physicalDisplayId, boolean connected) {
synchronized (getSyncRoot()) {
if (connected) {
@@ -1128,6 +1166,8 @@
}
}
}
+
+ @Override
public void onConfigChanged(long timestampNanos, long physicalDisplayId, int configId) {
if (DEBUG) {
Slog.d(TAG, "onConfigChanged("
@@ -1147,5 +1187,26 @@
device.onActiveDisplayConfigChangedLocked(configId);
}
}
+
+ @Override
+ public void onFrameRateOverridesChanged(long timestampNanos, long physicalDisplayId,
+ DisplayEventReceiver.FrameRateOverride[] overrides) {
+ if (DEBUG) {
+ Slog.d(TAG, "onFrameRateOverrideChanged(timestampNanos=" + timestampNanos
+ + ", physicalDisplayId=" + physicalDisplayId + " overrides="
+ + Arrays.toString(overrides) + ")");
+ }
+ synchronized (getSyncRoot()) {
+ LocalDisplayDevice device = mDevices.get(physicalDisplayId);
+ if (device == null) {
+ if (DEBUG) {
+ Slog.d(TAG, "Received frame rate override event for unhandled physical"
+ + " display: physicalDisplayId=" + physicalDisplayId);
+ }
+ return;
+ }
+ device.onFrameRateOverridesChanged(overrides);
+ }
+ }
}
}
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index 979c3b8..d80e168 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -20,8 +20,11 @@
import android.graphics.Point;
import android.graphics.Rect;
import android.hardware.display.DisplayManagerInternal;
+import android.util.ArraySet;
import android.util.Slog;
+import android.util.SparseArray;
import android.view.Display;
+import android.view.DisplayEventReceiver;
import android.view.DisplayInfo;
import android.view.Surface;
import android.view.SurfaceControl;
@@ -123,10 +126,27 @@
*/
private boolean mIsEnabled = true;
+ /**
+ * The UID mappings for refresh rate override
+ */
+ private DisplayEventReceiver.FrameRateOverride[] mFrameRateOverrides;
+
+ /**
+ * Holds a set of UIDs that their frame rate override changed and needs to be notified
+ */
+ private ArraySet<Integer> mPendingFrameRateOverrideUids;
+
+ /**
+ * Temporary frame rate override list, used when needed.
+ */
+ private final SparseArray<Float> mTempFrameRateOverride;
+
public LogicalDisplay(int displayId, int layerStack, DisplayDevice primaryDisplayDevice) {
mDisplayId = displayId;
mLayerStack = layerStack;
mPrimaryDisplayDevice = primaryDisplayDevice;
+ mPendingFrameRateOverrideUids = new ArraySet<>();
+ mTempFrameRateOverride = new SparseArray<>();
}
/**
@@ -179,6 +199,27 @@
}
/**
+ * Returns the frame rate overrides list
+ */
+ public DisplayEventReceiver.FrameRateOverride[] getFrameRateOverrides() {
+ return mFrameRateOverrides;
+ }
+
+ /**
+ * Returns the list of uids that needs to be updated about their frame rate override
+ */
+ public ArraySet<Integer> getPendingFrameRateOverrideUids() {
+ return mPendingFrameRateOverrideUids;
+ }
+
+ /**
+ * Clears the list of uids that needs to be updated about their frame rate override
+ */
+ public void clearPendingFrameRateOverrideUids() {
+ mPendingFrameRateOverrideUids = new ArraySet<>();
+ }
+
+ /**
* @see DisplayManagerInternal#getNonOverrideDisplayInfo(int, DisplayInfo)
*/
void getNonOverrideDisplayInfoLocked(DisplayInfo outInfo) {
@@ -324,12 +365,40 @@
(deviceInfo.flags & DisplayDeviceInfo.FLAG_MASK_DISPLAY_CUTOUT) != 0;
mBaseDisplayInfo.displayCutout = maskCutout ? null : deviceInfo.displayCutout;
mBaseDisplayInfo.displayId = mDisplayId;
+ updateFrameRateOverrides(deviceInfo);
mPrimaryDisplayDeviceInfo = deviceInfo;
mInfo.set(null);
}
}
+ private void updateFrameRateOverrides(DisplayDeviceInfo deviceInfo) {
+ mTempFrameRateOverride.clear();
+ if (mFrameRateOverrides != null) {
+ for (DisplayEventReceiver.FrameRateOverride frameRateOverride
+ : mFrameRateOverrides) {
+ mTempFrameRateOverride.put(frameRateOverride.uid,
+ frameRateOverride.frameRateHz);
+ }
+ }
+ mFrameRateOverrides = deviceInfo.frameRateOverrides;
+ if (mFrameRateOverrides != null) {
+ for (DisplayEventReceiver.FrameRateOverride frameRateOverride
+ : mFrameRateOverrides) {
+ float refreshRate = mTempFrameRateOverride.get(frameRateOverride.uid, 0f);
+ if (refreshRate == 0 || frameRateOverride.frameRateHz != refreshRate) {
+ mTempFrameRateOverride.put(frameRateOverride.uid,
+ frameRateOverride.frameRateHz);
+ } else {
+ mTempFrameRateOverride.delete(frameRateOverride.uid);
+ }
+ }
+ }
+ for (int i = 0; i < mTempFrameRateOverride.size(); i++) {
+ mPendingFrameRateOverrideUids.add(mTempFrameRateOverride.keyAt(i));
+ }
+ }
+
/**
* Return the insets currently applied to the display.
*
@@ -638,5 +707,7 @@
pw.println("mBaseDisplayInfo=" + mBaseDisplayInfo);
pw.println("mOverrideDisplayInfo=" + mOverrideDisplayInfo);
pw.println("mRequestedMinimalPostProcessing=" + mRequestedMinimalPostProcessing);
+ pw.println("mFrameRateOverrides=" + Arrays.toString(mFrameRateOverrides));
+ pw.println("mPendingFrameRateOverrideUids=" + mPendingFrameRateOverrideUids);
}
}
diff --git a/services/core/java/com/android/server/display/LogicalDisplayMapper.java b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
index 45c38b4..ec8af78 100644
--- a/services/core/java/com/android/server/display/LogicalDisplayMapper.java
+++ b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
@@ -23,6 +23,7 @@
import android.util.Slog;
import android.util.SparseArray;
import android.view.Display;
+import android.view.DisplayEventReceiver;
import android.view.DisplayInfo;
import com.android.internal.util.IndentingPrintWriter;
@@ -52,6 +53,7 @@
public static final int LOGICAL_DISPLAY_EVENT_CHANGED = 2;
public static final int LOGICAL_DISPLAY_EVENT_REMOVED = 3;
public static final int LOGICAL_DISPLAY_EVENT_SWAPPED = 4;
+ public static final int LOGICAL_DISPLAY_EVENT_FRAME_RATE_OVERRIDES_CHANGED = 5;
/**
* Temporary display info, used for comparing display configurations.
@@ -331,6 +333,8 @@
mTempDisplayInfo.copyFrom(display.getDisplayInfoLocked());
display.getNonOverrideDisplayInfoLocked(mTempNonOverrideDisplayInfo);
+ DisplayEventReceiver.FrameRateOverride[] frameRatesOverrides =
+ display.getFrameRateOverrides();
display.updateLocked(mDisplayDeviceRepo);
if (!display.isValidLocked()) {
mLogicalDisplays.removeAt(i);
@@ -364,6 +368,9 @@
final int eventMsg = TextUtils.equals(oldUniqueId, newUniqueId)
? LOGICAL_DISPLAY_EVENT_CHANGED : LOGICAL_DISPLAY_EVENT_SWAPPED;
mListener.onLogicalDisplayEventLocked(display, eventMsg);
+ } else if (!display.getPendingFrameRateOverrideUids().isEmpty()) {
+ mListener.onLogicalDisplayEventLocked(display,
+ LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_FRAME_RATE_OVERRIDES_CHANGED);
} else {
// While applications shouldn't know nor care about the non-overridden info, we
// still need to let WindowManager know so it can update its own internal state for
diff --git a/services/core/java/com/android/server/display/OverlayDisplayWindow.java b/services/core/java/com/android/server/display/OverlayDisplayWindow.java
index 49f0d35..cd3a453 100644
--- a/services/core/java/com/android/server/display/OverlayDisplayWindow.java
+++ b/services/core/java/com/android/server/display/OverlayDisplayWindow.java
@@ -16,8 +16,6 @@
package com.android.server.display;
-import com.android.internal.util.DumpUtils;
-
import android.content.Context;
import android.graphics.SurfaceTexture;
import android.hardware.display.DisplayManager;
@@ -30,12 +28,14 @@
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.TextureView;
+import android.view.TextureView.SurfaceTextureListener;
import android.view.ThreadedRenderer;
import android.view.View;
import android.view.WindowManager;
-import android.view.TextureView.SurfaceTextureListener;
import android.widget.TextView;
+import com.android.internal.util.DumpUtils;
+
import java.io.PrintWriter;
/**
@@ -319,7 +319,7 @@
public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture,
int width, int height) {
mListener.onWindowCreated(surfaceTexture,
- mDefaultDisplayInfo.getMode().getRefreshRate(),
+ mDefaultDisplayInfo.getRefreshRate(),
mDefaultDisplayInfo.presentationDeadlineNanos, mDefaultDisplayInfo.state);
}
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index 1f72374..8ca057a 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -49,6 +49,8 @@
// TODO: remove once Android migrates to JUnit 4.12,
// which provides assertThrows
"testng",
+ "junit",
+ "platform-compat-test-rules",
],
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
index 026db42..640d6e5 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -28,6 +28,7 @@
import static org.mockito.Mockito.when;
import android.app.PropertyInvalidatedCache;
+import android.compat.testing.PlatformCompatChangeRule;
import android.content.Context;
import android.graphics.Insets;
import android.graphics.Rect;
@@ -44,8 +45,10 @@
import android.hardware.input.InputManagerInternal;
import android.os.Handler;
import android.os.IBinder;
+import android.os.Process;
import android.view.Display;
import android.view.DisplayCutout;
+import android.view.DisplayEventReceiver;
import android.view.DisplayInfo;
import android.view.Surface;
import android.view.SurfaceControl;
@@ -57,13 +60,17 @@
import com.android.server.LocalServices;
import com.android.server.SystemService;
-import com.android.server.display.DisplayDeviceInfo;
import com.android.server.display.DisplayManagerService.SyncRoot;
import com.android.server.lights.LightsManager;
import com.android.server.wm.WindowManagerInternal;
+import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
+import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
+
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.TestRule;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
@@ -80,6 +87,9 @@
private static final String VIRTUAL_DISPLAY_NAME = "Test Virtual Display";
private static final String PACKAGE_NAME = "com.android.frameworks.servicestests";
+ @Rule
+ public TestRule compatChangeRule = new PlatformCompatChangeRule();
+
private Context mContext;
private final DisplayManagerService.Injector mShortMockedInjector =
@@ -95,15 +105,31 @@
return SHORT_DEFAULT_DISPLAY_TIMEOUT_MILLIS;
}
};
- private final DisplayManagerService.Injector mBasicInjector =
- new DisplayManagerService.Injector() {
+
+ class BasicInjector extends DisplayManagerService.Injector {
+ @Override
+ VirtualDisplayAdapter getVirtualDisplayAdapter(SyncRoot syncRoot, Context context,
+ Handler handler, DisplayAdapter.Listener displayAdapterListener) {
+ return new VirtualDisplayAdapter(syncRoot, context, handler, displayAdapterListener,
+ (String name, boolean secure) -> mMockDisplayToken);
+ }
+ }
+
+ private final DisplayManagerService.Injector mBasicInjector = new BasicInjector();
+
+ private final DisplayManagerService.Injector mAllowNonNativeRefreshRateOverrideInjector =
+ new BasicInjector() {
@Override
- VirtualDisplayAdapter getVirtualDisplayAdapter(SyncRoot syncRoot,
- Context context, Handler handler,
- DisplayAdapter.Listener displayAdapterListener) {
- return new VirtualDisplayAdapter(syncRoot, context, handler,
- displayAdapterListener,
- (String name, boolean secure) -> mMockDisplayToken);
+ boolean getAllowNonNativeRefreshRateOverride() {
+ return true;
+ }
+ };
+
+ private final DisplayManagerService.Injector mDenyNonNativeRefreshRateOverrideInjector =
+ new BasicInjector() {
+ @Override
+ boolean getAllowNonNativeRefreshRateOverride() {
+ return false;
}
};
@@ -575,6 +601,337 @@
assertEquals(displayManager.getVirtualDisplaySurfaceInternal(mMockAppToken), surface);
}
+ /**
+ * Tests that there should be a display change notification if the frame rate overrides
+ * list is updated.
+ */
+ @Test
+ public void testShouldNotifyChangeWhenDisplayInfoFrameRateOverrideChanged() throws Exception {
+ DisplayManagerService displayManager =
+ new DisplayManagerService(mContext, mShortMockedInjector);
+ DisplayManagerService.BinderService displayManagerBinderService =
+ displayManager.new BinderService();
+ registerDefaultDisplays(displayManager);
+ displayManager.onBootPhase(SystemService.PHASE_WAIT_FOR_DEFAULT_DISPLAY);
+
+ FakeDisplayDevice displayDevice = createFakeDisplayDevice(displayManager, new float[]{60f});
+ FakeDisplayManagerCallback callback = registerDisplayListenerCallback(displayManager,
+ displayManagerBinderService, displayDevice);
+
+ int myUid = Process.myUid();
+ updateFrameRateOverride(displayManager, displayDevice,
+ new DisplayEventReceiver.FrameRateOverride[]{
+ new DisplayEventReceiver.FrameRateOverride(myUid, 30f),
+ });
+ assertTrue(callback.mCalled);
+ callback.clear();
+
+ updateFrameRateOverride(displayManager, displayDevice,
+ new DisplayEventReceiver.FrameRateOverride[]{
+ new DisplayEventReceiver.FrameRateOverride(myUid, 30f),
+ new DisplayEventReceiver.FrameRateOverride(1234, 30f),
+ });
+ assertFalse(callback.mCalled);
+
+ updateFrameRateOverride(displayManager, displayDevice,
+ new DisplayEventReceiver.FrameRateOverride[]{
+ new DisplayEventReceiver.FrameRateOverride(myUid, 20f),
+ new DisplayEventReceiver.FrameRateOverride(1234, 30f),
+ new DisplayEventReceiver.FrameRateOverride(5678, 30f),
+ });
+ assertTrue(callback.mCalled);
+ callback.clear();
+
+ updateFrameRateOverride(displayManager, displayDevice,
+ new DisplayEventReceiver.FrameRateOverride[]{
+ new DisplayEventReceiver.FrameRateOverride(1234, 30f),
+ new DisplayEventReceiver.FrameRateOverride(5678, 30f),
+ });
+ assertTrue(callback.mCalled);
+ callback.clear();
+
+ updateFrameRateOverride(displayManager, displayDevice,
+ new DisplayEventReceiver.FrameRateOverride[]{
+ new DisplayEventReceiver.FrameRateOverride(5678, 30f),
+ });
+ assertFalse(callback.mCalled);
+ }
+
+ /**
+ * Tests that the DisplayInfo is updated correctly with a frame rate override
+ */
+ @Test
+ public void testDisplayInfoFrameRateOverride() throws Exception {
+ DisplayManagerService displayManager =
+ new DisplayManagerService(mContext, mShortMockedInjector);
+ DisplayManagerService.BinderService displayManagerBinderService =
+ displayManager.new BinderService();
+ registerDefaultDisplays(displayManager);
+ displayManager.onBootPhase(SystemService.PHASE_WAIT_FOR_DEFAULT_DISPLAY);
+
+ FakeDisplayDevice displayDevice = createFakeDisplayDevice(displayManager,
+ new float[]{60f, 30f, 20f});
+ int displayId = getDisplayIdForDisplayDevice(displayManager, displayManagerBinderService,
+ displayDevice);
+ DisplayInfo displayInfo = displayManagerBinderService.getDisplayInfo(displayId);
+ assertEquals(60f, displayInfo.getRefreshRate(), 0.01f);
+
+ updateFrameRateOverride(displayManager, displayDevice,
+ new DisplayEventReceiver.FrameRateOverride[]{
+ new DisplayEventReceiver.FrameRateOverride(
+ Process.myUid(), 20f),
+ new DisplayEventReceiver.FrameRateOverride(
+ Process.myUid() + 1, 30f)
+ });
+ displayInfo = displayManagerBinderService.getDisplayInfo(displayId);
+ assertEquals(20f, displayInfo.getRefreshRate(), 0.01f);
+
+ // Changing the mode to 30Hz should not override the refresh rate to 20Hz anymore
+ // as 20 is not a divider of 30.
+ updateModeId(displayManager, displayDevice, 2);
+ displayInfo = displayManagerBinderService.getDisplayInfo(displayId);
+ assertEquals(30f, displayInfo.getRefreshRate(), 0.01f);
+ }
+
+ /**
+ * Tests that the frame rate override is updated accordingly to the
+ * allowNonNativeRefreshRateOverride policy.
+ */
+ @Test
+ public void testDisplayInfoNonNativeFrameRateOverride() throws Exception {
+ testDisplayInfoNonNativeFrameRateOverride(mDenyNonNativeRefreshRateOverrideInjector);
+ testDisplayInfoNonNativeFrameRateOverride(mAllowNonNativeRefreshRateOverrideInjector);
+ }
+
+ /**
+ * Tests that the mode reflects the frame rate override is in compat mode
+ */
+ @Test
+ @DisableCompatChanges({DisplayManagerService.DISPLAY_MODE_RETURNS_PHYSICAL_REFRESH_RATE})
+ public void testDisplayInfoFrameRateOverrideModeCompat() throws Exception {
+ testDisplayInfoFrameRateOverrideModeCompat(/*compatChangeEnabled*/ false);
+ }
+
+ /**
+ * Tests that the mode reflects the physical display refresh rate when not in compat mode.
+ */
+ @Test
+ @EnableCompatChanges({DisplayManagerService.DISPLAY_MODE_RETURNS_PHYSICAL_REFRESH_RATE})
+ public void testDisplayInfoFrameRateOverrideMode() throws Exception {
+ testDisplayInfoFrameRateOverrideModeCompat(/*compatChangeEnabled*/ true);
+ }
+
+ /**
+ * Tests that the mode reflects the frame rate override is in compat mode and accordingly to the
+ * allowNonNativeRefreshRateOverride policy.
+ */
+ @Test
+ @DisableCompatChanges({DisplayManagerService.DISPLAY_MODE_RETURNS_PHYSICAL_REFRESH_RATE})
+ public void testDisplayInfoNonNativeFrameRateOverrideModeCompat() throws Exception {
+ testDisplayInfoNonNativeFrameRateOverrideMode(mDenyNonNativeRefreshRateOverrideInjector,
+ /*compatChangeEnabled*/ false);
+ testDisplayInfoNonNativeFrameRateOverrideMode(mAllowNonNativeRefreshRateOverrideInjector,
+ /*compatChangeEnabled*/ false);
+ }
+
+ /**
+ * Tests that the mode reflects the physical display refresh rate when not in compat mode.
+ */
+ @Test
+ @EnableCompatChanges({DisplayManagerService.DISPLAY_MODE_RETURNS_PHYSICAL_REFRESH_RATE})
+ public void testDisplayInfoNonNativeFrameRateOverrideMode() throws Exception {
+ testDisplayInfoNonNativeFrameRateOverrideMode(mDenyNonNativeRefreshRateOverrideInjector,
+ /*compatChangeEnabled*/ true);
+ testDisplayInfoNonNativeFrameRateOverrideMode(mAllowNonNativeRefreshRateOverrideInjector,
+ /*compatChangeEnabled*/ true);
+ }
+
+ private void testDisplayInfoFrameRateOverrideModeCompat(boolean compatChangeEnabled)
+ throws Exception {
+ DisplayManagerService displayManager =
+ new DisplayManagerService(mContext, mShortMockedInjector);
+ DisplayManagerService.BinderService displayManagerBinderService =
+ displayManager.new BinderService();
+ registerDefaultDisplays(displayManager);
+ displayManager.onBootPhase(SystemService.PHASE_WAIT_FOR_DEFAULT_DISPLAY);
+
+ FakeDisplayDevice displayDevice = createFakeDisplayDevice(displayManager,
+ new float[]{60f, 30f, 20f});
+ int displayId = getDisplayIdForDisplayDevice(displayManager, displayManagerBinderService,
+ displayDevice);
+ DisplayInfo displayInfo = displayManagerBinderService.getDisplayInfo(displayId);
+ assertEquals(60f, displayInfo.getRefreshRate(), 0.01f);
+
+ updateFrameRateOverride(displayManager, displayDevice,
+ new DisplayEventReceiver.FrameRateOverride[]{
+ new DisplayEventReceiver.FrameRateOverride(
+ Process.myUid(), 20f),
+ new DisplayEventReceiver.FrameRateOverride(
+ Process.myUid() + 1, 30f)
+ });
+ displayInfo = displayManagerBinderService.getDisplayInfo(displayId);
+ assertEquals(20f, displayInfo.getRefreshRate(), 0.01f);
+ Display.Mode expectedMode;
+ if (compatChangeEnabled) {
+ expectedMode = new Display.Mode(1, 100, 200, 60f);
+ } else {
+ expectedMode = new Display.Mode(3, 100, 200, 20f);
+ }
+ assertEquals(expectedMode, displayInfo.getMode());
+ }
+
+ private void testDisplayInfoNonNativeFrameRateOverrideMode(
+ DisplayManagerService.Injector injector, boolean compatChangeEnabled) {
+ DisplayManagerService displayManager =
+ new DisplayManagerService(mContext, injector);
+ DisplayManagerService.BinderService displayManagerBinderService =
+ displayManager.new BinderService();
+ registerDefaultDisplays(displayManager);
+ displayManager.onBootPhase(SystemService.PHASE_WAIT_FOR_DEFAULT_DISPLAY);
+
+ FakeDisplayDevice displayDevice = createFakeDisplayDevice(displayManager,
+ new float[]{60f});
+ int displayId = getDisplayIdForDisplayDevice(displayManager, displayManagerBinderService,
+ displayDevice);
+ DisplayInfo displayInfo = displayManagerBinderService.getDisplayInfo(displayId);
+ assertEquals(60f, displayInfo.getRefreshRate(), 0.01f);
+
+ updateFrameRateOverride(displayManager, displayDevice,
+ new DisplayEventReceiver.FrameRateOverride[]{
+ new DisplayEventReceiver.FrameRateOverride(
+ Process.myUid(), 20f)
+ });
+ displayInfo = displayManagerBinderService.getDisplayInfo(displayId);
+ Display.Mode expectedMode;
+ if (compatChangeEnabled) {
+ expectedMode = new Display.Mode(1, 100, 200, 60f);
+ } else if (injector.getAllowNonNativeRefreshRateOverride()) {
+ expectedMode = new Display.Mode(255, 100, 200, 20f);
+ } else {
+ expectedMode = new Display.Mode(1, 100, 200, 60f);
+ }
+ assertEquals(expectedMode, displayInfo.getMode());
+ }
+
+ private void testDisplayInfoNonNativeFrameRateOverride(
+ DisplayManagerService.Injector injector) {
+ DisplayManagerService displayManager =
+ new DisplayManagerService(mContext, injector);
+ DisplayManagerService.BinderService displayManagerBinderService =
+ displayManager.new BinderService();
+ registerDefaultDisplays(displayManager);
+ displayManager.onBootPhase(SystemService.PHASE_WAIT_FOR_DEFAULT_DISPLAY);
+
+ FakeDisplayDevice displayDevice = createFakeDisplayDevice(displayManager,
+ new float[]{60f});
+ int displayId = getDisplayIdForDisplayDevice(displayManager, displayManagerBinderService,
+ displayDevice);
+ DisplayInfo displayInfo = displayManagerBinderService.getDisplayInfo(displayId);
+ assertEquals(60f, displayInfo.getRefreshRate(), 0.01f);
+
+ updateFrameRateOverride(displayManager, displayDevice,
+ new DisplayEventReceiver.FrameRateOverride[]{
+ new DisplayEventReceiver.FrameRateOverride(
+ Process.myUid(), 20f)
+ });
+ displayInfo = displayManagerBinderService.getDisplayInfo(displayId);
+ float expectedRefreshRate = injector.getAllowNonNativeRefreshRateOverride() ? 20f : 60f;
+ assertEquals(expectedRefreshRate, displayInfo.getRefreshRate(), 0.01f);
+ }
+
+ private int getDisplayIdForDisplayDevice(
+ DisplayManagerService displayManager,
+ DisplayManagerService.BinderService displayManagerBinderService,
+ FakeDisplayDevice displayDevice) {
+
+ final int[] displayIds = displayManagerBinderService.getDisplayIds();
+ assertTrue(displayIds.length > 0);
+ int displayId = Display.INVALID_DISPLAY;
+ for (int i = 0; i < displayIds.length; i++) {
+ DisplayDeviceInfo ddi = displayManager.getDisplayDeviceInfoInternal(displayIds[i]);
+ if (displayDevice.getDisplayDeviceInfoLocked().equals(ddi)) {
+ displayId = displayIds[i];
+ break;
+ }
+ }
+ assertFalse(displayId == Display.INVALID_DISPLAY);
+ return displayId;
+ }
+
+ private void updateDisplayDeviceInfo(DisplayManagerService displayManager,
+ FakeDisplayDevice displayDevice,
+ DisplayDeviceInfo displayDeviceInfo) {
+ displayDevice.setDisplayDeviceInfo(displayDeviceInfo);
+ displayManager.getDisplayDeviceRepository()
+ .onDisplayDeviceEvent(displayDevice, DisplayAdapter.DISPLAY_DEVICE_EVENT_CHANGED);
+ Handler handler = displayManager.getDisplayHandler();
+ handler.runWithScissors(() -> {
+ }, 0 /* now */);
+ }
+
+ private void updateFrameRateOverride(DisplayManagerService displayManager,
+ FakeDisplayDevice displayDevice,
+ DisplayEventReceiver.FrameRateOverride[] frameRateOverrides) {
+ DisplayDeviceInfo displayDeviceInfo = new DisplayDeviceInfo();
+ displayDeviceInfo.copyFrom(displayDevice.getDisplayDeviceInfoLocked());
+ displayDeviceInfo.frameRateOverrides = frameRateOverrides;
+ updateDisplayDeviceInfo(displayManager, displayDevice, displayDeviceInfo);
+ }
+
+ private void updateModeId(DisplayManagerService displayManager,
+ FakeDisplayDevice displayDevice,
+ int modeId) {
+ DisplayDeviceInfo displayDeviceInfo = new DisplayDeviceInfo();
+ displayDeviceInfo.copyFrom(displayDevice.getDisplayDeviceInfoLocked());
+ displayDeviceInfo.modeId = modeId;
+ updateDisplayDeviceInfo(displayManager, displayDevice, displayDeviceInfo);
+ }
+
+ private FakeDisplayManagerCallback registerDisplayListenerCallback(
+ DisplayManagerService displayManager,
+ DisplayManagerService.BinderService displayManagerBinderService,
+ FakeDisplayDevice displayDevice) {
+ // Find the display id of the added FakeDisplayDevice
+ DisplayDeviceInfo displayDeviceInfo = displayDevice.getDisplayDeviceInfoLocked();
+
+ int displayId = getDisplayIdForDisplayDevice(displayManager, displayManagerBinderService,
+ displayDevice);
+
+ Handler handler = displayManager.getDisplayHandler();
+ handler.runWithScissors(() -> {
+ }, 0 /* now */);
+
+ // register display listener callback
+ FakeDisplayManagerCallback callback = new FakeDisplayManagerCallback(displayId);
+ displayManagerBinderService.registerCallback(callback);
+ return callback;
+ }
+
+ private FakeDisplayDevice createFakeDisplayDevice(DisplayManagerService displayManager,
+ float[] refreshRates) {
+ FakeDisplayDevice displayDevice = new FakeDisplayDevice();
+ DisplayDeviceInfo displayDeviceInfo = new DisplayDeviceInfo();
+ int width = 100;
+ int height = 200;
+ displayDeviceInfo.supportedModes = new Display.Mode[refreshRates.length];
+ for (int i = 0; i < refreshRates.length; i++) {
+ displayDeviceInfo.supportedModes[i] =
+ new Display.Mode(i + 1, width, height, refreshRates[i]);
+ }
+ displayDeviceInfo.modeId = 1;
+ displayDeviceInfo.width = width;
+ displayDeviceInfo.height = height;
+ final Rect zeroRect = new Rect();
+ displayDeviceInfo.displayCutout = new DisplayCutout(
+ Insets.of(0, 10, 0, 0),
+ zeroRect, new Rect(0, 0, 10, 10), zeroRect, zeroRect);
+ displayDeviceInfo.flags = DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY;
+ displayDevice.setDisplayDeviceInfo(displayDeviceInfo);
+ displayManager.getDisplayDeviceRepository()
+ .onDisplayDeviceEvent(displayDevice, DisplayAdapter.DISPLAY_DEVICE_EVENT_ADDED);
+ return displayDevice;
+ }
+
private void registerDefaultDisplays(DisplayManagerService displayManager) {
Handler handler = displayManager.getDisplayHandler();
// Would prefer to call displayManager.onStart() directly here but it performs binderService
@@ -598,6 +955,10 @@
mCalled = true;
}
}
+
+ public void clear() {
+ mCalled = false;
+ }
}
private class FakeDisplayDevice extends DisplayDevice {