Loading core/java/android/view/DisplayInfo.java +10 −1 Original line number Diff line number Diff line Loading @@ -337,6 +337,9 @@ public final class DisplayInfo implements Parcelable { @Nullable public DisplayShape displayShape; @Nullable public SurfaceControl.RefreshRateRange layoutLimitedRefreshRate; public static final @android.annotation.NonNull Creator<DisplayInfo> CREATOR = new Creator<DisplayInfo>() { @Override public DisplayInfo createFromParcel(Parcel source) { Loading Loading @@ -411,7 +414,8 @@ public final class DisplayInfo implements Parcelable { && brightnessDefault == other.brightnessDefault && Objects.equals(roundedCorners, other.roundedCorners) && installOrientation == other.installOrientation && Objects.equals(displayShape, other.displayShape); && Objects.equals(displayShape, other.displayShape) && Objects.equals(layoutLimitedRefreshRate, other.layoutLimitedRefreshRate); } @Override Loading Loading @@ -466,6 +470,7 @@ public final class DisplayInfo implements Parcelable { roundedCorners = other.roundedCorners; installOrientation = other.installOrientation; displayShape = other.displayShape; layoutLimitedRefreshRate = other.layoutLimitedRefreshRate; } public void readFromParcel(Parcel source) { Loading Loading @@ -526,6 +531,7 @@ public final class DisplayInfo implements Parcelable { } installOrientation = source.readInt(); displayShape = source.readTypedObject(DisplayShape.CREATOR); layoutLimitedRefreshRate = source.readTypedObject(SurfaceControl.RefreshRateRange.CREATOR); } @Override Loading Loading @@ -584,6 +590,7 @@ public final class DisplayInfo implements Parcelable { } dest.writeInt(installOrientation); dest.writeTypedObject(displayShape, flags); dest.writeTypedObject(layoutLimitedRefreshRate, flags); } @Override Loading Loading @@ -843,6 +850,8 @@ public final class DisplayInfo implements Parcelable { sb.append(brightnessDefault); sb.append(", installOrientation "); sb.append(Surface.rotationToString(installOrientation)); sb.append(", layoutLimitedRefreshRate "); sb.append(layoutLimitedRefreshRate); sb.append("}"); return sb.toString(); } Loading core/java/android/view/SurfaceControl.java +30 −1 Original line number Diff line number Diff line Loading @@ -1757,7 +1757,7 @@ public final class SurfaceControl implements Parcelable { * Information about the min and max refresh rate DM would like to set the display to. * @hide */ public static final class RefreshRateRange { public static final class RefreshRateRange implements Parcelable { public static final String TAG = "RefreshRateRange"; // The tolerance within which we consider something approximately equals. Loading Loading @@ -1826,6 +1826,35 @@ public final class SurfaceControl implements Parcelable { this.min = other.min; this.max = other.max; } /** * Writes the RefreshRateRange to parce * * @param dest parcel to write the transaction to */ @Override public void writeToParcel(@NonNull Parcel dest, @WriteFlags int flags) { dest.writeFloat(min); dest.writeFloat(max); } @Override public int describeContents() { return 0; } public static final @NonNull Creator<RefreshRateRange> CREATOR = new Creator<RefreshRateRange>() { @Override public RefreshRateRange createFromParcel(Parcel in) { return new RefreshRateRange(in.readFloat(), in.readFloat()); } @Override public RefreshRateRange[] newArray(int size) { return new RefreshRateRange[size]; } }; } /** Loading services/core/java/com/android/server/display/DeviceStateToLayoutMap.java +1 −0 Original line number Diff line number Diff line Loading @@ -133,6 +133,7 @@ class DeviceStateToLayoutMap { } else { display.setPosition(POSITION_UNKNOWN); } display.setRefreshRateZoneId(d.getRefreshRateZoneId()); } } } catch (IOException | DatatypeConfigurationException | XmlPullParserException e) { Loading services/core/java/com/android/server/display/DisplayDeviceConfig.java +42 −3 Original line number Diff line number Diff line Loading @@ -33,6 +33,7 @@ import android.util.Pair; import android.util.Slog; import android.util.Spline; import android.view.DisplayAddress; import android.view.SurfaceControl; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; Loading @@ -53,6 +54,7 @@ import com.android.server.display.config.NitsMap; import com.android.server.display.config.Point; import com.android.server.display.config.RefreshRateConfigs; import com.android.server.display.config.RefreshRateRange; import com.android.server.display.config.RefreshRateZone; import com.android.server.display.config.SdrHdrRatioMap; import com.android.server.display.config.SdrHdrRatioPoint; import com.android.server.display.config.SensorDetails; Loading Loading @@ -503,10 +505,10 @@ public class DisplayDeviceConfig { /** * Array of light sensor lux values to define our levels for auto backlight * brightness support. * * The N + 1 entries of this array define N control points defined in mBrightnessLevelsNits, * with first value always being 0 lux * * The control points must be strictly increasing. Each control point * corresponds to an entry in the brightness backlight values arrays. * For example, if lux == level[1] (second element of the levels array) Loading @@ -515,7 +517,6 @@ public class DisplayDeviceConfig { * * Spline interpolation is used to determine the auto-brightness * backlight values for lux levels between these control points. * */ private float[] mBrightnessLevelsLux; Loading Loading @@ -619,6 +620,10 @@ public class DisplayDeviceConfig { */ private int mDefaultLowBlockingZoneRefreshRate = DEFAULT_LOW_REFRESH_RATE; // Refresh rate profiles, currently only for concurrent mode profile and controlled by Layout private final Map<String, SurfaceControl.RefreshRateRange> mRefreshRateZoneProfiles = new HashMap<>(); /** * The display uses different gamma curves for different refresh rates. It's hard for panel * vendors to tune the curves to have exact same brightness for different refresh rate. So Loading Loading @@ -1353,6 +1358,23 @@ public class DisplayDeviceConfig { return mDefaultLowBlockingZoneRefreshRate; } /** * @return Refresh rate range for specific profile id or null */ @Nullable public SurfaceControl.RefreshRateRange getRefreshRange(@Nullable String id) { if (TextUtils.isEmpty(id)) { return null; } return mRefreshRateZoneProfiles.get(id); } @NonNull @VisibleForTesting Map<String, SurfaceControl.RefreshRateRange> getRefreshRangeProfiles() { return mRefreshRateZoneProfiles; } /** * @return An array of lower display brightness thresholds. This, in combination with lower * ambient brightness thresholds help define buckets in which the refresh rate switching is not Loading Loading @@ -1500,6 +1522,7 @@ public class DisplayDeviceConfig { + ", mDefaultHighBlockingZoneRefreshRate= " + mDefaultHighBlockingZoneRefreshRate + ", mDefaultPeakRefreshRate= " + mDefaultPeakRefreshRate + ", mDefaultRefreshRate= " + mDefaultRefreshRate + ", mRefreshRateZoneProfiles= " + mRefreshRateZoneProfiles + ", mLowDisplayBrightnessThresholds= " + Arrays.toString(mLowDisplayBrightnessThresholds) + ", mLowAmbientBrightnessThresholds= " Loading Loading @@ -1828,6 +1851,7 @@ public class DisplayDeviceConfig { loadDefaultRefreshRate(refreshRateConfigs); loadLowerRefreshRateBlockingZones(lowerBlockingZoneConfig); loadHigherRefreshRateBlockingZones(higherBlockingZoneConfig); loadRefreshRateZoneProfiles(refreshRateConfigs); } private void loadPeakDefaultRefreshRate(RefreshRateConfigs refreshRateConfigs) { Loading @@ -1850,6 +1874,21 @@ public class DisplayDeviceConfig { } } /** Loads the refresh rate profiles. */ private void loadRefreshRateZoneProfiles(RefreshRateConfigs refreshRateConfigs) { if (refreshRateConfigs == null) { return; } for (RefreshRateZone zone : refreshRateConfigs.getRefreshRateZoneProfiles().getRefreshRateZoneProfile()) { RefreshRateRange range = zone.getRefreshRateRange(); mRefreshRateZoneProfiles.put( zone.getId(), new SurfaceControl.RefreshRateRange( range.getMinimum().floatValue(), range.getMaximum().floatValue())); } } /** * Loads the refresh rate configurations pertaining to the upper blocking zones. */ Loading services/core/java/com/android/server/display/DisplayModeDirector.java +36 −11 Original line number Diff line number Diff line Loading @@ -159,7 +159,6 @@ public class DisplayModeDirector { mSupportedModesByDisplay = new SparseArray<>(); mDefaultModeByDisplay = new SparseArray<>(); mAppRequestObserver = new AppRequestObserver(); mDisplayObserver = new DisplayObserver(context, handler); mDeviceConfig = injector.getDeviceConfig(); mDeviceConfigDisplaySettings = new DeviceConfigDisplaySettings(); mSettingsObserver = new SettingsObserver(context, handler); Loading @@ -170,6 +169,7 @@ public class DisplayModeDirector { updateVoteLocked(displayId, priority, vote); } }; mDisplayObserver = new DisplayObserver(context, handler, ballotBox); mSensorObserver = new SensorObserver(context, ballotBox, injector); mSkinThermalStatusObserver = new SkinThermalStatusObserver(injector, ballotBox); mHbmObserver = new HbmObserver(injector, ballotBox, BackgroundThread.getHandler(), Loading Loading @@ -1186,26 +1186,29 @@ public class DisplayModeDirector { // rest of low priority voters. It votes [0, max(PEAK, MIN)] public static final int PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE = 7; // For concurrent displays we want to limit refresh rate on all displays public static final int PRIORITY_LAYOUT_LIMITED_FRAME_RATE = 8; // LOW_POWER_MODE force the render frame rate to [0, 60HZ] if // Settings.Global.LOW_POWER_MODE is on. public static final int PRIORITY_LOW_POWER_MODE = 8; public static final int PRIORITY_LOW_POWER_MODE = 9; // PRIORITY_FLICKER_REFRESH_RATE_SWITCH votes for disabling refresh rate switching. If the // higher priority voters' result is a range, it will fix the rate to a single choice. // It's used to avoid refresh rate switches in certain conditions which may result in the // user seeing the display flickering when the switches occur. public static final int PRIORITY_FLICKER_REFRESH_RATE_SWITCH = 9; public static final int PRIORITY_FLICKER_REFRESH_RATE_SWITCH = 10; // Force display to [0, 60HZ] if skin temperature is at or above CRITICAL. public static final int PRIORITY_SKIN_TEMPERATURE = 10; public static final int PRIORITY_SKIN_TEMPERATURE = 11; // The proximity sensor needs the refresh rate to be locked in order to function, so this is // set to a high priority. public static final int PRIORITY_PROXIMITY = 11; public static final int PRIORITY_PROXIMITY = 12; // The Under-Display Fingerprint Sensor (UDFPS) needs the refresh rate to be locked in order // to function, so this needs to be the highest priority of all votes. public static final int PRIORITY_UDFPS = 12; public static final int PRIORITY_UDFPS = 13; // Whenever a new priority is added, remember to update MIN_PRIORITY, MAX_PRIORITY, and // APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF, as well as priorityToString. Loading Loading @@ -1657,10 +1660,12 @@ public class DisplayModeDirector { // calling into us already holding its own lock. private final Context mContext; private final Handler mHandler; private final BallotBox mBallotBox; DisplayObserver(Context context, Handler handler) { DisplayObserver(Context context, Handler handler, BallotBox ballotBox) { mContext = context; mHandler = handler; mBallotBox = ballotBox; } public void observe() { Loading Loading @@ -1689,7 +1694,9 @@ public class DisplayModeDirector { @Override public void onDisplayAdded(int displayId) { updateDisplayModes(displayId); DisplayInfo displayInfo = getDisplayInfo(displayId); updateDisplayModes(displayId, displayInfo); updateLayoutLimitedFrameRate(displayId, displayInfo); } @Override Loading @@ -1698,23 +1705,41 @@ public class DisplayModeDirector { mSupportedModesByDisplay.remove(displayId); mDefaultModeByDisplay.remove(displayId); } updateLayoutLimitedFrameRate(displayId, null); } @Override public void onDisplayChanged(int displayId) { updateDisplayModes(displayId); DisplayInfo displayInfo = getDisplayInfo(displayId); updateDisplayModes(displayId, displayInfo); updateLayoutLimitedFrameRate(displayId, displayInfo); } private void updateDisplayModes(int displayId) { @Nullable private DisplayInfo getDisplayInfo(int displayId) { Display d = mContext.getSystemService(DisplayManager.class).getDisplay(displayId); if (d == null) { // We can occasionally get a display added or changed event for a display that was // subsequently removed, which means this returns null. Check this case and bail // out early; if it gets re-attached we'll eventually get another call back for it. return; return null; } DisplayInfo info = new DisplayInfo(); d.getDisplayInfo(info); return info; } private void updateLayoutLimitedFrameRate(int displayId, @Nullable DisplayInfo info) { Vote vote = info != null && info.layoutLimitedRefreshRate != null ? Vote.forPhysicalRefreshRates(info.layoutLimitedRefreshRate.min, info.layoutLimitedRefreshRate.max) : null; mBallotBox.vote(displayId, Vote.PRIORITY_LAYOUT_LIMITED_FRAME_RATE, vote); } private void updateDisplayModes(int displayId, @Nullable DisplayInfo info) { if (info == null) { return; } boolean changed = false; synchronized (mLock) { if (!Arrays.equals(mSupportedModesByDisplay.get(displayId), info.supportedModes)) { Loading Loading
core/java/android/view/DisplayInfo.java +10 −1 Original line number Diff line number Diff line Loading @@ -337,6 +337,9 @@ public final class DisplayInfo implements Parcelable { @Nullable public DisplayShape displayShape; @Nullable public SurfaceControl.RefreshRateRange layoutLimitedRefreshRate; public static final @android.annotation.NonNull Creator<DisplayInfo> CREATOR = new Creator<DisplayInfo>() { @Override public DisplayInfo createFromParcel(Parcel source) { Loading Loading @@ -411,7 +414,8 @@ public final class DisplayInfo implements Parcelable { && brightnessDefault == other.brightnessDefault && Objects.equals(roundedCorners, other.roundedCorners) && installOrientation == other.installOrientation && Objects.equals(displayShape, other.displayShape); && Objects.equals(displayShape, other.displayShape) && Objects.equals(layoutLimitedRefreshRate, other.layoutLimitedRefreshRate); } @Override Loading Loading @@ -466,6 +470,7 @@ public final class DisplayInfo implements Parcelable { roundedCorners = other.roundedCorners; installOrientation = other.installOrientation; displayShape = other.displayShape; layoutLimitedRefreshRate = other.layoutLimitedRefreshRate; } public void readFromParcel(Parcel source) { Loading Loading @@ -526,6 +531,7 @@ public final class DisplayInfo implements Parcelable { } installOrientation = source.readInt(); displayShape = source.readTypedObject(DisplayShape.CREATOR); layoutLimitedRefreshRate = source.readTypedObject(SurfaceControl.RefreshRateRange.CREATOR); } @Override Loading Loading @@ -584,6 +590,7 @@ public final class DisplayInfo implements Parcelable { } dest.writeInt(installOrientation); dest.writeTypedObject(displayShape, flags); dest.writeTypedObject(layoutLimitedRefreshRate, flags); } @Override Loading Loading @@ -843,6 +850,8 @@ public final class DisplayInfo implements Parcelable { sb.append(brightnessDefault); sb.append(", installOrientation "); sb.append(Surface.rotationToString(installOrientation)); sb.append(", layoutLimitedRefreshRate "); sb.append(layoutLimitedRefreshRate); sb.append("}"); return sb.toString(); } Loading
core/java/android/view/SurfaceControl.java +30 −1 Original line number Diff line number Diff line Loading @@ -1757,7 +1757,7 @@ public final class SurfaceControl implements Parcelable { * Information about the min and max refresh rate DM would like to set the display to. * @hide */ public static final class RefreshRateRange { public static final class RefreshRateRange implements Parcelable { public static final String TAG = "RefreshRateRange"; // The tolerance within which we consider something approximately equals. Loading Loading @@ -1826,6 +1826,35 @@ public final class SurfaceControl implements Parcelable { this.min = other.min; this.max = other.max; } /** * Writes the RefreshRateRange to parce * * @param dest parcel to write the transaction to */ @Override public void writeToParcel(@NonNull Parcel dest, @WriteFlags int flags) { dest.writeFloat(min); dest.writeFloat(max); } @Override public int describeContents() { return 0; } public static final @NonNull Creator<RefreshRateRange> CREATOR = new Creator<RefreshRateRange>() { @Override public RefreshRateRange createFromParcel(Parcel in) { return new RefreshRateRange(in.readFloat(), in.readFloat()); } @Override public RefreshRateRange[] newArray(int size) { return new RefreshRateRange[size]; } }; } /** Loading
services/core/java/com/android/server/display/DeviceStateToLayoutMap.java +1 −0 Original line number Diff line number Diff line Loading @@ -133,6 +133,7 @@ class DeviceStateToLayoutMap { } else { display.setPosition(POSITION_UNKNOWN); } display.setRefreshRateZoneId(d.getRefreshRateZoneId()); } } } catch (IOException | DatatypeConfigurationException | XmlPullParserException e) { Loading
services/core/java/com/android/server/display/DisplayDeviceConfig.java +42 −3 Original line number Diff line number Diff line Loading @@ -33,6 +33,7 @@ import android.util.Pair; import android.util.Slog; import android.util.Spline; import android.view.DisplayAddress; import android.view.SurfaceControl; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; Loading @@ -53,6 +54,7 @@ import com.android.server.display.config.NitsMap; import com.android.server.display.config.Point; import com.android.server.display.config.RefreshRateConfigs; import com.android.server.display.config.RefreshRateRange; import com.android.server.display.config.RefreshRateZone; import com.android.server.display.config.SdrHdrRatioMap; import com.android.server.display.config.SdrHdrRatioPoint; import com.android.server.display.config.SensorDetails; Loading Loading @@ -503,10 +505,10 @@ public class DisplayDeviceConfig { /** * Array of light sensor lux values to define our levels for auto backlight * brightness support. * * The N + 1 entries of this array define N control points defined in mBrightnessLevelsNits, * with first value always being 0 lux * * The control points must be strictly increasing. Each control point * corresponds to an entry in the brightness backlight values arrays. * For example, if lux == level[1] (second element of the levels array) Loading @@ -515,7 +517,6 @@ public class DisplayDeviceConfig { * * Spline interpolation is used to determine the auto-brightness * backlight values for lux levels between these control points. * */ private float[] mBrightnessLevelsLux; Loading Loading @@ -619,6 +620,10 @@ public class DisplayDeviceConfig { */ private int mDefaultLowBlockingZoneRefreshRate = DEFAULT_LOW_REFRESH_RATE; // Refresh rate profiles, currently only for concurrent mode profile and controlled by Layout private final Map<String, SurfaceControl.RefreshRateRange> mRefreshRateZoneProfiles = new HashMap<>(); /** * The display uses different gamma curves for different refresh rates. It's hard for panel * vendors to tune the curves to have exact same brightness for different refresh rate. So Loading Loading @@ -1353,6 +1358,23 @@ public class DisplayDeviceConfig { return mDefaultLowBlockingZoneRefreshRate; } /** * @return Refresh rate range for specific profile id or null */ @Nullable public SurfaceControl.RefreshRateRange getRefreshRange(@Nullable String id) { if (TextUtils.isEmpty(id)) { return null; } return mRefreshRateZoneProfiles.get(id); } @NonNull @VisibleForTesting Map<String, SurfaceControl.RefreshRateRange> getRefreshRangeProfiles() { return mRefreshRateZoneProfiles; } /** * @return An array of lower display brightness thresholds. This, in combination with lower * ambient brightness thresholds help define buckets in which the refresh rate switching is not Loading Loading @@ -1500,6 +1522,7 @@ public class DisplayDeviceConfig { + ", mDefaultHighBlockingZoneRefreshRate= " + mDefaultHighBlockingZoneRefreshRate + ", mDefaultPeakRefreshRate= " + mDefaultPeakRefreshRate + ", mDefaultRefreshRate= " + mDefaultRefreshRate + ", mRefreshRateZoneProfiles= " + mRefreshRateZoneProfiles + ", mLowDisplayBrightnessThresholds= " + Arrays.toString(mLowDisplayBrightnessThresholds) + ", mLowAmbientBrightnessThresholds= " Loading Loading @@ -1828,6 +1851,7 @@ public class DisplayDeviceConfig { loadDefaultRefreshRate(refreshRateConfigs); loadLowerRefreshRateBlockingZones(lowerBlockingZoneConfig); loadHigherRefreshRateBlockingZones(higherBlockingZoneConfig); loadRefreshRateZoneProfiles(refreshRateConfigs); } private void loadPeakDefaultRefreshRate(RefreshRateConfigs refreshRateConfigs) { Loading @@ -1850,6 +1874,21 @@ public class DisplayDeviceConfig { } } /** Loads the refresh rate profiles. */ private void loadRefreshRateZoneProfiles(RefreshRateConfigs refreshRateConfigs) { if (refreshRateConfigs == null) { return; } for (RefreshRateZone zone : refreshRateConfigs.getRefreshRateZoneProfiles().getRefreshRateZoneProfile()) { RefreshRateRange range = zone.getRefreshRateRange(); mRefreshRateZoneProfiles.put( zone.getId(), new SurfaceControl.RefreshRateRange( range.getMinimum().floatValue(), range.getMaximum().floatValue())); } } /** * Loads the refresh rate configurations pertaining to the upper blocking zones. */ Loading
services/core/java/com/android/server/display/DisplayModeDirector.java +36 −11 Original line number Diff line number Diff line Loading @@ -159,7 +159,6 @@ public class DisplayModeDirector { mSupportedModesByDisplay = new SparseArray<>(); mDefaultModeByDisplay = new SparseArray<>(); mAppRequestObserver = new AppRequestObserver(); mDisplayObserver = new DisplayObserver(context, handler); mDeviceConfig = injector.getDeviceConfig(); mDeviceConfigDisplaySettings = new DeviceConfigDisplaySettings(); mSettingsObserver = new SettingsObserver(context, handler); Loading @@ -170,6 +169,7 @@ public class DisplayModeDirector { updateVoteLocked(displayId, priority, vote); } }; mDisplayObserver = new DisplayObserver(context, handler, ballotBox); mSensorObserver = new SensorObserver(context, ballotBox, injector); mSkinThermalStatusObserver = new SkinThermalStatusObserver(injector, ballotBox); mHbmObserver = new HbmObserver(injector, ballotBox, BackgroundThread.getHandler(), Loading Loading @@ -1186,26 +1186,29 @@ public class DisplayModeDirector { // rest of low priority voters. It votes [0, max(PEAK, MIN)] public static final int PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE = 7; // For concurrent displays we want to limit refresh rate on all displays public static final int PRIORITY_LAYOUT_LIMITED_FRAME_RATE = 8; // LOW_POWER_MODE force the render frame rate to [0, 60HZ] if // Settings.Global.LOW_POWER_MODE is on. public static final int PRIORITY_LOW_POWER_MODE = 8; public static final int PRIORITY_LOW_POWER_MODE = 9; // PRIORITY_FLICKER_REFRESH_RATE_SWITCH votes for disabling refresh rate switching. If the // higher priority voters' result is a range, it will fix the rate to a single choice. // It's used to avoid refresh rate switches in certain conditions which may result in the // user seeing the display flickering when the switches occur. public static final int PRIORITY_FLICKER_REFRESH_RATE_SWITCH = 9; public static final int PRIORITY_FLICKER_REFRESH_RATE_SWITCH = 10; // Force display to [0, 60HZ] if skin temperature is at or above CRITICAL. public static final int PRIORITY_SKIN_TEMPERATURE = 10; public static final int PRIORITY_SKIN_TEMPERATURE = 11; // The proximity sensor needs the refresh rate to be locked in order to function, so this is // set to a high priority. public static final int PRIORITY_PROXIMITY = 11; public static final int PRIORITY_PROXIMITY = 12; // The Under-Display Fingerprint Sensor (UDFPS) needs the refresh rate to be locked in order // to function, so this needs to be the highest priority of all votes. public static final int PRIORITY_UDFPS = 12; public static final int PRIORITY_UDFPS = 13; // Whenever a new priority is added, remember to update MIN_PRIORITY, MAX_PRIORITY, and // APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF, as well as priorityToString. Loading Loading @@ -1657,10 +1660,12 @@ public class DisplayModeDirector { // calling into us already holding its own lock. private final Context mContext; private final Handler mHandler; private final BallotBox mBallotBox; DisplayObserver(Context context, Handler handler) { DisplayObserver(Context context, Handler handler, BallotBox ballotBox) { mContext = context; mHandler = handler; mBallotBox = ballotBox; } public void observe() { Loading Loading @@ -1689,7 +1694,9 @@ public class DisplayModeDirector { @Override public void onDisplayAdded(int displayId) { updateDisplayModes(displayId); DisplayInfo displayInfo = getDisplayInfo(displayId); updateDisplayModes(displayId, displayInfo); updateLayoutLimitedFrameRate(displayId, displayInfo); } @Override Loading @@ -1698,23 +1705,41 @@ public class DisplayModeDirector { mSupportedModesByDisplay.remove(displayId); mDefaultModeByDisplay.remove(displayId); } updateLayoutLimitedFrameRate(displayId, null); } @Override public void onDisplayChanged(int displayId) { updateDisplayModes(displayId); DisplayInfo displayInfo = getDisplayInfo(displayId); updateDisplayModes(displayId, displayInfo); updateLayoutLimitedFrameRate(displayId, displayInfo); } private void updateDisplayModes(int displayId) { @Nullable private DisplayInfo getDisplayInfo(int displayId) { Display d = mContext.getSystemService(DisplayManager.class).getDisplay(displayId); if (d == null) { // We can occasionally get a display added or changed event for a display that was // subsequently removed, which means this returns null. Check this case and bail // out early; if it gets re-attached we'll eventually get another call back for it. return; return null; } DisplayInfo info = new DisplayInfo(); d.getDisplayInfo(info); return info; } private void updateLayoutLimitedFrameRate(int displayId, @Nullable DisplayInfo info) { Vote vote = info != null && info.layoutLimitedRefreshRate != null ? Vote.forPhysicalRefreshRates(info.layoutLimitedRefreshRate.min, info.layoutLimitedRefreshRate.max) : null; mBallotBox.vote(displayId, Vote.PRIORITY_LAYOUT_LIMITED_FRAME_RATE, vote); } private void updateDisplayModes(int displayId, @Nullable DisplayInfo info) { if (info == null) { return; } boolean changed = false; synchronized (mLock) { if (!Arrays.equals(mSupportedModesByDisplay.get(displayId), info.supportedModes)) { Loading