Merge "Extracting VotesStorage to separate class with local locking" into udc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
24dcc93de7
@@ -103,10 +103,6 @@ public class DisplayModeDirector {
|
||||
private static final int MSG_REFRESH_RATE_IN_HBM_SUNLIGHT_CHANGED = 7;
|
||||
private static final int MSG_REFRESH_RATE_IN_HBM_HDR_CHANGED = 8;
|
||||
|
||||
// Special ID used to indicate that given vote is to be applied globally, rather than to a
|
||||
// specific display.
|
||||
private static final int GLOBAL_ID = -1;
|
||||
|
||||
private static final float FLOAT_TOLERANCE = RefreshRateRange.FLOAT_TOLERANCE;
|
||||
|
||||
private final Object mLock = new Object();
|
||||
@@ -129,10 +125,6 @@ public class DisplayModeDirector {
|
||||
@Nullable
|
||||
private DisplayDeviceConfig mDefaultDisplayDeviceConfig;
|
||||
|
||||
// A map from the display ID to the collection of votes and their priority. The latter takes
|
||||
// the form of another map from the priority to the vote itself so that each priority is
|
||||
// guaranteed to have exactly one vote, which is also easily and efficiently replaceable.
|
||||
private SparseArray<SparseArray<Vote>> mVotesByDisplay;
|
||||
// A map from the display ID to the supported modes on that display.
|
||||
private SparseArray<Display.Mode[]> mSupportedModesByDisplay;
|
||||
// A map from the display ID to the default mode of that display.
|
||||
@@ -146,6 +138,8 @@ public class DisplayModeDirector {
|
||||
|
||||
private final boolean mSupportsFrameRateOverride;
|
||||
|
||||
private final VotesStorage mVotesStorage;
|
||||
|
||||
/**
|
||||
* The allowed refresh rate switching type. This is used by SurfaceFlinger.
|
||||
*/
|
||||
@@ -161,7 +155,6 @@ public class DisplayModeDirector {
|
||||
mContext = context;
|
||||
mHandler = new DisplayModeDirectorHandler(handler.getLooper());
|
||||
mInjector = injector;
|
||||
mVotesByDisplay = new SparseArray<>();
|
||||
mSupportedModesByDisplay = new SparseArray<>();
|
||||
mDefaultModeByDisplay = new SparseArray<>();
|
||||
mAppRequestObserver = new AppRequestObserver();
|
||||
@@ -171,15 +164,11 @@ public class DisplayModeDirector {
|
||||
mBrightnessObserver = new BrightnessObserver(context, handler, injector);
|
||||
mDefaultDisplayDeviceConfig = null;
|
||||
mUdfpsObserver = new UdfpsObserver();
|
||||
final BallotBox ballotBox = (displayId, priority, vote) -> {
|
||||
synchronized (mLock) {
|
||||
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(),
|
||||
mVotesStorage = new VotesStorage(this::notifyDesiredDisplayModeSpecsChangedLocked);
|
||||
mDisplayObserver = new DisplayObserver(context, handler, mVotesStorage);
|
||||
mSensorObserver = new SensorObserver(context, mVotesStorage, injector);
|
||||
mSkinThermalStatusObserver = new SkinThermalStatusObserver(injector, mVotesStorage);
|
||||
mHbmObserver = new HbmObserver(injector, mVotesStorage, BackgroundThread.getHandler(),
|
||||
mDeviceConfigDisplaySettings);
|
||||
mAlwaysRespectAppRequest = false;
|
||||
mSupportsFrameRateOverride = injector.supportsFrameRateOverride();
|
||||
@@ -226,29 +215,7 @@ public class DisplayModeDirector {
|
||||
mLoggingEnabled = loggingEnabled;
|
||||
mBrightnessObserver.setLoggingEnabled(loggingEnabled);
|
||||
mSkinThermalStatusObserver.setLoggingEnabled(loggingEnabled);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private SparseArray<Vote> getVotesLocked(int displayId) {
|
||||
SparseArray<Vote> displayVotes = mVotesByDisplay.get(displayId);
|
||||
final SparseArray<Vote> votes;
|
||||
if (displayVotes != null) {
|
||||
votes = displayVotes.clone();
|
||||
} else {
|
||||
votes = new SparseArray<>();
|
||||
}
|
||||
|
||||
SparseArray<Vote> globalVotes = mVotesByDisplay.get(GLOBAL_ID);
|
||||
if (globalVotes != null) {
|
||||
for (int i = 0; i < globalVotes.size(); i++) {
|
||||
int priority = globalVotes.keyAt(i);
|
||||
if (votes.indexOfKey(priority) < 0) {
|
||||
votes.put(priority, globalVotes.valueAt(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return votes;
|
||||
mVotesStorage.setLoggingEnabled(loggingEnabled);
|
||||
}
|
||||
|
||||
private static final class VoteSummary {
|
||||
@@ -407,7 +374,7 @@ public class DisplayModeDirector {
|
||||
@NonNull
|
||||
public DesiredDisplayModeSpecs getDesiredDisplayModeSpecs(int displayId) {
|
||||
synchronized (mLock) {
|
||||
SparseArray<Vote> votes = getVotesLocked(displayId);
|
||||
SparseArray<Vote> votes = mVotesStorage.getVotes(displayId);
|
||||
Display.Mode[] modes = mSupportedModesByDisplay.get(displayId);
|
||||
Display.Mode defaultMode = mDefaultModeByDisplay.get(displayId);
|
||||
if (modes == null || defaultMode == null) {
|
||||
@@ -780,10 +747,8 @@ public class DisplayModeDirector {
|
||||
@VisibleForTesting
|
||||
@Nullable
|
||||
Vote getVote(int displayId, int priority) {
|
||||
synchronized (mLock) {
|
||||
SparseArray<Vote> votes = getVotesLocked(displayId);
|
||||
return votes.get(priority);
|
||||
}
|
||||
SparseArray<Vote> votes = mVotesStorage.getVotes(displayId);
|
||||
return votes.get(priority);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -806,18 +771,6 @@ public class DisplayModeDirector {
|
||||
final Display.Mode mode = mDefaultModeByDisplay.valueAt(i);
|
||||
pw.println(" " + id + " -> " + mode);
|
||||
}
|
||||
pw.println(" mVotesByDisplay:");
|
||||
for (int i = 0; i < mVotesByDisplay.size(); i++) {
|
||||
pw.println(" " + mVotesByDisplay.keyAt(i) + ":");
|
||||
SparseArray<Vote> votes = mVotesByDisplay.valueAt(i);
|
||||
for (int p = Vote.MAX_PRIORITY; p >= Vote.MIN_PRIORITY; p--) {
|
||||
Vote vote = votes.get(p);
|
||||
if (vote == null) {
|
||||
continue;
|
||||
}
|
||||
pw.println(" " + Vote.priorityToString(p) + " -> " + vote);
|
||||
}
|
||||
}
|
||||
pw.println(" mModeSwitchingType: " + switchingTypeToString(mModeSwitchingType));
|
||||
pw.println(" mAlwaysRespectAppRequest: " + mAlwaysRespectAppRequest);
|
||||
mSettingsObserver.dumpLocked(pw);
|
||||
@@ -827,44 +780,10 @@ public class DisplayModeDirector {
|
||||
mHbmObserver.dumpLocked(pw);
|
||||
mSkinThermalStatusObserver.dumpLocked(pw);
|
||||
}
|
||||
|
||||
mVotesStorage.dump(pw);
|
||||
mSensorObserver.dump(pw);
|
||||
}
|
||||
|
||||
private void updateVoteLocked(int priority, Vote vote) {
|
||||
updateVoteLocked(GLOBAL_ID, priority, vote);
|
||||
}
|
||||
|
||||
private void updateVoteLocked(int displayId, int priority, Vote vote) {
|
||||
if (mLoggingEnabled) {
|
||||
Slog.i(TAG, "updateVoteLocked(displayId=" + displayId
|
||||
+ ", priority=" + Vote.priorityToString(priority)
|
||||
+ ", vote=" + vote + ")");
|
||||
}
|
||||
if (priority < Vote.MIN_PRIORITY || priority > Vote.MAX_PRIORITY) {
|
||||
Slog.w(TAG, "Received a vote with an invalid priority, ignoring:"
|
||||
+ " priority=" + Vote.priorityToString(priority)
|
||||
+ ", vote=" + vote, new Throwable());
|
||||
return;
|
||||
}
|
||||
final SparseArray<Vote> votes = getOrCreateVotesByDisplay(displayId);
|
||||
|
||||
if (vote != null) {
|
||||
votes.put(priority, vote);
|
||||
} else {
|
||||
votes.remove(priority);
|
||||
}
|
||||
|
||||
if (votes.size() == 0) {
|
||||
if (mLoggingEnabled) {
|
||||
Slog.i(TAG, "No votes left for display " + displayId + ", removing.");
|
||||
}
|
||||
mVotesByDisplay.remove(displayId);
|
||||
}
|
||||
|
||||
notifyDesiredDisplayModeSpecsChangedLocked();
|
||||
}
|
||||
|
||||
@GuardedBy("mLock")
|
||||
private float getMaxRefreshRateLocked(int displayId) {
|
||||
Display.Mode[] modes = mSupportedModesByDisplay.get(displayId);
|
||||
@@ -890,16 +809,6 @@ public class DisplayModeDirector {
|
||||
}
|
||||
}
|
||||
|
||||
private SparseArray<Vote> getOrCreateVotesByDisplay(int displayId) {
|
||||
if (mVotesByDisplay.indexOfKey(displayId) >= 0) {
|
||||
return mVotesByDisplay.get(displayId);
|
||||
} else {
|
||||
SparseArray<Vote> votes = new SparseArray<>();
|
||||
mVotesByDisplay.put(displayId, votes);
|
||||
return votes;
|
||||
}
|
||||
}
|
||||
|
||||
private static String switchingTypeToString(@DisplayManager.SwitchingType int type) {
|
||||
switch (type) {
|
||||
case DisplayManager.SWITCHING_TYPE_NONE:
|
||||
@@ -927,7 +836,7 @@ public class DisplayModeDirector {
|
||||
|
||||
@VisibleForTesting
|
||||
void injectVotesByDisplay(SparseArray<SparseArray<Vote>> votesByDisplay) {
|
||||
mVotesByDisplay = votesByDisplay;
|
||||
mVotesStorage.injectVotesByDisplay(votesByDisplay);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
@@ -1156,225 +1065,6 @@ public class DisplayModeDirector {
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
static final class Vote {
|
||||
// DEFAULT_RENDER_FRAME_RATE votes for render frame rate [0, DEFAULT]. As the lowest
|
||||
// priority vote, it's overridden by all other considerations. It acts to set a default
|
||||
// frame rate for a device.
|
||||
public static final int PRIORITY_DEFAULT_RENDER_FRAME_RATE = 0;
|
||||
|
||||
// PRIORITY_FLICKER_REFRESH_RATE votes for a single refresh rate like [60,60], [90,90] or
|
||||
// null. It is used to set a preferred refresh rate value in case the higher priority votes
|
||||
// result is a range.
|
||||
public static final int PRIORITY_FLICKER_REFRESH_RATE = 1;
|
||||
|
||||
// High-brightness-mode may need a specific range of refresh-rates to function properly.
|
||||
public static final int PRIORITY_HIGH_BRIGHTNESS_MODE = 2;
|
||||
|
||||
// SETTING_MIN_RENDER_FRAME_RATE is used to propose a lower bound of the render frame rate.
|
||||
// It votes [minRefreshRate, Float.POSITIVE_INFINITY]
|
||||
public static final int PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE = 3;
|
||||
|
||||
// APP_REQUEST_RENDER_FRAME_RATE_RANGE is used to for internal apps to limit the render
|
||||
// frame rate in certain cases, mostly to preserve power.
|
||||
// @see android.view.WindowManager.LayoutParams#preferredMinRefreshRate
|
||||
// @see android.view.WindowManager.LayoutParams#preferredMaxRefreshRate
|
||||
// It votes to [preferredMinRefreshRate, preferredMaxRefreshRate].
|
||||
public static final int PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE = 4;
|
||||
|
||||
// We split the app request into different priorities in case we can satisfy one desire
|
||||
// without the other.
|
||||
|
||||
// Application can specify preferred refresh rate with below attrs.
|
||||
// @see android.view.WindowManager.LayoutParams#preferredRefreshRate
|
||||
// @see android.view.WindowManager.LayoutParams#preferredDisplayModeId
|
||||
//
|
||||
// When the app specifies a LayoutParams#preferredDisplayModeId, in addition to the
|
||||
// refresh rate, it also chooses a preferred size (resolution) as part of the selected
|
||||
// mode id. The app preference is then translated to APP_REQUEST_BASE_MODE_REFRESH_RATE and
|
||||
// optionally to APP_REQUEST_SIZE as well, if a mode id was selected.
|
||||
// The system also forces some apps like denylisted app to run at a lower refresh rate.
|
||||
// @see android.R.array#config_highRefreshRateBlacklist
|
||||
//
|
||||
// When summarizing the votes and filtering the allowed display modes, these votes determine
|
||||
// which mode id should be the base mode id to be sent to SurfaceFlinger:
|
||||
// - APP_REQUEST_BASE_MODE_REFRESH_RATE is used to validate the vote summary. If a summary
|
||||
// includes a base mode refresh rate, but it is not in the refresh rate range, then the
|
||||
// summary is considered invalid so we could drop a lower priority vote and try again.
|
||||
// - APP_REQUEST_SIZE is used to filter out display modes of a different size.
|
||||
//
|
||||
// The preferred refresh rate is set on the main surface of the app outside of
|
||||
// DisplayModeDirector.
|
||||
// @see com.android.server.wm.WindowState#updateFrameRateSelectionPriorityIfNeeded
|
||||
public static final int PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE = 5;
|
||||
public static final int PRIORITY_APP_REQUEST_SIZE = 6;
|
||||
|
||||
// SETTING_PEAK_RENDER_FRAME_RATE has a high priority and will restrict the bounds of the
|
||||
// rest of low priority voters. It votes [0, max(PEAK, MIN)]
|
||||
public static final int PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE = 7;
|
||||
|
||||
// To avoid delay in switching between 60HZ -> 90HZ when activating LHBM, set refresh
|
||||
// rate to max value (same as for PRIORITY_UDFPS) on lock screen
|
||||
public static final int PRIORITY_AUTH_OPTIMIZER_RENDER_FRAME_RATE = 8;
|
||||
|
||||
// For concurrent displays we want to limit refresh rate on all displays
|
||||
public static final int PRIORITY_LAYOUT_LIMITED_FRAME_RATE = 9;
|
||||
|
||||
// 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 = 10;
|
||||
|
||||
// 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 = 11;
|
||||
|
||||
// Force display to [0, 60HZ] if skin temperature is at or above CRITICAL.
|
||||
public static final int PRIORITY_SKIN_TEMPERATURE = 12;
|
||||
|
||||
// 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 = 13;
|
||||
|
||||
// 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 = 14;
|
||||
|
||||
// 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.
|
||||
|
||||
public static final int MIN_PRIORITY = PRIORITY_DEFAULT_RENDER_FRAME_RATE;
|
||||
public static final int MAX_PRIORITY = PRIORITY_UDFPS;
|
||||
|
||||
// The cutoff for the app request refresh rate range. Votes with priorities lower than this
|
||||
// value will not be considered when constructing the app request refresh rate range.
|
||||
public static final int APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF =
|
||||
PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE;
|
||||
|
||||
/**
|
||||
* A value signifying an invalid width or height in a vote.
|
||||
*/
|
||||
public static final int INVALID_SIZE = -1;
|
||||
|
||||
/**
|
||||
* The requested width of the display in pixels, or INVALID_SIZE;
|
||||
*/
|
||||
public final int width;
|
||||
/**
|
||||
* The requested height of the display in pixels, or INVALID_SIZE;
|
||||
*/
|
||||
public final int height;
|
||||
/**
|
||||
* Information about the refresh rate frame rate ranges DM would like to set the display to.
|
||||
*/
|
||||
public final RefreshRateRanges refreshRateRanges;
|
||||
|
||||
/**
|
||||
* Whether refresh rate switching should be disabled (i.e. the refresh rate range is
|
||||
* a single value).
|
||||
*/
|
||||
public final boolean disableRefreshRateSwitching;
|
||||
|
||||
/**
|
||||
* The preferred refresh rate selected by the app. It is used to validate that the summary
|
||||
* refresh rate ranges include this value, and are not restricted by a lower priority vote.
|
||||
*/
|
||||
public final float appRequestBaseModeRefreshRate;
|
||||
|
||||
public static Vote forPhysicalRefreshRates(float minRefreshRate, float maxRefreshRate) {
|
||||
return new Vote(INVALID_SIZE, INVALID_SIZE, minRefreshRate, maxRefreshRate, 0,
|
||||
Float.POSITIVE_INFINITY,
|
||||
minRefreshRate == maxRefreshRate, 0f);
|
||||
}
|
||||
|
||||
public static Vote forRenderFrameRates(float minFrameRate, float maxFrameRate) {
|
||||
return new Vote(INVALID_SIZE, INVALID_SIZE, 0, Float.POSITIVE_INFINITY, minFrameRate,
|
||||
maxFrameRate,
|
||||
false, 0f);
|
||||
}
|
||||
|
||||
public static Vote forSize(int width, int height) {
|
||||
return new Vote(width, height, 0, Float.POSITIVE_INFINITY, 0, Float.POSITIVE_INFINITY,
|
||||
false,
|
||||
0f);
|
||||
}
|
||||
|
||||
public static Vote forDisableRefreshRateSwitching() {
|
||||
return new Vote(INVALID_SIZE, INVALID_SIZE, 0, Float.POSITIVE_INFINITY, 0,
|
||||
Float.POSITIVE_INFINITY, true,
|
||||
0f);
|
||||
}
|
||||
|
||||
public static Vote forBaseModeRefreshRate(float baseModeRefreshRate) {
|
||||
return new Vote(INVALID_SIZE, INVALID_SIZE, 0, Float.POSITIVE_INFINITY, 0,
|
||||
Float.POSITIVE_INFINITY, false,
|
||||
baseModeRefreshRate);
|
||||
}
|
||||
|
||||
private Vote(int width, int height,
|
||||
float minPhysicalRefreshRate,
|
||||
float maxPhysicalRefreshRate,
|
||||
float minRenderFrameRate,
|
||||
float maxRenderFrameRate,
|
||||
boolean disableRefreshRateSwitching,
|
||||
float baseModeRefreshRate) {
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.refreshRateRanges = new RefreshRateRanges(
|
||||
new RefreshRateRange(minPhysicalRefreshRate, maxPhysicalRefreshRate),
|
||||
new RefreshRateRange(minRenderFrameRate, maxRenderFrameRate));
|
||||
this.disableRefreshRateSwitching = disableRefreshRateSwitching;
|
||||
this.appRequestBaseModeRefreshRate = baseModeRefreshRate;
|
||||
}
|
||||
|
||||
public static String priorityToString(int priority) {
|
||||
switch (priority) {
|
||||
case PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE:
|
||||
return "PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE";
|
||||
case PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE:
|
||||
return "PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE";
|
||||
case PRIORITY_APP_REQUEST_SIZE:
|
||||
return "PRIORITY_APP_REQUEST_SIZE";
|
||||
case PRIORITY_DEFAULT_RENDER_FRAME_RATE:
|
||||
return "PRIORITY_DEFAULT_REFRESH_RATE";
|
||||
case PRIORITY_FLICKER_REFRESH_RATE:
|
||||
return "PRIORITY_FLICKER_REFRESH_RATE";
|
||||
case PRIORITY_FLICKER_REFRESH_RATE_SWITCH:
|
||||
return "PRIORITY_FLICKER_REFRESH_RATE_SWITCH";
|
||||
case PRIORITY_HIGH_BRIGHTNESS_MODE:
|
||||
return "PRIORITY_HIGH_BRIGHTNESS_MODE";
|
||||
case PRIORITY_PROXIMITY:
|
||||
return "PRIORITY_PROXIMITY";
|
||||
case PRIORITY_LOW_POWER_MODE:
|
||||
return "PRIORITY_LOW_POWER_MODE";
|
||||
case PRIORITY_SKIN_TEMPERATURE:
|
||||
return "PRIORITY_SKIN_TEMPERATURE";
|
||||
case PRIORITY_UDFPS:
|
||||
return "PRIORITY_UDFPS";
|
||||
case PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE:
|
||||
return "PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE";
|
||||
case PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE:
|
||||
return "PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE";
|
||||
case PRIORITY_AUTH_OPTIMIZER_RENDER_FRAME_RATE:
|
||||
return "PRIORITY_AUTH_OPTIMIZER_RENDER_FRAME_RATE";
|
||||
case PRIORITY_LAYOUT_LIMITED_FRAME_RATE:
|
||||
return "PRIORITY_LAYOUT_LIMITED_FRAME_RATE";
|
||||
default:
|
||||
return Integer.toString(priority);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Vote{"
|
||||
+ "width=" + width + ", height=" + height
|
||||
+ ", refreshRateRanges=" + refreshRateRanges
|
||||
+ ", disableRefreshRateSwitching=" + disableRefreshRateSwitching
|
||||
+ ", appRequestBaseModeRefreshRate=" + appRequestBaseModeRefreshRate + "}";
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
final class SettingsObserver extends ContentObserver {
|
||||
private final Uri mSmoothDisplaySetting =
|
||||
@@ -1510,7 +1200,7 @@ public class DisplayModeDirector {
|
||||
} else {
|
||||
vote = null;
|
||||
}
|
||||
updateVoteLocked(Vote.PRIORITY_LOW_POWER_MODE, vote);
|
||||
mVotesStorage.updateGlobalVote(Vote.PRIORITY_LOW_POWER_MODE, vote);
|
||||
mBrightnessObserver.onLowPowerModeEnabledLocked(inLowPowerMode);
|
||||
}
|
||||
|
||||
@@ -1529,13 +1219,14 @@ public class DisplayModeDirector {
|
||||
Vote peakVote = peakRefreshRate == 0f
|
||||
? null
|
||||
: Vote.forRenderFrameRates(0f, Math.max(minRefreshRate, peakRefreshRate));
|
||||
updateVoteLocked(Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE, peakVote);
|
||||
updateVoteLocked(Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE,
|
||||
mVotesStorage.updateGlobalVote(Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE,
|
||||
peakVote);
|
||||
mVotesStorage.updateGlobalVote(Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE,
|
||||
Vote.forRenderFrameRates(minRefreshRate, Float.POSITIVE_INFINITY));
|
||||
Vote defaultVote =
|
||||
defaultRefreshRate == 0f
|
||||
? null : Vote.forRenderFrameRates(0f, defaultRefreshRate);
|
||||
updateVoteLocked(Vote.PRIORITY_DEFAULT_RENDER_FRAME_RATE, defaultVote);
|
||||
mVotesStorage.updateGlobalVote(Vote.PRIORITY_DEFAULT_RENDER_FRAME_RATE, defaultVote);
|
||||
|
||||
float maxRefreshRate;
|
||||
if (peakRefreshRate == 0f && defaultRefreshRate == 0f) {
|
||||
@@ -1619,9 +1310,9 @@ public class DisplayModeDirector {
|
||||
sizeVote = null;
|
||||
}
|
||||
|
||||
updateVoteLocked(displayId, Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
|
||||
mVotesStorage.updateVote(displayId, Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
|
||||
baseModeRefreshRateVote);
|
||||
updateVoteLocked(displayId, Vote.PRIORITY_APP_REQUEST_SIZE, sizeVote);
|
||||
mVotesStorage.updateVote(displayId, Vote.PRIORITY_APP_REQUEST_SIZE, sizeVote);
|
||||
}
|
||||
|
||||
private void setAppPreferredRefreshRateRangeLocked(int displayId,
|
||||
@@ -1652,11 +1343,8 @@ public class DisplayModeDirector {
|
||||
mAppPreferredRefreshRateRangeByDisplay.remove(displayId);
|
||||
vote = null;
|
||||
}
|
||||
synchronized (mLock) {
|
||||
updateVoteLocked(displayId,
|
||||
Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE,
|
||||
vote);
|
||||
}
|
||||
mVotesStorage.updateVote(displayId, Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE,
|
||||
vote);
|
||||
}
|
||||
|
||||
private Display.Mode findModeByIdLocked(int displayId, int modeId) {
|
||||
@@ -1696,12 +1384,12 @@ public class DisplayModeDirector {
|
||||
// calling into us already holding its own lock.
|
||||
private final Context mContext;
|
||||
private final Handler mHandler;
|
||||
private final BallotBox mBallotBox;
|
||||
private final VotesStorage mVotesStorage;
|
||||
|
||||
DisplayObserver(Context context, Handler handler, BallotBox ballotBox) {
|
||||
DisplayObserver(Context context, Handler handler, VotesStorage votesStorage) {
|
||||
mContext = context;
|
||||
mHandler = handler;
|
||||
mBallotBox = ballotBox;
|
||||
mVotesStorage = votesStorage;
|
||||
}
|
||||
|
||||
public void observe() {
|
||||
@@ -1761,7 +1449,7 @@ public class DisplayModeDirector {
|
||||
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);
|
||||
mVotesStorage.updateVote(displayId, Vote.PRIORITY_LAYOUT_LIMITED_FRAME_RATE, vote);
|
||||
}
|
||||
|
||||
private void updateDisplayModes(int displayId, @Nullable DisplayInfo info) {
|
||||
@@ -2083,8 +1771,8 @@ public class DisplayModeDirector {
|
||||
updateSensorStatus();
|
||||
if (!changeable) {
|
||||
// Revoke previous vote from BrightnessObserver
|
||||
updateVoteLocked(Vote.PRIORITY_FLICKER_REFRESH_RATE, null);
|
||||
updateVoteLocked(Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH, null);
|
||||
mVotesStorage.updateGlobalVote(Vote.PRIORITY_FLICKER_REFRESH_RATE, null);
|
||||
mVotesStorage.updateGlobalVote(Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2409,8 +2097,9 @@ public class DisplayModeDirector {
|
||||
Slog.d(TAG, "Display brightness " + mBrightness + ", ambient lux " + mAmbientLux
|
||||
+ ", Vote " + refreshRateVote);
|
||||
}
|
||||
updateVoteLocked(Vote.PRIORITY_FLICKER_REFRESH_RATE, refreshRateVote);
|
||||
updateVoteLocked(Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH, refreshRateSwitchingVote);
|
||||
mVotesStorage.updateGlobalVote(Vote.PRIORITY_FLICKER_REFRESH_RATE, refreshRateVote);
|
||||
mVotesStorage.updateGlobalVote(Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH,
|
||||
refreshRateSwitchingVote);
|
||||
}
|
||||
|
||||
private boolean hasValidLowZone() {
|
||||
@@ -2678,7 +2367,7 @@ public class DisplayModeDirector {
|
||||
} else {
|
||||
vote = null;
|
||||
}
|
||||
DisplayModeDirector.this.updateVoteLocked(displayId, votePriority, vote);
|
||||
mVotesStorage.updateVote(displayId, votePriority, vote);
|
||||
}
|
||||
|
||||
void dumpLocked(PrintWriter pw) {
|
||||
@@ -2704,7 +2393,7 @@ public class DisplayModeDirector {
|
||||
private final String mProximitySensorName = null;
|
||||
private final String mProximitySensorType = Sensor.STRING_TYPE_PROXIMITY;
|
||||
|
||||
private final BallotBox mBallotBox;
|
||||
private final VotesStorage mVotesStorage;
|
||||
private final Context mContext;
|
||||
private final Injector mInjector;
|
||||
@GuardedBy("mSensorObserverLock")
|
||||
@@ -2716,9 +2405,9 @@ public class DisplayModeDirector {
|
||||
@GuardedBy("mSensorObserverLock")
|
||||
private boolean mIsProxActive = false;
|
||||
|
||||
SensorObserver(Context context, BallotBox ballotBox, Injector injector) {
|
||||
SensorObserver(Context context, VotesStorage votesStorage, Injector injector) {
|
||||
mContext = context;
|
||||
mBallotBox = ballotBox;
|
||||
mVotesStorage = votesStorage;
|
||||
mInjector = injector;
|
||||
}
|
||||
|
||||
@@ -2764,7 +2453,7 @@ public class DisplayModeDirector {
|
||||
vote = Vote.forPhysicalRefreshRates(rate.min, rate.max);
|
||||
}
|
||||
}
|
||||
mBallotBox.vote(displayId, Vote.PRIORITY_PROXIMITY, vote);
|
||||
mVotesStorage.updateVote(displayId, Vote.PRIORITY_PROXIMITY, vote);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2817,7 +2506,7 @@ public class DisplayModeDirector {
|
||||
* DisplayManagerInternal but originate in the display-device-config file.
|
||||
*/
|
||||
public class HbmObserver implements DisplayManager.DisplayListener {
|
||||
private final BallotBox mBallotBox;
|
||||
private final VotesStorage mVotesStorage;
|
||||
private final Handler mHandler;
|
||||
private final SparseIntArray mHbmMode = new SparseIntArray();
|
||||
private final SparseBooleanArray mHbmActive = new SparseBooleanArray();
|
||||
@@ -2828,10 +2517,10 @@ public class DisplayModeDirector {
|
||||
|
||||
private DisplayManagerInternal mDisplayManagerInternal;
|
||||
|
||||
HbmObserver(Injector injector, BallotBox ballotBox, Handler handler,
|
||||
HbmObserver(Injector injector, VotesStorage votesStorage, Handler handler,
|
||||
DeviceConfigDisplaySettings displaySettings) {
|
||||
mInjector = injector;
|
||||
mBallotBox = ballotBox;
|
||||
mVotesStorage = votesStorage;
|
||||
mHandler = handler;
|
||||
mDeviceConfigDisplaySettings = displaySettings;
|
||||
}
|
||||
@@ -2901,7 +2590,7 @@ public class DisplayModeDirector {
|
||||
|
||||
@Override
|
||||
public void onDisplayRemoved(int displayId) {
|
||||
mBallotBox.vote(displayId, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE, null);
|
||||
mVotesStorage.updateVote(displayId, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE, null);
|
||||
mHbmMode.delete(displayId);
|
||||
mHbmActive.delete(displayId);
|
||||
}
|
||||
@@ -2968,7 +2657,7 @@ public class DisplayModeDirector {
|
||||
}
|
||||
|
||||
}
|
||||
mBallotBox.vote(displayId, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE, vote);
|
||||
mVotesStorage.updateVote(displayId, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE, vote);
|
||||
}
|
||||
|
||||
void dumpLocked(PrintWriter pw) {
|
||||
@@ -3299,8 +2988,4 @@ public class DisplayModeDirector {
|
||||
ServiceManager.getService(Context.THERMAL_SERVICE));
|
||||
}
|
||||
}
|
||||
|
||||
interface BallotBox {
|
||||
void vote(int displayId, int priority, Vote vote);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ final class SkinThermalStatusObserver extends IThermalEventListener.Stub impleme
|
||||
DisplayManager.DisplayListener {
|
||||
private static final String TAG = "SkinThermalStatusObserver";
|
||||
|
||||
private final DisplayModeDirector.BallotBox mBallotBox;
|
||||
private final VotesStorage mVotesStorage;
|
||||
private final DisplayModeDirector.Injector mInjector;
|
||||
|
||||
private boolean mLoggingEnabled;
|
||||
@@ -52,15 +52,15 @@ final class SkinThermalStatusObserver extends IThermalEventListener.Stub impleme
|
||||
mThermalThrottlingByDisplay = new SparseArray<>();
|
||||
|
||||
SkinThermalStatusObserver(DisplayModeDirector.Injector injector,
|
||||
DisplayModeDirector.BallotBox ballotBox) {
|
||||
this(injector, ballotBox, BackgroundThread.getHandler());
|
||||
VotesStorage votesStorage) {
|
||||
this(injector, votesStorage, BackgroundThread.getHandler());
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
SkinThermalStatusObserver(DisplayModeDirector.Injector injector,
|
||||
DisplayModeDirector.BallotBox ballotBox, Handler handler) {
|
||||
VotesStorage votesStorage, Handler handler) {
|
||||
mInjector = injector;
|
||||
mBallotBox = ballotBox;
|
||||
mVotesStorage = votesStorage;
|
||||
mHandler = handler;
|
||||
}
|
||||
|
||||
@@ -112,8 +112,8 @@ final class SkinThermalStatusObserver extends IThermalEventListener.Stub impleme
|
||||
public void onDisplayRemoved(int displayId) {
|
||||
synchronized (mThermalObserverLock) {
|
||||
mThermalThrottlingByDisplay.remove(displayId);
|
||||
mHandler.post(() -> mBallotBox.vote(displayId,
|
||||
DisplayModeDirector.Vote.PRIORITY_SKIN_TEMPERATURE, null));
|
||||
mHandler.post(() -> mVotesStorage.updateVote(displayId,
|
||||
Vote.PRIORITY_SKIN_TEMPERATURE, null));
|
||||
}
|
||||
if (mLoggingEnabled) {
|
||||
Slog.d(TAG, "Display removed and voted: displayId=" + displayId);
|
||||
@@ -218,11 +218,11 @@ final class SkinThermalStatusObserver extends IThermalEventListener.Stub impleme
|
||||
SurfaceControl.RefreshRateRange foundRange = findBestMatchingRefreshRateRange(currentStatus,
|
||||
throttlingMap);
|
||||
// if status <= currentStatus not found in the map reset vote
|
||||
DisplayModeDirector.Vote vote = null;
|
||||
Vote vote = null;
|
||||
if (foundRange != null) { // otherwise vote with found range
|
||||
vote = DisplayModeDirector.Vote.forRenderFrameRates(foundRange.min, foundRange.max);
|
||||
vote = Vote.forRenderFrameRates(foundRange.min, foundRange.max);
|
||||
}
|
||||
mBallotBox.vote(displayId, DisplayModeDirector.Vote.PRIORITY_SKIN_TEMPERATURE, vote);
|
||||
mVotesStorage.updateVote(displayId, Vote.PRIORITY_SKIN_TEMPERATURE, vote);
|
||||
if (mLoggingEnabled) {
|
||||
Slog.d(TAG, "Voted: vote=" + vote + ", display =" + displayId);
|
||||
}
|
||||
@@ -244,11 +244,11 @@ final class SkinThermalStatusObserver extends IThermalEventListener.Stub impleme
|
||||
|
||||
private void fallbackReportThrottlingIfNeeded(int displayId,
|
||||
@Temperature.ThrottlingStatus int currentStatus) {
|
||||
DisplayModeDirector.Vote vote = null;
|
||||
Vote vote = null;
|
||||
if (currentStatus >= Temperature.THROTTLING_CRITICAL) {
|
||||
vote = DisplayModeDirector.Vote.forRenderFrameRates(0f, 60f);
|
||||
vote = Vote.forRenderFrameRates(0f, 60f);
|
||||
}
|
||||
mBallotBox.vote(displayId, DisplayModeDirector.Vote.PRIORITY_SKIN_TEMPERATURE, vote);
|
||||
mVotesStorage.updateVote(displayId, Vote.PRIORITY_SKIN_TEMPERATURE, vote);
|
||||
if (mLoggingEnabled) {
|
||||
Slog.d(TAG, "Voted(fallback): vote=" + vote + ", display =" + displayId);
|
||||
}
|
||||
|
||||
237
services/core/java/com/android/server/display/mode/Vote.java
Normal file
237
services/core/java/com/android/server/display/mode/Vote.java
Normal file
@@ -0,0 +1,237 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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.server.display.mode;
|
||||
|
||||
import android.view.SurfaceControl;
|
||||
|
||||
final class Vote {
|
||||
// DEFAULT_RENDER_FRAME_RATE votes for render frame rate [0, DEFAULT]. As the lowest
|
||||
// priority vote, it's overridden by all other considerations. It acts to set a default
|
||||
// frame rate for a device.
|
||||
static final int PRIORITY_DEFAULT_RENDER_FRAME_RATE = 0;
|
||||
|
||||
// PRIORITY_FLICKER_REFRESH_RATE votes for a single refresh rate like [60,60], [90,90] or
|
||||
// null. It is used to set a preferred refresh rate value in case the higher priority votes
|
||||
// result is a range.
|
||||
static final int PRIORITY_FLICKER_REFRESH_RATE = 1;
|
||||
|
||||
// High-brightness-mode may need a specific range of refresh-rates to function properly.
|
||||
static final int PRIORITY_HIGH_BRIGHTNESS_MODE = 2;
|
||||
|
||||
// SETTING_MIN_RENDER_FRAME_RATE is used to propose a lower bound of the render frame rate.
|
||||
// It votes [minRefreshRate, Float.POSITIVE_INFINITY]
|
||||
static final int PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE = 3;
|
||||
|
||||
// APP_REQUEST_RENDER_FRAME_RATE_RANGE is used to for internal apps to limit the render
|
||||
// frame rate in certain cases, mostly to preserve power.
|
||||
// @see android.view.WindowManager.LayoutParams#preferredMinRefreshRate
|
||||
// @see android.view.WindowManager.LayoutParams#preferredMaxRefreshRate
|
||||
// It votes to [preferredMinRefreshRate, preferredMaxRefreshRate].
|
||||
static final int PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE = 4;
|
||||
|
||||
// We split the app request into different priorities in case we can satisfy one desire
|
||||
// without the other.
|
||||
|
||||
// Application can specify preferred refresh rate with below attrs.
|
||||
// @see android.view.WindowManager.LayoutParams#preferredRefreshRate
|
||||
// @see android.view.WindowManager.LayoutParams#preferredDisplayModeId
|
||||
//
|
||||
// When the app specifies a LayoutParams#preferredDisplayModeId, in addition to the
|
||||
// refresh rate, it also chooses a preferred size (resolution) as part of the selected
|
||||
// mode id. The app preference is then translated to APP_REQUEST_BASE_MODE_REFRESH_RATE and
|
||||
// optionally to APP_REQUEST_SIZE as well, if a mode id was selected.
|
||||
// The system also forces some apps like denylisted app to run at a lower refresh rate.
|
||||
// @see android.R.array#config_highRefreshRateBlacklist
|
||||
//
|
||||
// When summarizing the votes and filtering the allowed display modes, these votes determine
|
||||
// which mode id should be the base mode id to be sent to SurfaceFlinger:
|
||||
// - APP_REQUEST_BASE_MODE_REFRESH_RATE is used to validate the vote summary. If a summary
|
||||
// includes a base mode refresh rate, but it is not in the refresh rate range, then the
|
||||
// summary is considered invalid so we could drop a lower priority vote and try again.
|
||||
// - APP_REQUEST_SIZE is used to filter out display modes of a different size.
|
||||
//
|
||||
// The preferred refresh rate is set on the main surface of the app outside of
|
||||
// DisplayModeDirector.
|
||||
// @see com.android.server.wm.WindowState#updateFrameRateSelectionPriorityIfNeeded
|
||||
static final int PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE = 5;
|
||||
static final int PRIORITY_APP_REQUEST_SIZE = 6;
|
||||
|
||||
// SETTING_PEAK_RENDER_FRAME_RATE has a high priority and will restrict the bounds of the
|
||||
// rest of low priority voters. It votes [0, max(PEAK, MIN)]
|
||||
static final int PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE = 7;
|
||||
|
||||
// To avoid delay in switching between 60HZ -> 90HZ when activating LHBM, set refresh
|
||||
// rate to max value (same as for PRIORITY_UDFPS) on lock screen
|
||||
static final int PRIORITY_AUTH_OPTIMIZER_RENDER_FRAME_RATE = 8;
|
||||
|
||||
// For concurrent displays we want to limit refresh rate on all displays
|
||||
static final int PRIORITY_LAYOUT_LIMITED_FRAME_RATE = 9;
|
||||
|
||||
// LOW_POWER_MODE force the render frame rate to [0, 60HZ] if
|
||||
// Settings.Global.LOW_POWER_MODE is on.
|
||||
static final int PRIORITY_LOW_POWER_MODE = 10;
|
||||
|
||||
// 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.
|
||||
static final int PRIORITY_FLICKER_REFRESH_RATE_SWITCH = 11;
|
||||
|
||||
// Force display to [0, 60HZ] if skin temperature is at or above CRITICAL.
|
||||
static final int PRIORITY_SKIN_TEMPERATURE = 12;
|
||||
|
||||
// The proximity sensor needs the refresh rate to be locked in order to function, so this is
|
||||
// set to a high priority.
|
||||
static final int PRIORITY_PROXIMITY = 13;
|
||||
|
||||
// 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.
|
||||
static final int PRIORITY_UDFPS = 14;
|
||||
|
||||
// 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.
|
||||
|
||||
static final int MIN_PRIORITY = PRIORITY_DEFAULT_RENDER_FRAME_RATE;
|
||||
static final int MAX_PRIORITY = PRIORITY_UDFPS;
|
||||
|
||||
// The cutoff for the app request refresh rate range. Votes with priorities lower than this
|
||||
// value will not be considered when constructing the app request refresh rate range.
|
||||
static final int APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF =
|
||||
PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE;
|
||||
|
||||
/**
|
||||
* A value signifying an invalid width or height in a vote.
|
||||
*/
|
||||
static final int INVALID_SIZE = -1;
|
||||
|
||||
/**
|
||||
* The requested width of the display in pixels, or INVALID_SIZE;
|
||||
*/
|
||||
public final int width;
|
||||
/**
|
||||
* The requested height of the display in pixels, or INVALID_SIZE;
|
||||
*/
|
||||
public final int height;
|
||||
/**
|
||||
* Information about the refresh rate frame rate ranges DM would like to set the display to.
|
||||
*/
|
||||
public final SurfaceControl.RefreshRateRanges refreshRateRanges;
|
||||
|
||||
/**
|
||||
* Whether refresh rate switching should be disabled (i.e. the refresh rate range is
|
||||
* a single value).
|
||||
*/
|
||||
public final boolean disableRefreshRateSwitching;
|
||||
|
||||
/**
|
||||
* The preferred refresh rate selected by the app. It is used to validate that the summary
|
||||
* refresh rate ranges include this value, and are not restricted by a lower priority vote.
|
||||
*/
|
||||
public final float appRequestBaseModeRefreshRate;
|
||||
|
||||
static Vote forPhysicalRefreshRates(float minRefreshRate, float maxRefreshRate) {
|
||||
return new Vote(INVALID_SIZE, INVALID_SIZE, minRefreshRate, maxRefreshRate, 0,
|
||||
Float.POSITIVE_INFINITY,
|
||||
minRefreshRate == maxRefreshRate, 0f);
|
||||
}
|
||||
|
||||
static Vote forRenderFrameRates(float minFrameRate, float maxFrameRate) {
|
||||
return new Vote(INVALID_SIZE, INVALID_SIZE, 0, Float.POSITIVE_INFINITY, minFrameRate,
|
||||
maxFrameRate,
|
||||
false, 0f);
|
||||
}
|
||||
|
||||
static Vote forSize(int width, int height) {
|
||||
return new Vote(width, height, 0, Float.POSITIVE_INFINITY, 0, Float.POSITIVE_INFINITY,
|
||||
false,
|
||||
0f);
|
||||
}
|
||||
|
||||
static Vote forDisableRefreshRateSwitching() {
|
||||
return new Vote(INVALID_SIZE, INVALID_SIZE, 0, Float.POSITIVE_INFINITY, 0,
|
||||
Float.POSITIVE_INFINITY, true,
|
||||
0f);
|
||||
}
|
||||
|
||||
static Vote forBaseModeRefreshRate(float baseModeRefreshRate) {
|
||||
return new Vote(INVALID_SIZE, INVALID_SIZE, 0, Float.POSITIVE_INFINITY, 0,
|
||||
Float.POSITIVE_INFINITY, false,
|
||||
baseModeRefreshRate);
|
||||
}
|
||||
|
||||
private Vote(int width, int height,
|
||||
float minPhysicalRefreshRate,
|
||||
float maxPhysicalRefreshRate,
|
||||
float minRenderFrameRate,
|
||||
float maxRenderFrameRate,
|
||||
boolean disableRefreshRateSwitching,
|
||||
float baseModeRefreshRate) {
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.refreshRateRanges = new SurfaceControl.RefreshRateRanges(
|
||||
new SurfaceControl.RefreshRateRange(minPhysicalRefreshRate, maxPhysicalRefreshRate),
|
||||
new SurfaceControl.RefreshRateRange(minRenderFrameRate, maxRenderFrameRate));
|
||||
this.disableRefreshRateSwitching = disableRefreshRateSwitching;
|
||||
this.appRequestBaseModeRefreshRate = baseModeRefreshRate;
|
||||
}
|
||||
|
||||
static String priorityToString(int priority) {
|
||||
switch (priority) {
|
||||
case PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE:
|
||||
return "PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE";
|
||||
case PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE:
|
||||
return "PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE";
|
||||
case PRIORITY_APP_REQUEST_SIZE:
|
||||
return "PRIORITY_APP_REQUEST_SIZE";
|
||||
case PRIORITY_DEFAULT_RENDER_FRAME_RATE:
|
||||
return "PRIORITY_DEFAULT_REFRESH_RATE";
|
||||
case PRIORITY_FLICKER_REFRESH_RATE:
|
||||
return "PRIORITY_FLICKER_REFRESH_RATE";
|
||||
case PRIORITY_FLICKER_REFRESH_RATE_SWITCH:
|
||||
return "PRIORITY_FLICKER_REFRESH_RATE_SWITCH";
|
||||
case PRIORITY_HIGH_BRIGHTNESS_MODE:
|
||||
return "PRIORITY_HIGH_BRIGHTNESS_MODE";
|
||||
case PRIORITY_PROXIMITY:
|
||||
return "PRIORITY_PROXIMITY";
|
||||
case PRIORITY_LOW_POWER_MODE:
|
||||
return "PRIORITY_LOW_POWER_MODE";
|
||||
case PRIORITY_SKIN_TEMPERATURE:
|
||||
return "PRIORITY_SKIN_TEMPERATURE";
|
||||
case PRIORITY_UDFPS:
|
||||
return "PRIORITY_UDFPS";
|
||||
case PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE:
|
||||
return "PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE";
|
||||
case PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE:
|
||||
return "PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE";
|
||||
case PRIORITY_AUTH_OPTIMIZER_RENDER_FRAME_RATE:
|
||||
return "PRIORITY_AUTH_OPTIMIZER_RENDER_FRAME_RATE";
|
||||
case PRIORITY_LAYOUT_LIMITED_FRAME_RATE:
|
||||
return "PRIORITY_LAYOUT_LIMITED_FRAME_RATE";
|
||||
default:
|
||||
return Integer.toString(priority);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Vote: {"
|
||||
+ "width: " + width + ", height: " + height
|
||||
+ ", refreshRateRanges: " + refreshRateRanges
|
||||
+ ", disableRefreshRateSwitching: " + disableRefreshRateSwitching
|
||||
+ ", appRequestBaseModeRefreshRate: " + appRequestBaseModeRefreshRate + "}";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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.server.display.mode;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.util.Slog;
|
||||
import android.util.SparseArray;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
|
||||
class VotesStorage {
|
||||
private static final String TAG = "VotesStorage";
|
||||
// Special ID used to indicate that given vote is to be applied globally, rather than to a
|
||||
// specific display.
|
||||
private static final int GLOBAL_ID = -1;
|
||||
|
||||
private boolean mLoggingEnabled;
|
||||
|
||||
private final Listener mListener;
|
||||
|
||||
private final Object mStorageLock = new Object();
|
||||
// A map from the display ID to the collection of votes and their priority. The latter takes
|
||||
// the form of another map from the priority to the vote itself so that each priority is
|
||||
// guaranteed to have exactly one vote, which is also easily and efficiently replaceable.
|
||||
@GuardedBy("mStorageLock")
|
||||
private final SparseArray<SparseArray<Vote>> mVotesByDisplay = new SparseArray<>();
|
||||
|
||||
VotesStorage(@NonNull Listener listener) {
|
||||
mListener = listener;
|
||||
}
|
||||
/** sets logging enabled/disabled for this class */
|
||||
void setLoggingEnabled(boolean loggingEnabled) {
|
||||
mLoggingEnabled = loggingEnabled;
|
||||
}
|
||||
/**
|
||||
* gets all votes for specific display, note that global display votes are also added to result
|
||||
*/
|
||||
@NonNull
|
||||
SparseArray<Vote> getVotes(int displayId) {
|
||||
SparseArray<Vote> votesLocal;
|
||||
SparseArray<Vote> globalVotesLocal;
|
||||
synchronized (mStorageLock) {
|
||||
SparseArray<Vote> displayVotes = mVotesByDisplay.get(displayId);
|
||||
votesLocal = displayVotes != null ? displayVotes.clone() : new SparseArray<>();
|
||||
SparseArray<Vote> globalVotes = mVotesByDisplay.get(GLOBAL_ID);
|
||||
globalVotesLocal = globalVotes != null ? globalVotes.clone() : new SparseArray<>();
|
||||
}
|
||||
for (int i = 0; i < globalVotesLocal.size(); i++) {
|
||||
int priority = globalVotesLocal.keyAt(i);
|
||||
if (!votesLocal.contains(priority)) {
|
||||
votesLocal.put(priority, globalVotesLocal.valueAt(i));
|
||||
}
|
||||
}
|
||||
return votesLocal;
|
||||
}
|
||||
|
||||
/** updates vote storage for all displays */
|
||||
void updateGlobalVote(int priority, @Nullable Vote vote) {
|
||||
updateVote(GLOBAL_ID, priority, vote);
|
||||
}
|
||||
|
||||
/** updates vote storage */
|
||||
void updateVote(int displayId, int priority, @Nullable Vote vote) {
|
||||
if (mLoggingEnabled) {
|
||||
Slog.i(TAG, "updateVoteLocked(displayId=" + displayId
|
||||
+ ", priority=" + Vote.priorityToString(priority)
|
||||
+ ", vote=" + vote + ")");
|
||||
}
|
||||
if (priority < Vote.MIN_PRIORITY || priority > Vote.MAX_PRIORITY) {
|
||||
Slog.w(TAG, "Received a vote with an invalid priority, ignoring:"
|
||||
+ " priority=" + Vote.priorityToString(priority)
|
||||
+ ", vote=" + vote);
|
||||
return;
|
||||
}
|
||||
SparseArray<Vote> votes;
|
||||
synchronized (mStorageLock) {
|
||||
if (mVotesByDisplay.contains(displayId)) {
|
||||
votes = mVotesByDisplay.get(displayId);
|
||||
} else {
|
||||
votes = new SparseArray<>();
|
||||
mVotesByDisplay.put(displayId, votes);
|
||||
}
|
||||
if (vote != null) {
|
||||
votes.put(priority, vote);
|
||||
} else {
|
||||
votes.remove(priority);
|
||||
}
|
||||
}
|
||||
if (mLoggingEnabled) {
|
||||
Slog.i(TAG, "Updated votes for display=" + displayId + " votes=" + votes);
|
||||
}
|
||||
mListener.onChanged();
|
||||
}
|
||||
|
||||
/** dump class values, for debugging */
|
||||
void dump(@NonNull PrintWriter pw) {
|
||||
SparseArray<SparseArray<Vote>> votesByDisplayLocal = new SparseArray<>();
|
||||
synchronized (mStorageLock) {
|
||||
for (int i = 0; i < mVotesByDisplay.size(); i++) {
|
||||
votesByDisplayLocal.put(mVotesByDisplay.keyAt(i),
|
||||
mVotesByDisplay.valueAt(i).clone());
|
||||
}
|
||||
}
|
||||
pw.println(" mVotesByDisplay:");
|
||||
for (int i = 0; i < votesByDisplayLocal.size(); i++) {
|
||||
SparseArray<Vote> votes = votesByDisplayLocal.valueAt(i);
|
||||
if (votes.size() == 0) {
|
||||
continue;
|
||||
}
|
||||
pw.println(" " + votesByDisplayLocal.keyAt(i) + ":");
|
||||
for (int p = Vote.MAX_PRIORITY; p >= Vote.MIN_PRIORITY; p--) {
|
||||
Vote vote = votes.get(p);
|
||||
if (vote == null) {
|
||||
continue;
|
||||
}
|
||||
pw.println(" " + Vote.priorityToString(p) + " -> " + vote);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void injectVotesByDisplay(SparseArray<SparseArray<Vote>> votesByDisplay) {
|
||||
synchronized (mStorageLock) {
|
||||
mVotesByDisplay.clear();
|
||||
for (int i = 0; i < votesByDisplay.size(); i++) {
|
||||
mVotesByDisplay.put(votesByDisplay.keyAt(i), votesByDisplay.valueAt(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface Listener {
|
||||
void onChanged();
|
||||
}
|
||||
}
|
||||
@@ -27,7 +27,7 @@ import static android.hardware.display.DisplayManager.DeviceConfig.KEY_REFRESH_R
|
||||
import static android.hardware.display.DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_HIGH_ZONE;
|
||||
import static android.hardware.display.DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_LOW_ZONE;
|
||||
|
||||
import static com.android.server.display.mode.DisplayModeDirector.Vote.INVALID_SIZE;
|
||||
import static com.android.server.display.mode.Vote.INVALID_SIZE;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
@@ -94,7 +94,6 @@ import com.android.server.display.DisplayDeviceConfig;
|
||||
import com.android.server.display.TestUtils;
|
||||
import com.android.server.display.mode.DisplayModeDirector.BrightnessObserver;
|
||||
import com.android.server.display.mode.DisplayModeDirector.DesiredDisplayModeSpecs;
|
||||
import com.android.server.display.mode.DisplayModeDirector.Vote;
|
||||
import com.android.server.sensors.SensorManagerInternal;
|
||||
import com.android.server.sensors.SensorManagerInternal.ProximityActiveListener;
|
||||
import com.android.server.statusbar.StatusBarManagerInternal;
|
||||
@@ -224,8 +223,7 @@ public class DisplayModeDirectorTest {
|
||||
assertThat(modeSpecs.appRequest.render.min).isEqualTo(0f);
|
||||
assertThat(modeSpecs.appRequest.render.max).isEqualTo(Float.POSITIVE_INFINITY);
|
||||
|
||||
int numPriorities =
|
||||
DisplayModeDirector.Vote.MAX_PRIORITY - DisplayModeDirector.Vote.MIN_PRIORITY + 1;
|
||||
int numPriorities = Vote.MAX_PRIORITY - Vote.MIN_PRIORITY + 1;
|
||||
|
||||
// Ensure vote priority works as expected. As we add new votes with higher priority, they
|
||||
// should take precedence over lower priority votes.
|
||||
|
||||
@@ -18,7 +18,6 @@ package com.android.server.display.mode;
|
||||
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
import android.hardware.display.DisplayManager;
|
||||
@@ -57,7 +56,7 @@ public class SkinThermalStatusObserverTest {
|
||||
private RegisteringFakesInjector mInjector = new RegisteringFakesInjector();
|
||||
|
||||
private final TestHandler mHandler = new TestHandler(null);
|
||||
private final FakeVoteStorage mStorage = new FakeVoteStorage();
|
||||
private final VotesStorage mStorage = new VotesStorage(() -> {});
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
@@ -92,28 +91,26 @@ public class SkinThermalStatusObserverTest {
|
||||
public void testNotifyWithDefaultVotesForCritical() {
|
||||
// GIVEN 2 displays with no thermalThrottling config
|
||||
mObserver.observe();
|
||||
assertEquals(0, mStorage.mVoteRegistry.size());
|
||||
assertEquals(0, mStorage.getVotes(DISPLAY_ID).size());
|
||||
assertEquals(0, mStorage.getVotes(DISPLAY_ID_OTHER).size());
|
||||
|
||||
// WHEN thermal sensor notifies CRITICAL
|
||||
mObserver.notifyThrottling(createTemperature(Temperature.THROTTLING_CRITICAL));
|
||||
mHandler.flush();
|
||||
|
||||
// THEN 2 votes are added to storage with (0,60) render refresh rate(default behaviour)
|
||||
assertEquals(2, mStorage.mVoteRegistry.size());
|
||||
|
||||
SparseArray<DisplayModeDirector.Vote> displayVotes = mStorage.mVoteRegistry.get(DISPLAY_ID);
|
||||
SparseArray<Vote> displayVotes = mStorage.getVotes(DISPLAY_ID);
|
||||
assertEquals(1, displayVotes.size());
|
||||
|
||||
DisplayModeDirector.Vote vote = displayVotes.get(
|
||||
DisplayModeDirector.Vote.PRIORITY_SKIN_TEMPERATURE);
|
||||
Vote vote = displayVotes.get(
|
||||
Vote.PRIORITY_SKIN_TEMPERATURE);
|
||||
assertEquals(0, vote.refreshRateRanges.render.min, FLOAT_TOLERANCE);
|
||||
assertEquals(60, vote.refreshRateRanges.render.max, FLOAT_TOLERANCE);
|
||||
|
||||
SparseArray<DisplayModeDirector.Vote> otherDisplayVotes = mStorage.mVoteRegistry.get(
|
||||
DISPLAY_ID_OTHER);
|
||||
SparseArray<Vote> otherDisplayVotes = mStorage.getVotes(DISPLAY_ID_OTHER);
|
||||
assertEquals(1, otherDisplayVotes.size());
|
||||
|
||||
vote = otherDisplayVotes.get(DisplayModeDirector.Vote.PRIORITY_SKIN_TEMPERATURE);
|
||||
vote = otherDisplayVotes.get(Vote.PRIORITY_SKIN_TEMPERATURE);
|
||||
assertEquals(0, vote.refreshRateRanges.render.min, FLOAT_TOLERANCE);
|
||||
assertEquals(60, vote.refreshRateRanges.render.max, FLOAT_TOLERANCE);
|
||||
}
|
||||
@@ -122,25 +119,29 @@ public class SkinThermalStatusObserverTest {
|
||||
public void testNotifyWithDefaultVotesChangeFromCriticalToSevere() {
|
||||
// GIVEN 2 displays with no thermalThrottling config AND temperature level CRITICAL
|
||||
mObserver.observe();
|
||||
assertEquals(0, mStorage.mVoteRegistry.size());
|
||||
assertEquals(0, mStorage.getVotes(DISPLAY_ID).size());
|
||||
assertEquals(0, mStorage.getVotes(DISPLAY_ID_OTHER).size());
|
||||
mObserver.notifyThrottling(createTemperature(Temperature.THROTTLING_CRITICAL));
|
||||
// WHEN thermal sensor notifies SEVERE
|
||||
mObserver.notifyThrottling(createTemperature(Temperature.THROTTLING_SEVERE));
|
||||
mHandler.flush();
|
||||
// THEN all votes with PRIORITY_SKIN_TEMPERATURE are removed from the storage
|
||||
assertEquals(0, mStorage.mVoteRegistry.size());
|
||||
assertEquals(0, mStorage.getVotes(DISPLAY_ID).size());
|
||||
assertEquals(0, mStorage.getVotes(DISPLAY_ID_OTHER).size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotifyWithDefaultVotesForSevere() {
|
||||
// GIVEN 2 displays with no thermalThrottling config
|
||||
mObserver.observe();
|
||||
assertEquals(0, mStorage.mVoteRegistry.size());
|
||||
assertEquals(0, mStorage.getVotes(DISPLAY_ID).size());
|
||||
assertEquals(0, mStorage.getVotes(DISPLAY_ID_OTHER).size());
|
||||
// WHEN thermal sensor notifies CRITICAL
|
||||
mObserver.notifyThrottling(createTemperature(Temperature.THROTTLING_SEVERE));
|
||||
mHandler.flush();
|
||||
// THEN nothing is added to the storage
|
||||
assertEquals(0, mStorage.mVoteRegistry.size());
|
||||
assertEquals(0, mStorage.getVotes(DISPLAY_ID).size());
|
||||
assertEquals(0, mStorage.getVotes(DISPLAY_ID_OTHER).size());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -155,18 +156,20 @@ public class SkinThermalStatusObserverTest {
|
||||
mObserver = new SkinThermalStatusObserver(mInjector, mStorage, mHandler);
|
||||
mObserver.observe();
|
||||
mObserver.onDisplayChanged(DISPLAY_ID);
|
||||
assertEquals(0, mStorage.mVoteRegistry.size());
|
||||
assertEquals(0, mStorage.getVotes(DISPLAY_ID).size());
|
||||
assertEquals(0, mStorage.getVotes(DISPLAY_ID_OTHER).size());
|
||||
// WHEN thermal sensor notifies temperature above configured
|
||||
mObserver.notifyThrottling(createTemperature(Temperature.THROTTLING_SEVERE));
|
||||
mHandler.flush();
|
||||
// THEN vote with refreshRate from config is added to the storage
|
||||
assertEquals(1, mStorage.mVoteRegistry.size());
|
||||
SparseArray<DisplayModeDirector.Vote> displayVotes = mStorage.mVoteRegistry.get(DISPLAY_ID);
|
||||
assertEquals(0, mStorage.getVotes(DISPLAY_ID_OTHER).size());
|
||||
|
||||
SparseArray<Vote> displayVotes = mStorage.getVotes(DISPLAY_ID);
|
||||
assertEquals(1, displayVotes.size());
|
||||
DisplayModeDirector.Vote vote = displayVotes.get(
|
||||
DisplayModeDirector.Vote.PRIORITY_SKIN_TEMPERATURE);
|
||||
Vote vote = displayVotes.get(Vote.PRIORITY_SKIN_TEMPERATURE);
|
||||
assertEquals(90, vote.refreshRateRanges.render.min, FLOAT_TOLERANCE);
|
||||
assertEquals(120, vote.refreshRateRanges.render.max, FLOAT_TOLERANCE);
|
||||
assertEquals(0, mStorage.getVotes(DISPLAY_ID_OTHER).size());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -178,14 +181,13 @@ public class SkinThermalStatusObserverTest {
|
||||
mObserver.onDisplayAdded(DISPLAY_ID_ADDED);
|
||||
mHandler.flush();
|
||||
// THEN 3rd vote is added to storage with (0,60) render refresh rate(default behaviour)
|
||||
assertEquals(3, mStorage.mVoteRegistry.size());
|
||||
assertEquals(1, mStorage.getVotes(DISPLAY_ID).size());
|
||||
assertEquals(1, mStorage.getVotes(DISPLAY_ID_OTHER).size());
|
||||
assertEquals(1, mStorage.getVotes(DISPLAY_ID_ADDED).size());
|
||||
|
||||
SparseArray<DisplayModeDirector.Vote> displayVotes = mStorage.mVoteRegistry.get(
|
||||
DISPLAY_ID_ADDED);
|
||||
assertEquals(1, displayVotes.size());
|
||||
SparseArray<Vote> displayVotes = mStorage.getVotes(DISPLAY_ID_ADDED);
|
||||
|
||||
DisplayModeDirector.Vote vote = displayVotes.get(
|
||||
DisplayModeDirector.Vote.PRIORITY_SKIN_TEMPERATURE);
|
||||
Vote vote = displayVotes.get(Vote.PRIORITY_SKIN_TEMPERATURE);
|
||||
assertEquals(0, vote.refreshRateRanges.render.min, FLOAT_TOLERANCE);
|
||||
assertEquals(60, vote.refreshRateRanges.render.max, FLOAT_TOLERANCE);
|
||||
}
|
||||
@@ -200,9 +202,9 @@ public class SkinThermalStatusObserverTest {
|
||||
mObserver.onDisplayRemoved(DISPLAY_ID_ADDED);
|
||||
mHandler.flush();
|
||||
// THEN there are 2 votes in registry
|
||||
assertEquals(2, mStorage.mVoteRegistry.size());
|
||||
assertNotNull(mStorage.mVoteRegistry.get(DISPLAY_ID));
|
||||
assertNotNull(mStorage.mVoteRegistry.get(DISPLAY_ID_OTHER));
|
||||
assertEquals(1, mStorage.getVotes(DISPLAY_ID).size());
|
||||
assertEquals(1, mStorage.getVotes(DISPLAY_ID_OTHER).size());
|
||||
assertEquals(0, mStorage.getVotes(DISPLAY_ID_ADDED).size());
|
||||
}
|
||||
|
||||
private static Temperature createTemperature(@Temperature.ThrottlingStatus int status) {
|
||||
@@ -259,27 +261,4 @@ public class SkinThermalStatusObserverTest {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class FakeVoteStorage implements DisplayModeDirector.BallotBox {
|
||||
private final SparseArray<SparseArray<DisplayModeDirector.Vote>> mVoteRegistry =
|
||||
new SparseArray<>();
|
||||
|
||||
@Override
|
||||
public void vote(int displayId, int priority, DisplayModeDirector.Vote vote) {
|
||||
SparseArray<DisplayModeDirector.Vote> votesPerDisplay = mVoteRegistry.get(displayId);
|
||||
if (votesPerDisplay == null) {
|
||||
votesPerDisplay = new SparseArray<>();
|
||||
mVoteRegistry.put(displayId, votesPerDisplay);
|
||||
}
|
||||
if (vote == null) {
|
||||
votesPerDisplay.remove(priority);
|
||||
} else {
|
||||
votesPerDisplay.put(priority, vote);
|
||||
}
|
||||
if (votesPerDisplay.size() == 0) {
|
||||
mVoteRegistry.remove(displayId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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.server.display.mode;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import android.util.SparseArray;
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
@SmallTest
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class VotesStorageTest {
|
||||
private static final int DISPLAY_ID = 100;
|
||||
private static final int PRIORITY = Vote.PRIORITY_APP_REQUEST_SIZE;
|
||||
private static final Vote VOTE = Vote.forDisableRefreshRateSwitching();
|
||||
private static final int DISPLAY_ID_OTHER = 101;
|
||||
private static final int PRIORITY_OTHER = Vote.PRIORITY_FLICKER_REFRESH_RATE;
|
||||
private static final Vote VOTE_OTHER = Vote.forBaseModeRefreshRate(10f);
|
||||
|
||||
@Mock
|
||||
public VotesStorage.Listener mVotesListener;
|
||||
|
||||
private VotesStorage mVotesStorage;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mVotesStorage = new VotesStorage(mVotesListener);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addsVoteToStorage() {
|
||||
// WHEN updateVote is called
|
||||
mVotesStorage.updateVote(DISPLAY_ID, PRIORITY, VOTE);
|
||||
// THEN vote is added to the storage
|
||||
SparseArray<Vote> votes = mVotesStorage.getVotes(DISPLAY_ID);
|
||||
assertThat(votes.size()).isEqualTo(1);
|
||||
assertThat(votes.get(PRIORITY)).isEqualTo(VOTE);
|
||||
assertThat(mVotesStorage.getVotes(DISPLAY_ID_OTHER).size()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void notifiesVoteListenerIfVoteAdded() {
|
||||
// WHEN updateVote is called
|
||||
mVotesStorage.updateVote(DISPLAY_ID, PRIORITY, VOTE);
|
||||
// THEN listener is notified
|
||||
verify(mVotesListener).onChanged();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addsAnotherVoteToStorageWithDifferentPriority() {
|
||||
// GIVEN vote storage with one vote
|
||||
mVotesStorage.updateVote(DISPLAY_ID, PRIORITY, VOTE);
|
||||
// WHEN updateVote is called with other priority
|
||||
mVotesStorage.updateVote(DISPLAY_ID, PRIORITY_OTHER, VOTE_OTHER);
|
||||
// THEN another vote is added to storage
|
||||
SparseArray<Vote> votes = mVotesStorage.getVotes(DISPLAY_ID);
|
||||
assertThat(votes.size()).isEqualTo(2);
|
||||
assertThat(votes.get(PRIORITY)).isEqualTo(VOTE);
|
||||
assertThat(votes.get(PRIORITY_OTHER)).isEqualTo(VOTE_OTHER);
|
||||
assertThat(mVotesStorage.getVotes(DISPLAY_ID_OTHER).size()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void replacesVoteInStorageForSamePriority() {
|
||||
// GIVEN vote storage with one vote
|
||||
mVotesStorage.updateVote(DISPLAY_ID, PRIORITY, VOTE);
|
||||
// WHEN updateVote is called with same priority
|
||||
mVotesStorage.updateVote(DISPLAY_ID, PRIORITY, VOTE_OTHER);
|
||||
// THEN vote is replaced by other vote
|
||||
SparseArray<Vote> votes = mVotesStorage.getVotes(DISPLAY_ID);
|
||||
assertThat(votes.size()).isEqualTo(1);
|
||||
assertThat(votes.get(PRIORITY)).isEqualTo(VOTE_OTHER);
|
||||
assertThat(mVotesStorage.getVotes(DISPLAY_ID_OTHER).size()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void removesVoteInStorageForSamePriority() {
|
||||
// GIVEN vote storage with one vote
|
||||
mVotesStorage.updateVote(DISPLAY_ID, PRIORITY, VOTE);
|
||||
// WHEN update is called with same priority and null vote
|
||||
mVotesStorage.updateVote(DISPLAY_ID, PRIORITY, null);
|
||||
// THEN vote removed from the storage
|
||||
assertThat(mVotesStorage.getVotes(DISPLAY_ID).size()).isEqualTo(0);
|
||||
assertThat(mVotesStorage.getVotes(DISPLAY_ID_OTHER).size()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addsGlobalDisplayVoteToStorage() {
|
||||
// WHEN updateGlobalVote is called
|
||||
mVotesStorage.updateGlobalVote(PRIORITY, VOTE);
|
||||
// THEN it is added to the storage for every display
|
||||
SparseArray<Vote> votes = mVotesStorage.getVotes(DISPLAY_ID);
|
||||
assertThat(votes.size()).isEqualTo(1);
|
||||
assertThat(votes.get(PRIORITY)).isEqualTo(VOTE);
|
||||
votes = mVotesStorage.getVotes(DISPLAY_ID_OTHER);
|
||||
assertThat(votes.size()).isEqualTo(1);
|
||||
assertThat(votes.get(PRIORITY)).isEqualTo(VOTE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ignoresVotesWithLowerThanMinPriority() {
|
||||
// WHEN updateVote is called with invalid (lower than min) priority
|
||||
mVotesStorage.updateVote(DISPLAY_ID, Vote.MIN_PRIORITY - 1, VOTE);
|
||||
// THEN vote is not added to the storage AND listener not notified
|
||||
assertThat(mVotesStorage.getVotes(DISPLAY_ID).size()).isEqualTo(0);
|
||||
verify(mVotesListener, never()).onChanged();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ignoresVotesWithGreaterThanMaxPriority() {
|
||||
// WHEN updateVote is called with invalid (greater than max) priority
|
||||
mVotesStorage.updateVote(DISPLAY_ID, Vote.MAX_PRIORITY + 1, VOTE);
|
||||
// THEN vote is not added to the storage AND listener not notified
|
||||
assertThat(mVotesStorage.getVotes(DISPLAY_ID).size()).isEqualTo(0);
|
||||
verify(mVotesListener, never()).onChanged();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user