Merge "Adding refreshRate thermal throttling configuration" into udc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
9d0b62832d
@@ -24,6 +24,7 @@ import static android.view.DisplayInfoProto.LOGICAL_HEIGHT;
|
||||
import static android.view.DisplayInfoProto.LOGICAL_WIDTH;
|
||||
import static android.view.DisplayInfoProto.NAME;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.app.WindowConfiguration;
|
||||
import android.compat.annotation.UnsupportedAppUsage;
|
||||
@@ -37,6 +38,7 @@ import android.os.Parcelable;
|
||||
import android.os.Process;
|
||||
import android.util.ArraySet;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.SparseArray;
|
||||
import android.util.proto.ProtoOutputStream;
|
||||
|
||||
import com.android.internal.display.BrightnessSynchronizer;
|
||||
@@ -348,6 +350,12 @@ public final class DisplayInfo implements Parcelable {
|
||||
*/
|
||||
public float hdrSdrRatio = Float.NaN;
|
||||
|
||||
/**
|
||||
* RefreshRateRange limitation for @Temperature.ThrottlingStatus
|
||||
*/
|
||||
@NonNull
|
||||
public SparseArray<SurfaceControl.RefreshRateRange> refreshRateThermalThrottling =
|
||||
new SparseArray<>();
|
||||
|
||||
public static final @android.annotation.NonNull Creator<DisplayInfo> CREATOR = new Creator<DisplayInfo>() {
|
||||
@Override
|
||||
@@ -425,7 +433,8 @@ public final class DisplayInfo implements Parcelable {
|
||||
&& installOrientation == other.installOrientation
|
||||
&& Objects.equals(displayShape, other.displayShape)
|
||||
&& Objects.equals(layoutLimitedRefreshRate, other.layoutLimitedRefreshRate)
|
||||
&& BrightnessSynchronizer.floatEquals(hdrSdrRatio, other.hdrSdrRatio);
|
||||
&& BrightnessSynchronizer.floatEquals(hdrSdrRatio, other.hdrSdrRatio)
|
||||
&& refreshRateThermalThrottling.contentEquals(other.refreshRateThermalThrottling);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -482,6 +491,7 @@ public final class DisplayInfo implements Parcelable {
|
||||
displayShape = other.displayShape;
|
||||
layoutLimitedRefreshRate = other.layoutLimitedRefreshRate;
|
||||
hdrSdrRatio = other.hdrSdrRatio;
|
||||
refreshRateThermalThrottling = other.refreshRateThermalThrottling;
|
||||
}
|
||||
|
||||
public void readFromParcel(Parcel source) {
|
||||
@@ -544,6 +554,8 @@ public final class DisplayInfo implements Parcelable {
|
||||
displayShape = source.readTypedObject(DisplayShape.CREATOR);
|
||||
layoutLimitedRefreshRate = source.readTypedObject(SurfaceControl.RefreshRateRange.CREATOR);
|
||||
hdrSdrRatio = source.readFloat();
|
||||
refreshRateThermalThrottling = source.readSparseArray(null,
|
||||
SurfaceControl.RefreshRateRange.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -604,6 +616,7 @@ public final class DisplayInfo implements Parcelable {
|
||||
dest.writeTypedObject(displayShape, flags);
|
||||
dest.writeTypedObject(layoutLimitedRefreshRate, flags);
|
||||
dest.writeFloat(hdrSdrRatio);
|
||||
dest.writeSparseArray(refreshRateThermalThrottling);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -871,6 +884,8 @@ public final class DisplayInfo implements Parcelable {
|
||||
} else {
|
||||
sb.append(hdrSdrRatio);
|
||||
}
|
||||
sb.append(", refreshRateThermalThrottling ");
|
||||
sb.append(refreshRateThermalThrottling);
|
||||
sb.append("}");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@@ -138,6 +138,8 @@ class DeviceStateToLayoutMap {
|
||||
display.setPosition(POSITION_UNKNOWN);
|
||||
}
|
||||
display.setRefreshRateZoneId(d.getRefreshRateZoneId());
|
||||
display.setRefreshRateThermalThrottlingMapId(
|
||||
d.getRefreshRateThermalThrottlingMapId());
|
||||
}
|
||||
}
|
||||
} catch (IOException | DatatypeConfigurationException | XmlPullParserException e) {
|
||||
|
||||
@@ -31,6 +31,7 @@ import android.text.TextUtils;
|
||||
import android.util.MathUtils;
|
||||
import android.util.Pair;
|
||||
import android.util.Slog;
|
||||
import android.util.SparseArray;
|
||||
import android.util.Spline;
|
||||
import android.view.DisplayAddress;
|
||||
import android.view.SurfaceControl;
|
||||
@@ -54,6 +55,8 @@ 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.RefreshRateThrottlingMap;
|
||||
import com.android.server.display.config.RefreshRateThrottlingPoint;
|
||||
import com.android.server.display.config.RefreshRateZone;
|
||||
import com.android.server.display.config.SdrHdrRatioMap;
|
||||
import com.android.server.display.config.SdrHdrRatioPoint;
|
||||
@@ -149,9 +152,26 @@ import javax.xml.datatype.DatatypeConfigurationException;
|
||||
* <brightness>0.005</brightness>
|
||||
* </brightnessThrottlingPoint>
|
||||
* </concurrentDisplaysBrightnessThrottlingMap>
|
||||
* <refreshRateThrottlingMap>
|
||||
* <refreshRateThrottlingPoint>
|
||||
* <thermalStatus>critical</thermalStatus>
|
||||
* <refreshRateRange>
|
||||
* <minimum>0</minimum>
|
||||
* <maximum>60</maximum>
|
||||
* </refreshRateRange>
|
||||
* </refreshRateThrottlingPoint>
|
||||
* </refreshRateThrottlingMap>
|
||||
* </thermalThrottling>
|
||||
*
|
||||
* <refreshRate>
|
||||
* <refreshRateZoneProfiles>
|
||||
* <refreshRateZoneProfile id="concurrent">
|
||||
* <refreshRateRange>
|
||||
* <minimum>60</minimum>
|
||||
* <maximum>60</maximum>
|
||||
* </refreshRateRange>
|
||||
* </refreshRateZoneProfile>
|
||||
* </refreshRateZoneProfiles>
|
||||
* <defaultRefreshRateInHbmHdr>75</defaultRefreshRateInHbmHdr>
|
||||
* <defaultRefreshRateInHbmSunlight>75</defaultRefreshRateInHbmSunlight>
|
||||
* <lowerBlockingZoneConfigs>
|
||||
@@ -417,7 +437,7 @@ public class DisplayDeviceConfig {
|
||||
|
||||
public static final String QUIRK_CAN_SET_BRIGHTNESS_VIA_HWC = "canSetBrightnessViaHwc";
|
||||
|
||||
static final String DEFAULT_BRIGHTNESS_THROTTLING_DATA_ID = "default";
|
||||
static final String DEFAULT_ID = "default";
|
||||
|
||||
private static final float BRIGHTNESS_DEFAULT = 0.5f;
|
||||
private static final String ETC_DIR = "etc";
|
||||
@@ -662,7 +682,11 @@ public class DisplayDeviceConfig {
|
||||
private int[] mHighDisplayBrightnessThresholds = DEFAULT_BRIGHTNESS_THRESHOLDS;
|
||||
private int[] mHighAmbientBrightnessThresholds = DEFAULT_BRIGHTNESS_THRESHOLDS;
|
||||
|
||||
private Map<String, BrightnessThrottlingData> mBrightnessThrottlingDataMap = new HashMap();
|
||||
private final Map<String, BrightnessThrottlingData> mBrightnessThrottlingDataMap =
|
||||
new HashMap<>();
|
||||
|
||||
private final Map<String, SparseArray<SurfaceControl.RefreshRateRange>>
|
||||
mRefreshRateThrottlingMap = new HashMap<>();
|
||||
|
||||
@Nullable
|
||||
private HostUsiVersion mHostUsiVersion;
|
||||
@@ -1314,6 +1338,17 @@ public class DisplayDeviceConfig {
|
||||
return BrightnessThrottlingData.create(mBrightnessThrottlingDataMap.get(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param id - throttling data id or null for default
|
||||
* @return refresh rate throttling configuration
|
||||
*/
|
||||
@Nullable
|
||||
public SparseArray<SurfaceControl.RefreshRateRange> getRefreshRateThrottlingData(
|
||||
@Nullable String id) {
|
||||
String key = id == null ? DEFAULT_ID : id;
|
||||
return mRefreshRateThrottlingMap.get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Auto brightness darkening light debounce
|
||||
*/
|
||||
@@ -1552,6 +1587,8 @@ public class DisplayDeviceConfig {
|
||||
+ ", mRefreshRateZoneProfiles= " + mRefreshRateZoneProfiles
|
||||
+ ", mDefaultRefreshRateInHbmHdr= " + mDefaultRefreshRateInHbmHdr
|
||||
+ ", mDefaultRefreshRateInHbmSunlight= " + mDefaultRefreshRateInHbmSunlight
|
||||
+ ", mRefreshRateThrottlingMap= " + mRefreshRateThrottlingMap
|
||||
+ "\n"
|
||||
+ ", mLowDisplayBrightnessThresholds= "
|
||||
+ Arrays.toString(mLowDisplayBrightnessThresholds)
|
||||
+ ", mLowAmbientBrightnessThresholds= "
|
||||
@@ -1613,7 +1650,7 @@ public class DisplayDeviceConfig {
|
||||
loadBrightnessDefaultFromDdcXml(config);
|
||||
loadBrightnessConstraintsFromConfigXml();
|
||||
loadBrightnessMap(config);
|
||||
loadBrightnessThrottlingMaps(config);
|
||||
loadThermalThrottlingConfig(config);
|
||||
loadHighBrightnessModeData(config);
|
||||
loadQuirks(config);
|
||||
loadBrightnessRamps(config);
|
||||
@@ -1823,13 +1860,17 @@ public class DisplayDeviceConfig {
|
||||
return Spline.createSpline(nits, ratios);
|
||||
}
|
||||
|
||||
private void loadBrightnessThrottlingMaps(DisplayConfiguration config) {
|
||||
private void loadThermalThrottlingConfig(DisplayConfiguration config) {
|
||||
final ThermalThrottling throttlingConfig = config.getThermalThrottling();
|
||||
if (throttlingConfig == null) {
|
||||
Slog.i(TAG, "No thermal throttling config found");
|
||||
return;
|
||||
}
|
||||
loadBrightnessThrottlingMaps(throttlingConfig);
|
||||
loadRefreshRateThermalThrottlingMap(throttlingConfig);
|
||||
}
|
||||
|
||||
private void loadBrightnessThrottlingMaps(ThermalThrottling throttlingConfig) {
|
||||
final List<BrightnessThrottlingMap> maps = throttlingConfig.getBrightnessThrottlingMap();
|
||||
if (maps == null || maps.isEmpty()) {
|
||||
Slog.i(TAG, "No brightness throttling map found");
|
||||
@@ -1855,7 +1896,7 @@ public class DisplayDeviceConfig {
|
||||
}
|
||||
|
||||
if (!badConfig) {
|
||||
String id = map.getId() == null ? DEFAULT_BRIGHTNESS_THROTTLING_DATA_ID
|
||||
String id = map.getId() == null ? DEFAULT_ID
|
||||
: map.getId();
|
||||
if (mBrightnessThrottlingDataMap.containsKey(id)) {
|
||||
throw new RuntimeException("Brightness throttling data with ID " + id
|
||||
@@ -1867,6 +1908,57 @@ public class DisplayDeviceConfig {
|
||||
}
|
||||
}
|
||||
|
||||
private void loadRefreshRateThermalThrottlingMap(ThermalThrottling throttlingConfig) {
|
||||
List<RefreshRateThrottlingMap> maps = throttlingConfig.getRefreshRateThrottlingMap();
|
||||
if (maps == null || maps.isEmpty()) {
|
||||
Slog.w(TAG, "RefreshRateThrottling: map not found");
|
||||
return;
|
||||
}
|
||||
|
||||
for (RefreshRateThrottlingMap map : maps) {
|
||||
List<RefreshRateThrottlingPoint> points = map.getRefreshRateThrottlingPoint();
|
||||
String id = map.getId() == null ? DEFAULT_ID : map.getId();
|
||||
|
||||
if (points == null || points.isEmpty()) {
|
||||
// Expected at lease 1 throttling point for each map
|
||||
Slog.w(TAG, "RefreshRateThrottling: points not found for mapId=" + id);
|
||||
continue;
|
||||
}
|
||||
if (mRefreshRateThrottlingMap.containsKey(id)) {
|
||||
Slog.wtf(TAG, "RefreshRateThrottling: map already exists, mapId=" + id);
|
||||
continue;
|
||||
}
|
||||
|
||||
SparseArray<SurfaceControl.RefreshRateRange> refreshRates = new SparseArray<>();
|
||||
for (RefreshRateThrottlingPoint point : points) {
|
||||
ThermalStatus status = point.getThermalStatus();
|
||||
if (!thermalStatusIsValid(status)) {
|
||||
Slog.wtf(TAG,
|
||||
"RefreshRateThrottling: Invalid thermalStatus=" + status.getRawName()
|
||||
+ ",mapId=" + id);
|
||||
continue;
|
||||
}
|
||||
int thermalStatusInt = convertThermalStatus(status);
|
||||
if (refreshRates.contains(thermalStatusInt)) {
|
||||
Slog.wtf(TAG, "RefreshRateThrottling: thermalStatus=" + status.getRawName()
|
||||
+ " is already in the map, mapId=" + id);
|
||||
continue;
|
||||
}
|
||||
|
||||
refreshRates.put(thermalStatusInt, new SurfaceControl.RefreshRateRange(
|
||||
point.getRefreshRateRange().getMinimum().floatValue(),
|
||||
point.getRefreshRateRange().getMaximum().floatValue()
|
||||
));
|
||||
}
|
||||
if (refreshRates.size() == 0) {
|
||||
Slog.w(TAG, "RefreshRateThrottling: no valid throttling points fond for map, mapId="
|
||||
+ id);
|
||||
continue;
|
||||
}
|
||||
mRefreshRateThrottlingMap.put(id, refreshRates);
|
||||
}
|
||||
}
|
||||
|
||||
private void loadRefreshRateSetting(DisplayConfiguration config) {
|
||||
final RefreshRateConfigs refreshRateConfigs =
|
||||
(config == null) ? null : config.getRefreshRate();
|
||||
|
||||
@@ -189,7 +189,7 @@ final class LogicalDisplay {
|
||||
mTempFrameRateOverride = new SparseArray<>();
|
||||
mIsEnabled = true;
|
||||
mIsInTransition = false;
|
||||
mBrightnessThrottlingDataId = DisplayDeviceConfig.DEFAULT_BRIGHTNESS_THROTTLING_DATA_ID;
|
||||
mBrightnessThrottlingDataId = DisplayDeviceConfig.DEFAULT_ID;
|
||||
}
|
||||
|
||||
public void setDevicePositionLocked(int position) {
|
||||
@@ -344,6 +344,21 @@ final class LogicalDisplay {
|
||||
mInfo.set(null);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Updates refreshRateThermalThrottling
|
||||
*
|
||||
* @param refreshRanges new refreshRateThermalThrottling ranges limited by layout or default
|
||||
*/
|
||||
public void updateRefreshRateThermalThrottling(
|
||||
@Nullable SparseArray<SurfaceControl.RefreshRateRange> refreshRanges) {
|
||||
if (refreshRanges == null) {
|
||||
refreshRanges = new SparseArray<>();
|
||||
}
|
||||
if (!mBaseDisplayInfo.refreshRateThermalThrottling.contentEquals(refreshRanges)) {
|
||||
mBaseDisplayInfo.refreshRateThermalThrottling = refreshRanges;
|
||||
mInfo.set(null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the state of the logical display based on the available display devices.
|
||||
|
||||
@@ -1013,19 +1013,23 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
|
||||
if (newDisplay != oldDisplay) {
|
||||
newDisplay.swapDisplaysLocked(oldDisplay);
|
||||
}
|
||||
DisplayDeviceConfig config = device.getDisplayDeviceConfig();
|
||||
|
||||
newDisplay.setDevicePositionLocked(displayLayout.getPosition());
|
||||
newDisplay.setLeadDisplayLocked(displayLayout.getLeadDisplayId());
|
||||
newDisplay.updateLayoutLimitedRefreshRateLocked(
|
||||
device.getDisplayDeviceConfig().getRefreshRange(
|
||||
displayLayout.getRefreshRateZoneId()
|
||||
config.getRefreshRange(displayLayout.getRefreshRateZoneId())
|
||||
);
|
||||
newDisplay.updateRefreshRateThermalThrottling(
|
||||
config.getRefreshRateThrottlingData(
|
||||
displayLayout.getRefreshRateThermalThrottlingMapId()
|
||||
)
|
||||
);
|
||||
|
||||
setEnabledLocked(newDisplay, displayLayout.isEnabled());
|
||||
newDisplay.setBrightnessThrottlingDataIdLocked(
|
||||
displayLayout.getBrightnessThrottlingMapId() == null
|
||||
? DisplayDeviceConfig.DEFAULT_BRIGHTNESS_THROTTLING_DATA_ID
|
||||
? DisplayDeviceConfig.DEFAULT_ID
|
||||
: displayLayout.getBrightnessThrottlingMapId());
|
||||
|
||||
newDisplay.setDisplayGroupNameLocked(displayLayout.getDisplayGroupName());
|
||||
|
||||
@@ -257,6 +257,9 @@ public class Layout {
|
||||
@Nullable
|
||||
private String mRefreshRateZoneId;
|
||||
|
||||
@Nullable
|
||||
private String mRefreshRateThermalThrottlingMapId;
|
||||
|
||||
Display(@NonNull DisplayAddress address, int logicalDisplayId, boolean isEnabled,
|
||||
@NonNull String displayGroupName, String brightnessThrottlingMapId, int position,
|
||||
int leadDisplayId) {
|
||||
@@ -286,6 +289,7 @@ public class Layout {
|
||||
+ ", brightnessThrottlingMapId: " + mBrightnessThrottlingMapId
|
||||
+ ", mRefreshRateZoneId: " + mRefreshRateZoneId
|
||||
+ ", mLeadDisplayId: " + mLeadDisplayId
|
||||
+ ", mRefreshRateThermalThrottlingMapId: " + mRefreshRateThermalThrottlingMapId
|
||||
+ "}";
|
||||
}
|
||||
|
||||
@@ -305,7 +309,9 @@ public class Layout {
|
||||
&& Objects.equals(mBrightnessThrottlingMapId,
|
||||
otherDisplay.mBrightnessThrottlingMapId)
|
||||
&& Objects.equals(otherDisplay.mRefreshRateZoneId, this.mRefreshRateZoneId)
|
||||
&& this.mLeadDisplayId == otherDisplay.mLeadDisplayId;
|
||||
&& this.mLeadDisplayId == otherDisplay.mLeadDisplayId
|
||||
&& Objects.equals(mRefreshRateThermalThrottlingMapId,
|
||||
otherDisplay.mRefreshRateThermalThrottlingMapId);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -319,6 +325,7 @@ public class Layout {
|
||||
result = 31 * result + mBrightnessThrottlingMapId.hashCode();
|
||||
result = 31 * result + Objects.hashCode(mRefreshRateZoneId);
|
||||
result = 31 * result + mLeadDisplayId;
|
||||
result = 31 * result + Objects.hashCode(mRefreshRateThermalThrottlingMapId);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -388,5 +395,13 @@ public class Layout {
|
||||
public int getLeadDisplayId() {
|
||||
return mLeadDisplayId;
|
||||
}
|
||||
|
||||
public void setRefreshRateThermalThrottlingMapId(String refreshRateThermalThrottlingMapId) {
|
||||
mRefreshRateThermalThrottlingMapId = refreshRateThermalThrottlingMapId;
|
||||
}
|
||||
|
||||
public String getRefreshRateThermalThrottlingMapId() {
|
||||
return mRefreshRateThermalThrottlingMapId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -224,6 +224,7 @@ public class DisplayModeDirector {
|
||||
}
|
||||
mLoggingEnabled = loggingEnabled;
|
||||
mBrightnessObserver.setLoggingEnabled(loggingEnabled);
|
||||
mSkinThermalStatusObserver.setLoggingEnabled(loggingEnabled);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@@ -2669,7 +2670,7 @@ public class DisplayModeDirector {
|
||||
}
|
||||
}
|
||||
|
||||
private static final class SensorObserver implements ProximityActiveListener,
|
||||
protected static final class SensorObserver implements ProximityActiveListener,
|
||||
DisplayManager.DisplayListener {
|
||||
private final String mProximitySensorName = null;
|
||||
private final String mProximitySensorType = Sensor.STRING_TYPE_PROXIMITY;
|
||||
@@ -2952,52 +2953,6 @@ public class DisplayModeDirector {
|
||||
}
|
||||
}
|
||||
|
||||
private final class SkinThermalStatusObserver extends IThermalEventListener.Stub {
|
||||
private final BallotBox mBallotBox;
|
||||
private final Injector mInjector;
|
||||
|
||||
private @Temperature.ThrottlingStatus int mStatus = -1;
|
||||
|
||||
SkinThermalStatusObserver(Injector injector, BallotBox ballotBox) {
|
||||
mInjector = injector;
|
||||
mBallotBox = ballotBox;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyThrottling(Temperature temp) {
|
||||
mStatus = temp.getStatus();
|
||||
if (mLoggingEnabled) {
|
||||
Slog.d(TAG, "New thermal throttling status "
|
||||
+ ", current thermal status = " + mStatus);
|
||||
}
|
||||
final Vote vote;
|
||||
if (mStatus >= Temperature.THROTTLING_CRITICAL) {
|
||||
vote = Vote.forRenderFrameRates(0f, 60f);
|
||||
} else {
|
||||
vote = null;
|
||||
}
|
||||
mBallotBox.vote(GLOBAL_ID, Vote.PRIORITY_SKIN_TEMPERATURE, vote);
|
||||
}
|
||||
|
||||
public void observe() {
|
||||
IThermalService thermalService = mInjector.getThermalService();
|
||||
if (thermalService == null) {
|
||||
Slog.w(TAG, "Could not observe thermal status. Service not available");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
thermalService.registerThermalEventListenerWithType(this, Temperature.TYPE_SKIN);
|
||||
} catch (RemoteException e) {
|
||||
Slog.e(TAG, "Failed to register thermal status listener", e);
|
||||
}
|
||||
}
|
||||
|
||||
void dumpLocked(PrintWriter writer) {
|
||||
writer.println(" SkinThermalStatusObserver:");
|
||||
writer.println(" mStatus: " + mStatus);
|
||||
}
|
||||
}
|
||||
|
||||
private class DeviceConfigDisplaySettings implements DeviceConfig.OnPropertiesChangedListener {
|
||||
public void startListening() {
|
||||
mDeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
|
||||
@@ -3184,11 +3139,15 @@ public class DisplayModeDirector {
|
||||
void registerDisplayListener(@NonNull DisplayManager.DisplayListener listener,
|
||||
Handler handler, long flags);
|
||||
|
||||
Display[] getDisplays();
|
||||
|
||||
boolean getDisplayInfo(int displayId, DisplayInfo displayInfo);
|
||||
|
||||
BrightnessInfo getBrightnessInfo(int displayId);
|
||||
|
||||
boolean isDozeState(Display d);
|
||||
|
||||
IThermalService getThermalService();
|
||||
boolean registerThermalServiceListener(IThermalEventListener listener);
|
||||
|
||||
boolean supportsFrameRateOverride();
|
||||
}
|
||||
@@ -3221,6 +3180,20 @@ public class DisplayModeDirector {
|
||||
getDisplayManager().registerDisplayListener(listener, handler, flags);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Display[] getDisplays() {
|
||||
return getDisplayManager().getDisplays(DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getDisplayInfo(int displayId, DisplayInfo displayInfo) {
|
||||
Display display = getDisplayManager().getDisplay(displayId);
|
||||
if (display != null) {
|
||||
return display.getDisplayInfo(displayInfo);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BrightnessInfo getBrightnessInfo(int displayId) {
|
||||
final Display display = getDisplayManager().getDisplay(displayId);
|
||||
@@ -3239,9 +3212,20 @@ public class DisplayModeDirector {
|
||||
}
|
||||
|
||||
@Override
|
||||
public IThermalService getThermalService() {
|
||||
return IThermalService.Stub.asInterface(
|
||||
ServiceManager.getService(Context.THERMAL_SERVICE));
|
||||
public boolean registerThermalServiceListener(IThermalEventListener listener) {
|
||||
IThermalService thermalService = getThermalService();
|
||||
if (thermalService == null) {
|
||||
Slog.w(TAG, "Could not observe thermal status. Service not available");
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
thermalService.registerThermalEventListenerWithType(listener,
|
||||
Temperature.TYPE_SKIN);
|
||||
} catch (RemoteException e) {
|
||||
Slog.e(TAG, "Failed to register thermal status listener", e);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -3255,6 +3239,11 @@ public class DisplayModeDirector {
|
||||
}
|
||||
return mDisplayManager;
|
||||
}
|
||||
|
||||
private IThermalService getThermalService() {
|
||||
return IThermalService.Stub.asInterface(
|
||||
ServiceManager.getService(Context.THERMAL_SERVICE));
|
||||
}
|
||||
}
|
||||
|
||||
interface BallotBox {
|
||||
|
||||
@@ -0,0 +1,263 @@
|
||||
/*
|
||||
* 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.Nullable;
|
||||
import android.hardware.display.DisplayManager;
|
||||
import android.os.Handler;
|
||||
import android.os.IThermalEventListener;
|
||||
import android.os.Temperature;
|
||||
import android.util.Slog;
|
||||
import android.util.SparseArray;
|
||||
import android.view.Display;
|
||||
import android.view.DisplayInfo;
|
||||
import android.view.SurfaceControl;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.os.BackgroundThread;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
|
||||
final class SkinThermalStatusObserver extends IThermalEventListener.Stub implements
|
||||
DisplayManager.DisplayListener {
|
||||
private static final String TAG = "SkinThermalStatusObserver";
|
||||
|
||||
private final DisplayModeDirector.BallotBox mBallotBox;
|
||||
private final DisplayModeDirector.Injector mInjector;
|
||||
|
||||
private boolean mLoggingEnabled;
|
||||
|
||||
private final Handler mHandler;
|
||||
private final Object mThermalObserverLock = new Object();
|
||||
@GuardedBy("mThermalObserverLock")
|
||||
@Temperature.ThrottlingStatus
|
||||
private int mStatus = -1;
|
||||
@GuardedBy("mThermalObserverLock")
|
||||
private final SparseArray<SparseArray<SurfaceControl.RefreshRateRange>>
|
||||
mThermalThrottlingByDisplay = new SparseArray<>();
|
||||
|
||||
SkinThermalStatusObserver(DisplayModeDirector.Injector injector,
|
||||
DisplayModeDirector.BallotBox ballotBox) {
|
||||
this(injector, ballotBox, BackgroundThread.getHandler());
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
SkinThermalStatusObserver(DisplayModeDirector.Injector injector,
|
||||
DisplayModeDirector.BallotBox ballotBox, Handler handler) {
|
||||
mInjector = injector;
|
||||
mBallotBox = ballotBox;
|
||||
mHandler = handler;
|
||||
}
|
||||
|
||||
void observe() {
|
||||
// if failed to register thermal service listener, don't register display listener
|
||||
if (!mInjector.registerThermalServiceListener(this)) {
|
||||
return;
|
||||
}
|
||||
|
||||
mInjector.registerDisplayListener(this, mHandler,
|
||||
DisplayManager.EVENT_FLAG_DISPLAY_ADDED | DisplayManager.EVENT_FLAG_DISPLAY_CHANGED
|
||||
| DisplayManager.EVENT_FLAG_DISPLAY_REMOVED);
|
||||
|
||||
populateInitialDisplayInfo();
|
||||
}
|
||||
|
||||
void setLoggingEnabled(boolean enabled) {
|
||||
mLoggingEnabled = enabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyThrottling(Temperature temp) {
|
||||
@Temperature.ThrottlingStatus int currentStatus = temp.getStatus();
|
||||
synchronized (mThermalObserverLock) {
|
||||
mStatus = currentStatus;
|
||||
mHandler.post(this::updateVotes);
|
||||
}
|
||||
|
||||
if (mLoggingEnabled) {
|
||||
Slog.d(TAG, "New thermal throttling status " + ", current thermal status = "
|
||||
+ currentStatus);
|
||||
}
|
||||
}
|
||||
|
||||
//region DisplayManager.DisplayListener
|
||||
@Override
|
||||
public void onDisplayAdded(int displayId) {
|
||||
updateRefreshRateThermalThrottling(displayId);
|
||||
if (mLoggingEnabled) {
|
||||
Slog.d(TAG, "Display added:" + displayId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisplayRemoved(int displayId) {
|
||||
synchronized (mThermalObserverLock) {
|
||||
mThermalThrottlingByDisplay.remove(displayId);
|
||||
mHandler.post(() -> mBallotBox.vote(displayId,
|
||||
DisplayModeDirector.Vote.PRIORITY_SKIN_TEMPERATURE, null));
|
||||
}
|
||||
if (mLoggingEnabled) {
|
||||
Slog.d(TAG, "Display removed and voted: displayId=" + displayId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisplayChanged(int displayId) {
|
||||
updateRefreshRateThermalThrottling(displayId);
|
||||
if (mLoggingEnabled) {
|
||||
Slog.d(TAG, "Display changed:" + displayId);
|
||||
}
|
||||
}
|
||||
//endregion
|
||||
|
||||
private void populateInitialDisplayInfo() {
|
||||
DisplayInfo info = new DisplayInfo();
|
||||
Display[] displays = mInjector.getDisplays();
|
||||
int size = displays.length;
|
||||
SparseArray<SparseArray<SurfaceControl.RefreshRateRange>> localMap = new SparseArray<>(
|
||||
size);
|
||||
for (Display d : displays) {
|
||||
final int displayId = d.getDisplayId();
|
||||
d.getDisplayInfo(info);
|
||||
localMap.put(displayId, info.refreshRateThermalThrottling);
|
||||
}
|
||||
synchronized (mThermalObserverLock) {
|
||||
for (int i = 0; i < size; i++) {
|
||||
mThermalThrottlingByDisplay.put(localMap.keyAt(i), localMap.valueAt(i));
|
||||
}
|
||||
}
|
||||
if (mLoggingEnabled) {
|
||||
Slog.d(TAG, "Display initial info:" + localMap);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateRefreshRateThermalThrottling(int displayId) {
|
||||
DisplayInfo displayInfo = new DisplayInfo();
|
||||
mInjector.getDisplayInfo(displayId, displayInfo);
|
||||
SparseArray<SurfaceControl.RefreshRateRange> throttlingMap =
|
||||
displayInfo.refreshRateThermalThrottling;
|
||||
|
||||
synchronized (mThermalObserverLock) {
|
||||
mThermalThrottlingByDisplay.put(displayId, throttlingMap);
|
||||
mHandler.post(() -> updateVoteForDisplay(displayId));
|
||||
}
|
||||
if (mLoggingEnabled) {
|
||||
Slog.d(TAG,
|
||||
"Thermal throttling updated: display=" + displayId + ", map=" + throttlingMap);
|
||||
}
|
||||
}
|
||||
|
||||
//region in mHandler thread
|
||||
private void updateVotes() {
|
||||
@Temperature.ThrottlingStatus int localStatus;
|
||||
SparseArray<SparseArray<SurfaceControl.RefreshRateRange>> localMap;
|
||||
|
||||
synchronized (mThermalObserverLock) {
|
||||
localStatus = mStatus;
|
||||
localMap = mThermalThrottlingByDisplay.clone();
|
||||
}
|
||||
if (mLoggingEnabled) {
|
||||
Slog.d(TAG, "Updating votes for status=" + localStatus + ", map=" + localMap);
|
||||
}
|
||||
int size = localMap.size();
|
||||
for (int i = 0; i < size; i++) {
|
||||
reportThrottlingIfNeeded(localMap.keyAt(i), localStatus, localMap.valueAt(i));
|
||||
}
|
||||
}
|
||||
|
||||
private void updateVoteForDisplay(int displayId) {
|
||||
@Temperature.ThrottlingStatus int localStatus;
|
||||
SparseArray<SurfaceControl.RefreshRateRange> localMap;
|
||||
|
||||
synchronized (mThermalObserverLock) {
|
||||
localStatus = mStatus;
|
||||
localMap = mThermalThrottlingByDisplay.get(displayId);
|
||||
}
|
||||
if (mLoggingEnabled) {
|
||||
Slog.d(TAG, "Updating votes for status=" + localStatus + ", display =" + displayId
|
||||
+ ", map=" + localMap);
|
||||
}
|
||||
reportThrottlingIfNeeded(displayId, localStatus, localMap);
|
||||
}
|
||||
|
||||
private void reportThrottlingIfNeeded(int displayId,
|
||||
@Temperature.ThrottlingStatus int currentStatus,
|
||||
SparseArray<SurfaceControl.RefreshRateRange> throttlingMap) {
|
||||
if (currentStatus == -1) { // no throttling status reported from thermal sensor yet
|
||||
return;
|
||||
}
|
||||
|
||||
if (throttlingMap.size() == 0) { // map is not configured, using default behaviour
|
||||
fallbackReportThrottlingIfNeeded(displayId, currentStatus);
|
||||
return;
|
||||
}
|
||||
|
||||
SurfaceControl.RefreshRateRange foundRange = findBestMatchingRefreshRateRange(currentStatus,
|
||||
throttlingMap);
|
||||
// if status <= currentStatus not found in the map reset vote
|
||||
DisplayModeDirector.Vote vote = null;
|
||||
if (foundRange != null) { // otherwise vote with found range
|
||||
vote = DisplayModeDirector.Vote.forRenderFrameRates(foundRange.min, foundRange.max);
|
||||
}
|
||||
mBallotBox.vote(displayId, DisplayModeDirector.Vote.PRIORITY_SKIN_TEMPERATURE, vote);
|
||||
if (mLoggingEnabled) {
|
||||
Slog.d(TAG, "Voted: vote=" + vote + ", display =" + displayId);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private SurfaceControl.RefreshRateRange findBestMatchingRefreshRateRange(
|
||||
@Temperature.ThrottlingStatus int currentStatus,
|
||||
SparseArray<SurfaceControl.RefreshRateRange> throttlingMap) {
|
||||
SurfaceControl.RefreshRateRange foundRange = null;
|
||||
for (int status = currentStatus; status >= 0; status--) {
|
||||
foundRange = throttlingMap.get(status);
|
||||
if (foundRange != null) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return foundRange;
|
||||
}
|
||||
|
||||
private void fallbackReportThrottlingIfNeeded(int displayId,
|
||||
@Temperature.ThrottlingStatus int currentStatus) {
|
||||
DisplayModeDirector.Vote vote = null;
|
||||
if (currentStatus >= Temperature.THROTTLING_CRITICAL) {
|
||||
vote = DisplayModeDirector.Vote.forRenderFrameRates(0f, 60f);
|
||||
}
|
||||
mBallotBox.vote(displayId, DisplayModeDirector.Vote.PRIORITY_SKIN_TEMPERATURE, vote);
|
||||
if (mLoggingEnabled) {
|
||||
Slog.d(TAG, "Voted(fallback): vote=" + vote + ", display =" + displayId);
|
||||
}
|
||||
}
|
||||
//endregion
|
||||
|
||||
void dumpLocked(PrintWriter writer) {
|
||||
@Temperature.ThrottlingStatus int localStatus;
|
||||
SparseArray<SparseArray<SurfaceControl.RefreshRateRange>> localMap;
|
||||
|
||||
synchronized (mThermalObserverLock) {
|
||||
localStatus = mStatus;
|
||||
localMap = mThermalThrottlingByDisplay.clone();
|
||||
}
|
||||
|
||||
writer.println(" SkinThermalStatusObserver:");
|
||||
writer.println(" mStatus: " + localStatus);
|
||||
writer.println(" mThermalThrottlingByDisplay: " + localMap);
|
||||
}
|
||||
}
|
||||
@@ -211,6 +211,32 @@
|
||||
<xs:element type="brightnessThrottlingMap" name="brightnessThrottlingMap" maxOccurs="unbounded">
|
||||
<xs:annotation name="final"/>
|
||||
</xs:element>
|
||||
<xs:element type="refreshRateThrottlingMap" name="refreshRateThrottlingMap" maxOccurs="unbounded">
|
||||
<xs:annotation name="final"/>
|
||||
</xs:element>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="refreshRateThrottlingMap">
|
||||
<xs:attribute name="id" type="xs:string" />
|
||||
<xs:sequence>
|
||||
<xs:element name="refreshRateThrottlingPoint" type="refreshRateThrottlingPoint" maxOccurs="unbounded">
|
||||
<xs:annotation name="nonnull"/>
|
||||
<xs:annotation name="final"/>
|
||||
</xs:element>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="refreshRateThrottlingPoint">
|
||||
<xs:sequence>
|
||||
<xs:element type="thermalStatus" name="thermalStatus">
|
||||
<xs:annotation name="nonnull"/>
|
||||
<xs:annotation name="final"/>
|
||||
</xs:element>
|
||||
<xs:element type="refreshRateRange" name="refreshRateRange">
|
||||
<xs:annotation name="nonnull"/>
|
||||
<xs:annotation name="final"/>
|
||||
</xs:element>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
|
||||
|
||||
@@ -214,6 +214,21 @@ package com.android.server.display.config {
|
||||
method public final void setMinimum(java.math.BigInteger);
|
||||
}
|
||||
|
||||
public class RefreshRateThrottlingMap {
|
||||
ctor public RefreshRateThrottlingMap();
|
||||
method public String getId();
|
||||
method @NonNull public final java.util.List<com.android.server.display.config.RefreshRateThrottlingPoint> getRefreshRateThrottlingPoint();
|
||||
method public void setId(String);
|
||||
}
|
||||
|
||||
public class RefreshRateThrottlingPoint {
|
||||
ctor public RefreshRateThrottlingPoint();
|
||||
method @NonNull public final com.android.server.display.config.RefreshRateRange getRefreshRateRange();
|
||||
method @NonNull public final com.android.server.display.config.ThermalStatus getThermalStatus();
|
||||
method public final void setRefreshRateRange(@NonNull com.android.server.display.config.RefreshRateRange);
|
||||
method public final void setThermalStatus(@NonNull com.android.server.display.config.ThermalStatus);
|
||||
}
|
||||
|
||||
public class RefreshRateZone {
|
||||
ctor public RefreshRateZone();
|
||||
method public String getId();
|
||||
@@ -264,6 +279,7 @@ package com.android.server.display.config {
|
||||
public class ThermalThrottling {
|
||||
ctor public ThermalThrottling();
|
||||
method public final java.util.List<com.android.server.display.config.BrightnessThrottlingMap> getBrightnessThrottlingMap();
|
||||
method public final java.util.List<com.android.server.display.config.RefreshRateThrottlingMap> getRefreshRateThrottlingMap();
|
||||
}
|
||||
|
||||
public class ThresholdPoint {
|
||||
|
||||
@@ -52,6 +52,7 @@
|
||||
<xs:element name="address" type="xs:nonNegativeInteger"/>
|
||||
<xs:element name="position" type="xs:string" minOccurs="0" maxOccurs="1" />
|
||||
<xs:element name="brightnessThrottlingMapId" type="xs:string" minOccurs="0" maxOccurs="1" />
|
||||
<xs:element name="refreshRateThermalThrottlingMapId" type="xs:string" minOccurs="0" />
|
||||
</xs:sequence>
|
||||
<xs:attribute name="enabled" type="xs:boolean" use="optional" />
|
||||
<xs:attribute name="defaultDisplay" type="xs:boolean" use="optional" />
|
||||
|
||||
@@ -7,6 +7,7 @@ package com.android.server.display.config.layout {
|
||||
method public String getBrightnessThrottlingMapId();
|
||||
method public String getDisplayGroup();
|
||||
method public String getPosition();
|
||||
method public String getRefreshRateThermalThrottlingMapId();
|
||||
method public String getRefreshRateZoneId();
|
||||
method public boolean isDefaultDisplay();
|
||||
method public boolean isEnabled();
|
||||
@@ -16,6 +17,7 @@ package com.android.server.display.config.layout {
|
||||
method public void setDisplayGroup(String);
|
||||
method public void setEnabled(boolean);
|
||||
method public void setPosition(String);
|
||||
method public void setRefreshRateThermalThrottlingMapId(String);
|
||||
method public void setRefreshRateZoneId(String);
|
||||
}
|
||||
|
||||
|
||||
@@ -249,7 +249,7 @@ public final class DisplayPowerController2Test {
|
||||
when(logicalDisplayMock.isEnabledLocked()).thenReturn(isEnabled);
|
||||
when(logicalDisplayMock.isInTransitionLocked()).thenReturn(false);
|
||||
when(logicalDisplayMock.getBrightnessThrottlingDataIdLocked()).thenReturn(
|
||||
DisplayDeviceConfig.DEFAULT_BRIGHTNESS_THROTTLING_DATA_ID);
|
||||
DisplayDeviceConfig.DEFAULT_ID);
|
||||
when(displayDeviceMock.getDisplayDeviceInfoLocked()).thenReturn(deviceInfo);
|
||||
when(displayDeviceMock.getUniqueId()).thenReturn(uniqueId);
|
||||
when(displayDeviceMock.getDisplayDeviceConfig()).thenReturn(displayDeviceConfigMock);
|
||||
|
||||
@@ -252,7 +252,7 @@ public final class DisplayPowerControllerTest {
|
||||
when(logicalDisplayMock.isEnabledLocked()).thenReturn(isEnabled);
|
||||
when(logicalDisplayMock.isInTransitionLocked()).thenReturn(false);
|
||||
when(logicalDisplayMock.getBrightnessThrottlingDataIdLocked()).thenReturn(
|
||||
DisplayDeviceConfig.DEFAULT_BRIGHTNESS_THROTTLING_DATA_ID);
|
||||
DisplayDeviceConfig.DEFAULT_ID);
|
||||
when(displayDeviceMock.getDisplayDeviceInfoLocked()).thenReturn(deviceInfo);
|
||||
when(displayDeviceMock.getUniqueId()).thenReturn(uniqueId);
|
||||
when(displayDeviceMock.getDisplayDeviceConfig()).thenReturn(displayDeviceConfigMock);
|
||||
|
||||
@@ -157,6 +157,26 @@ public class DeviceStateToLayoutMapTest {
|
||||
assertEquals(testLayout, configLayout);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRefreshRateThermalThrottlingMapId() {
|
||||
Layout configLayout = mDeviceStateToLayoutMap.get(4);
|
||||
|
||||
Layout testLayout = new Layout();
|
||||
Layout.Display display1 = testLayout.createDisplayLocked(
|
||||
DisplayAddress.fromPhysicalDisplayId(345L), /* isDefault= */ true,
|
||||
/* isEnabled= */ true, /* displayGroup= */ null, mDisplayIdProducerMock,
|
||||
/* brightnessThrottlingMapId= */ null,
|
||||
/* leadDisplayId= */ Display.DEFAULT_DISPLAY);
|
||||
display1.setRefreshRateThermalThrottlingMapId("test2");
|
||||
testLayout.createDisplayLocked(
|
||||
DisplayAddress.fromPhysicalDisplayId(678L), /* isDefault= */ false,
|
||||
/* isEnabled= */ true, /* displayGroup= */ null, mDisplayIdProducerMock,
|
||||
/* brightnessThrottlingMapId= */ null,
|
||||
/* leadDisplayId= */ Display.DEFAULT_DISPLAY);
|
||||
|
||||
assertEquals(testLayout, configLayout);
|
||||
}
|
||||
|
||||
////////////////////
|
||||
// Helper Methods //
|
||||
////////////////////
|
||||
@@ -221,6 +241,19 @@ public class DeviceStateToLayoutMapTest {
|
||||
+ "<address>678</address>\n"
|
||||
+ "</display>\n"
|
||||
+ "</layout>\n"
|
||||
|
||||
+ "<layout>\n"
|
||||
+ "<state>4</state> \n"
|
||||
+ "<display enabled=\"true\" defaultDisplay=\"true\" >\n"
|
||||
+ "<address>345</address>\n"
|
||||
+ "<refreshRateThermalThrottlingMapId>"
|
||||
+ "test2"
|
||||
+ "</refreshRateThermalThrottlingMapId>"
|
||||
+ "</display>\n"
|
||||
+ "<display enabled=\"true\">\n"
|
||||
+ "<address>678</address>\n"
|
||||
+ "</display>\n"
|
||||
+ "</layout>\n"
|
||||
+ "</layouts>\n";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,9 @@ import static org.mockito.Mockito.when;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.content.res.TypedArray;
|
||||
import android.os.Temperature;
|
||||
import android.util.SparseArray;
|
||||
import android.view.SurfaceControl;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
@@ -315,6 +318,55 @@ public final class DisplayDeviceConfigTest {
|
||||
// HighBrightnessModeData AmbientLightSensor, RefreshRateLimitations and ProximitySensor.
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRefreshRateThermalThrottlingFromDisplayConfig() throws IOException {
|
||||
setupDisplayDeviceConfigFromDisplayConfigFile();
|
||||
|
||||
SparseArray<SurfaceControl.RefreshRateRange> defaultMap =
|
||||
mDisplayDeviceConfig.getRefreshRateThrottlingData(null);
|
||||
assertNotNull(defaultMap);
|
||||
assertEquals(2, defaultMap.size());
|
||||
assertEquals(30, defaultMap.get(Temperature.THROTTLING_CRITICAL).min, SMALL_DELTA);
|
||||
assertEquals(60, defaultMap.get(Temperature.THROTTLING_CRITICAL).max, SMALL_DELTA);
|
||||
assertEquals(0, defaultMap.get(Temperature.THROTTLING_SHUTDOWN).min, SMALL_DELTA);
|
||||
assertEquals(30, defaultMap.get(Temperature.THROTTLING_SHUTDOWN).max, SMALL_DELTA);
|
||||
|
||||
SparseArray<SurfaceControl.RefreshRateRange> testMap =
|
||||
mDisplayDeviceConfig.getRefreshRateThrottlingData("test");
|
||||
assertNotNull(testMap);
|
||||
assertEquals(1, testMap.size());
|
||||
assertEquals(60, testMap.get(Temperature.THROTTLING_EMERGENCY).min, SMALL_DELTA);
|
||||
assertEquals(90, testMap.get(Temperature.THROTTLING_EMERGENCY).max, SMALL_DELTA);
|
||||
}
|
||||
|
||||
private String getRefreshThermalThrottlingMaps() {
|
||||
return "<refreshRateThrottlingMap>\n"
|
||||
+ " <refreshRateThrottlingPoint>\n"
|
||||
+ " <thermalStatus>critical</thermalStatus>\n"
|
||||
+ " <refreshRateRange>\n"
|
||||
+ " <minimum>30</minimum>\n"
|
||||
+ " <maximum>60</maximum>\n"
|
||||
+ " </refreshRateRange>\n"
|
||||
+ " </refreshRateThrottlingPoint>\n"
|
||||
+ " <refreshRateThrottlingPoint>\n"
|
||||
+ " <thermalStatus>shutdown</thermalStatus>\n"
|
||||
+ " <refreshRateRange>\n"
|
||||
+ " <minimum>0</minimum>\n"
|
||||
+ " <maximum>30</maximum>\n"
|
||||
+ " </refreshRateRange>\n"
|
||||
+ " </refreshRateThrottlingPoint>\n"
|
||||
+ "</refreshRateThrottlingMap>\n"
|
||||
+ "<refreshRateThrottlingMap id=\"test\">\n"
|
||||
+ " <refreshRateThrottlingPoint>\n"
|
||||
+ " <thermalStatus>emergency</thermalStatus>\n"
|
||||
+ " <refreshRateRange>\n"
|
||||
+ " <minimum>60</minimum>\n"
|
||||
+ " <maximum>90</maximum>\n"
|
||||
+ " </refreshRateRange>\n"
|
||||
+ " </refreshRateThrottlingPoint>\n"
|
||||
+ "</refreshRateThrottlingMap>\n";
|
||||
}
|
||||
|
||||
private String getContent() {
|
||||
return "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
|
||||
+ "<displayConfiguration>\n"
|
||||
@@ -557,6 +609,7 @@ public final class DisplayDeviceConfigTest {
|
||||
+ "<brightness>0.0125</brightness>\n"
|
||||
+ "</brightnessThrottlingPoint>\n"
|
||||
+ "</brightnessThrottlingMap>\n"
|
||||
+ getRefreshThermalThrottlingMaps()
|
||||
+ "</thermalThrottling>\n"
|
||||
+ "<refreshRate>\n"
|
||||
+ "<defaultRefreshRate>45</defaultRefreshRate>\n"
|
||||
|
||||
@@ -687,10 +687,10 @@ public class LogicalDisplayMapperTest {
|
||||
assertTrue(mLogicalDisplayMapper.getDisplayLocked(device2).isEnabledLocked());
|
||||
assertFalse(mLogicalDisplayMapper.getDisplayLocked(device1).isInTransitionLocked());
|
||||
assertFalse(mLogicalDisplayMapper.getDisplayLocked(device2).isInTransitionLocked());
|
||||
assertEquals(DisplayDeviceConfig.DEFAULT_BRIGHTNESS_THROTTLING_DATA_ID,
|
||||
assertEquals(DisplayDeviceConfig.DEFAULT_ID,
|
||||
mLogicalDisplayMapper.getDisplayLocked(device1)
|
||||
.getBrightnessThrottlingDataIdLocked());
|
||||
assertEquals(DisplayDeviceConfig.DEFAULT_BRIGHTNESS_THROTTLING_DATA_ID,
|
||||
assertEquals(DisplayDeviceConfig.DEFAULT_ID,
|
||||
mLogicalDisplayMapper.getDisplayLocked(device2)
|
||||
.getBrightnessThrottlingDataIdLocked());
|
||||
|
||||
@@ -700,10 +700,10 @@ public class LogicalDisplayMapperTest {
|
||||
assertTrue(mLogicalDisplayMapper.getDisplayLocked(device2).isEnabledLocked());
|
||||
assertFalse(mLogicalDisplayMapper.getDisplayLocked(device1).isInTransitionLocked());
|
||||
assertFalse(mLogicalDisplayMapper.getDisplayLocked(device2).isInTransitionLocked());
|
||||
assertEquals(DisplayDeviceConfig.DEFAULT_BRIGHTNESS_THROTTLING_DATA_ID,
|
||||
assertEquals(DisplayDeviceConfig.DEFAULT_ID,
|
||||
mLogicalDisplayMapper.getDisplayLocked(device1)
|
||||
.getBrightnessThrottlingDataIdLocked());
|
||||
assertEquals(DisplayDeviceConfig.DEFAULT_BRIGHTNESS_THROTTLING_DATA_ID,
|
||||
assertEquals(DisplayDeviceConfig.DEFAULT_ID,
|
||||
mLogicalDisplayMapper.getDisplayLocked(device2)
|
||||
.getBrightnessThrottlingDataIdLocked());
|
||||
}
|
||||
|
||||
@@ -59,12 +59,12 @@ import android.hardware.SensorManager;
|
||||
import android.hardware.display.BrightnessInfo;
|
||||
import android.hardware.display.DisplayManager;
|
||||
import android.hardware.display.DisplayManager.DisplayListener;
|
||||
import android.hardware.display.DisplayManagerGlobal;
|
||||
import android.hardware.display.DisplayManagerInternal;
|
||||
import android.hardware.display.DisplayManagerInternal.RefreshRateLimitation;
|
||||
import android.hardware.fingerprint.IUdfpsRefreshRateRequestCallback;
|
||||
import android.os.Handler;
|
||||
import android.os.IThermalEventListener;
|
||||
import android.os.IThermalService;
|
||||
import android.os.Looper;
|
||||
import android.os.RemoteException;
|
||||
import android.os.Temperature;
|
||||
@@ -75,6 +75,7 @@ import android.util.Slog;
|
||||
import android.util.SparseArray;
|
||||
import android.util.TypedValue;
|
||||
import android.view.Display;
|
||||
import android.view.DisplayInfo;
|
||||
import android.view.SurfaceControl.RefreshRateRange;
|
||||
import android.view.SurfaceControl.RefreshRateRanges;
|
||||
|
||||
@@ -83,6 +84,7 @@ import androidx.test.filters.SmallTest;
|
||||
|
||||
import com.android.internal.R;
|
||||
import com.android.internal.display.BrightnessSynchronizer;
|
||||
import com.android.internal.os.BackgroundThread;
|
||||
import com.android.internal.util.Preconditions;
|
||||
import com.android.internal.util.test.FakeSettingsProvider;
|
||||
import com.android.internal.util.test.FakeSettingsProviderRule;
|
||||
@@ -140,8 +142,6 @@ public class DisplayModeDirectorTest {
|
||||
public SensorManagerInternal mSensorManagerInternalMock;
|
||||
@Mock
|
||||
public DisplayManagerInternal mDisplayManagerInternalMock;
|
||||
@Mock
|
||||
public IThermalService mThermalServiceMock;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
@@ -150,7 +150,6 @@ public class DisplayModeDirectorTest {
|
||||
final MockContentResolver resolver = mSettingsProviderRule.mockContentResolver(mContext);
|
||||
when(mContext.getContentResolver()).thenReturn(resolver);
|
||||
mInjector = spy(new FakesInjector());
|
||||
when(mInjector.getThermalService()).thenReturn(mThermalServiceMock);
|
||||
mHandler = new Handler(Looper.getMainLooper());
|
||||
|
||||
LocalServices.removeServiceForTest(StatusBarManagerInternal.class);
|
||||
@@ -1773,11 +1772,12 @@ public class DisplayModeDirectorTest {
|
||||
|
||||
ArgumentCaptor<DisplayListener> DisplayCaptor =
|
||||
ArgumentCaptor.forClass(DisplayListener.class);
|
||||
verify(mInjector).registerDisplayListener(DisplayCaptor.capture(), any(Handler.class),
|
||||
verify(mInjector, times(2)).registerDisplayListener(DisplayCaptor.capture(),
|
||||
any(Handler.class),
|
||||
eq(DisplayManager.EVENT_FLAG_DISPLAY_ADDED
|
||||
| DisplayManager.EVENT_FLAG_DISPLAY_CHANGED
|
||||
| DisplayManager.EVENT_FLAG_DISPLAY_REMOVED));
|
||||
DisplayListener displayListener = DisplayCaptor.getValue();
|
||||
DisplayListener displayListener = DisplayCaptor.getAllValues().get(0);
|
||||
|
||||
// Verify that there is no proximity vote initially
|
||||
Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_PROXIMITY);
|
||||
@@ -2236,8 +2236,7 @@ public class DisplayModeDirectorTest {
|
||||
ArgumentCaptor<IThermalEventListener> thermalEventListener =
|
||||
ArgumentCaptor.forClass(IThermalEventListener.class);
|
||||
|
||||
verify(mThermalServiceMock).registerThermalEventListenerWithType(
|
||||
thermalEventListener.capture(), eq(Temperature.TYPE_SKIN));
|
||||
verify(mInjector).registerThermalServiceListener(thermalEventListener.capture());
|
||||
final IThermalEventListener listener = thermalEventListener.getValue();
|
||||
|
||||
// Verify that there is no skin temperature vote initially.
|
||||
@@ -2246,11 +2245,13 @@ public class DisplayModeDirectorTest {
|
||||
|
||||
// Set the skin temperature to critical and verify that we added a vote.
|
||||
listener.notifyThrottling(getSkinTemp(Temperature.THROTTLING_CRITICAL));
|
||||
BackgroundThread.getHandler().runWithScissors(() -> { }, 500 /*timeout*/);
|
||||
vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_SKIN_TEMPERATURE);
|
||||
assertVoteForRenderFrameRateRange(vote, 0f, 60.f);
|
||||
|
||||
// Set the skin temperature to severe and verify that the vote is gone.
|
||||
listener.notifyThrottling(getSkinTemp(Temperature.THROTTLING_SEVERE));
|
||||
BackgroundThread.getHandler().runWithScissors(() -> { }, 500 /*timeout*/);
|
||||
vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_SKIN_TEMPERATURE);
|
||||
assertNull(vote);
|
||||
}
|
||||
@@ -2708,6 +2709,16 @@ public class DisplayModeDirectorTest {
|
||||
@Override
|
||||
public void registerDisplayListener(DisplayListener listener, Handler handler, long flag) {}
|
||||
|
||||
@Override
|
||||
public Display[] getDisplays() {
|
||||
return new Display[] { createDisplay(DISPLAY_ID) };
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getDisplayInfo(int displayId, DisplayInfo displayInfo) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BrightnessInfo getBrightnessInfo(int displayId) {
|
||||
return null;
|
||||
@@ -2719,8 +2730,8 @@ public class DisplayModeDirectorTest {
|
||||
}
|
||||
|
||||
@Override
|
||||
public IThermalService getThermalService() {
|
||||
return null;
|
||||
public boolean registerThermalServiceListener(IThermalEventListener listener) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -2728,6 +2739,11 @@ public class DisplayModeDirectorTest {
|
||||
return true;
|
||||
}
|
||||
|
||||
protected Display createDisplay(int id) {
|
||||
return new Display(DisplayManagerGlobal.getInstance(), id, new DisplayInfo(),
|
||||
ApplicationProvider.getApplicationContext().getResources());
|
||||
}
|
||||
|
||||
void notifyPeakRefreshRateChanged() {
|
||||
if (mPeakRefreshRateObserver != null) {
|
||||
mPeakRefreshRateObserver.dispatchChange(false /*selfChange*/,
|
||||
|
||||
@@ -0,0 +1,247 @@
|
||||
/*
|
||||
* 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 org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
import android.hardware.display.DisplayManager;
|
||||
import android.os.Handler;
|
||||
import android.os.IThermalEventListener;
|
||||
import android.os.Temperature;
|
||||
import android.util.SparseArray;
|
||||
import android.view.Display;
|
||||
import android.view.DisplayInfo;
|
||||
import android.view.SurfaceControl;
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import com.android.server.testutils.TestHandler;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
|
||||
/**
|
||||
* Tests for DisplayModeDirector.SkinThermalStatusObserver. Comply with changes described in
|
||||
* b/266789924
|
||||
*/
|
||||
@SmallTest
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class SkinThermalStatusObserverTest {
|
||||
private static final float FLOAT_TOLERANCE = 0.01f;
|
||||
private static final int DISPLAY_ID = 1;
|
||||
private static final int DISPLAY_ID_OTHER = 2;
|
||||
|
||||
SkinThermalStatusObserver mObserver;
|
||||
|
||||
private RegisteringFakesInjector mInjector = new RegisteringFakesInjector();
|
||||
|
||||
private final TestHandler mHandler = new TestHandler(null);
|
||||
private final FakeVoteStorage mStorage = new FakeVoteStorage();
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mObserver = new SkinThermalStatusObserver(mInjector, mStorage, mHandler);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRegisterListenersOnObserve() {
|
||||
// GIVEN thermal sensor is available
|
||||
assertNull(mInjector.mThermalEventListener);
|
||||
assertNull(mInjector.mDisplayListener);
|
||||
// WHEN observe is called
|
||||
mObserver.observe();
|
||||
// THEN thermal and display listeners are registered
|
||||
assertEquals(mObserver, mInjector.mThermalEventListener);
|
||||
assertEquals(mObserver, mInjector.mDisplayListener);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFailToRegisterThermalListenerOnObserve() {
|
||||
// GIVEN thermal sensor is not available
|
||||
mInjector = new RegisteringFakesInjector(false);
|
||||
mObserver = new SkinThermalStatusObserver(mInjector, mStorage, mHandler);
|
||||
// WHEN observe is called
|
||||
mObserver.observe();
|
||||
// THEN nothing is registered
|
||||
assertNull(mInjector.mThermalEventListener);
|
||||
assertNull(mInjector.mDisplayListener);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotifyWithDefaultVotesForCritical() {
|
||||
// GIVEN 2 displays with no thermalThrottling config
|
||||
mObserver.observe();
|
||||
assertEquals(0, mStorage.mVoteRegistry.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);
|
||||
assertEquals(1, displayVotes.size());
|
||||
|
||||
DisplayModeDirector.Vote vote = displayVotes.get(
|
||||
DisplayModeDirector.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);
|
||||
assertEquals(1, otherDisplayVotes.size());
|
||||
|
||||
vote = otherDisplayVotes.get(DisplayModeDirector.Vote.PRIORITY_SKIN_TEMPERATURE);
|
||||
assertEquals(0, vote.refreshRateRanges.render.min, FLOAT_TOLERANCE);
|
||||
assertEquals(60, vote.refreshRateRanges.render.max, FLOAT_TOLERANCE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotifyWithDefaultVotesChangeFromCriticalToSevere() {
|
||||
// GIVEN 2 displays with no thermalThrottling config AND temperature level CRITICAL
|
||||
mObserver.observe();
|
||||
assertEquals(0, mStorage.mVoteRegistry.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());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotifyWithDefaultVotesForSevere() {
|
||||
// GIVEN 2 displays with no thermalThrottling config
|
||||
mObserver.observe();
|
||||
assertEquals(0, mStorage.mVoteRegistry.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());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotifiesWithConfigVotes() {
|
||||
// GIVEN 2 displays AND one has thermalThrottling config defined
|
||||
SparseArray<SurfaceControl.RefreshRateRange> displayConfig = new SparseArray<>();
|
||||
displayConfig.put(Temperature.THROTTLING_MODERATE,
|
||||
new SurfaceControl.RefreshRateRange(90.0f, 120.0f));
|
||||
SparseArray<SparseArray<SurfaceControl.RefreshRateRange>> config = new SparseArray<>();
|
||||
config.put(DISPLAY_ID, displayConfig);
|
||||
mInjector = new RegisteringFakesInjector(true, config);
|
||||
mObserver = new SkinThermalStatusObserver(mInjector, mStorage, mHandler);
|
||||
mObserver.observe();
|
||||
mObserver.onDisplayChanged(DISPLAY_ID);
|
||||
assertEquals(0, mStorage.mVoteRegistry.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(1, displayVotes.size());
|
||||
DisplayModeDirector.Vote vote = displayVotes.get(
|
||||
DisplayModeDirector.Vote.PRIORITY_SKIN_TEMPERATURE);
|
||||
assertEquals(90, vote.refreshRateRanges.render.min, FLOAT_TOLERANCE);
|
||||
assertEquals(120, vote.refreshRateRanges.render.max, FLOAT_TOLERANCE);
|
||||
}
|
||||
|
||||
private static Temperature createTemperature(@Temperature.ThrottlingStatus int status) {
|
||||
return new Temperature(40.0f, Temperature.TYPE_SKIN, "test_temp", status);
|
||||
}
|
||||
|
||||
|
||||
private static class RegisteringFakesInjector extends DisplayModeDirectorTest.FakesInjector {
|
||||
private IThermalEventListener mThermalEventListener;
|
||||
private DisplayManager.DisplayListener mDisplayListener;
|
||||
|
||||
private final boolean mRegisterThermalListener;
|
||||
private final SparseArray<SparseArray<SurfaceControl.RefreshRateRange>> mOverriddenConfig;
|
||||
|
||||
|
||||
private RegisteringFakesInjector() {
|
||||
this(true);
|
||||
}
|
||||
|
||||
private RegisteringFakesInjector(boolean registerThermalListener) {
|
||||
this(registerThermalListener, new SparseArray<>());
|
||||
}
|
||||
|
||||
private RegisteringFakesInjector(boolean registerThermalListener,
|
||||
SparseArray<SparseArray<SurfaceControl.RefreshRateRange>> overriddenConfig) {
|
||||
mRegisterThermalListener = registerThermalListener;
|
||||
mOverriddenConfig = overriddenConfig;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean registerThermalServiceListener(IThermalEventListener listener) {
|
||||
mThermalEventListener = (mRegisterThermalListener ? listener : null);
|
||||
return mRegisterThermalListener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerDisplayListener(DisplayManager.DisplayListener listener,
|
||||
Handler handler, long flag) {
|
||||
mDisplayListener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Display[] getDisplays() {
|
||||
return new Display[] {createDisplay(DISPLAY_ID), createDisplay(DISPLAY_ID_OTHER)};
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getDisplayInfo(int displayId, DisplayInfo displayInfo) {
|
||||
SparseArray<SurfaceControl.RefreshRateRange> config = mOverriddenConfig.get(displayId);
|
||||
if (config != null) {
|
||||
displayInfo.refreshRateThermalThrottling = config;
|
||||
return true;
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user