summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--services/core/java/com/android/server/display/AutomaticBrightnessController.java12
-rw-r--r--services/core/java/com/android/server/display/DisplayDeviceConfig.java47
-rw-r--r--services/core/java/com/android/server/display/DisplayPowerController.java4
-rw-r--r--services/core/java/com/android/server/display/HighBrightnessModeController.java203
-rw-r--r--services/core/xsd/display-device-config/display-device-config.xsd23
-rw-r--r--services/core/xsd/display-device-config/schema/current.txt15
-rw-r--r--services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java132
7 files changed, 408 insertions, 28 deletions
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index 1a07cb854cae..a4a5f96c7358 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -68,6 +68,7 @@ class AutomaticBrightnessController {
private static final int MSG_INVALIDATE_SHORT_TERM_MODEL = 3;
private static final int MSG_UPDATE_FOREGROUND_APP = 4;
private static final int MSG_UPDATE_FOREGROUND_APP_SYNC = 5;
+ private static final int MSG_RUN_UPDATE = 6;
// Length of the ambient light horizon used to calculate the long term estimate of ambient
// light.
@@ -360,6 +361,13 @@ class AutomaticBrightnessController {
return mBrightnessMapper.getDefaultConfig();
}
+ /**
+ * Force recalculate of the state of automatic brightness.
+ */
+ public void update() {
+ mHandler.sendEmptyMessage(MSG_RUN_UPDATE);
+ }
+
private boolean setDisplayPolicy(int policy) {
if (mDisplayPolicy == policy) {
return false;
@@ -910,6 +918,10 @@ class AutomaticBrightnessController {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
+ case MSG_RUN_UPDATE:
+ updateAutoBrightness(true /*sendUpdate*/, false /*isManuallySet*/);
+ break;
+
case MSG_UPDATE_AMBIENT_LUX:
updateAmbientLux();
break;
diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
index 88f88a8439cc..4c9d0f2691b3 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
@@ -39,6 +39,7 @@ import com.android.server.display.config.NitsMap;
import com.android.server.display.config.Point;
import com.android.server.display.config.RefreshRateRange;
import com.android.server.display.config.SensorDetails;
+import com.android.server.display.config.ThermalStatus;
import com.android.server.display.config.XmlParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -657,6 +658,8 @@ public class DisplayDeviceConfig {
mHbmData.timeWindowMillis = hbmTiming.getTimeWindowSecs_all().longValue() * 1000;
mHbmData.timeMaxMillis = hbmTiming.getTimeMaxSecs_all().longValue() * 1000;
mHbmData.timeMinMillis = hbmTiming.getTimeMinSecs_all().longValue() * 1000;
+ mHbmData.thermalStatusLimit = convertThermalStatus(hbm.getThermalStatusLimit_all());
+ mHbmData.allowInLowPowerMode = hbm.getAllowInLowPowerMode_all();
final RefreshRateRange rr = hbm.getRefreshRate_all();
if (rr != null) {
final float min = rr.getMinimum().floatValue();
@@ -743,6 +746,31 @@ public class DisplayDeviceConfig {
}
}
+ private @PowerManager.ThermalStatus int convertThermalStatus(ThermalStatus value) {
+ if (value == null) {
+ return PowerManager.THERMAL_STATUS_NONE;
+ }
+ switch (value) {
+ case none:
+ return PowerManager.THERMAL_STATUS_NONE;
+ case light:
+ return PowerManager.THERMAL_STATUS_LIGHT;
+ case moderate:
+ return PowerManager.THERMAL_STATUS_MODERATE;
+ case severe:
+ return PowerManager.THERMAL_STATUS_SEVERE;
+ case critical:
+ return PowerManager.THERMAL_STATUS_CRITICAL;
+ case emergency:
+ return PowerManager.THERMAL_STATUS_EMERGENCY;
+ case shutdown:
+ return PowerManager.THERMAL_STATUS_SHUTDOWN;
+ default:
+ Slog.wtf(TAG, "Unexpected Thermal Status: " + value);
+ return PowerManager.THERMAL_STATUS_NONE;
+ }
+ }
+
static class SensorData {
public String type;
public String name;
@@ -781,6 +809,12 @@ public class DisplayDeviceConfig {
/** Brightness level at which we transition from normal to high-brightness. */
public float transitionPoint;
+ /** Enable HBM only if the thermal status is not higher than this. */
+ public @PowerManager.ThermalStatus int thermalStatusLimit;
+
+ /** Whether HBM is allowed when {@code Settings.Global.LOW_POWER_MODE} is active. */
+ public boolean allowInLowPowerMode;
+
/** Time window for HBM. */
public long timeWindowMillis;
@@ -792,13 +826,16 @@ public class DisplayDeviceConfig {
HighBrightnessModeData() {}
- HighBrightnessModeData(float minimumLux, float transitionPoint,
- long timeWindowMillis, long timeMaxMillis, long timeMinMillis) {
+ HighBrightnessModeData(float minimumLux, float transitionPoint, long timeWindowMillis,
+ long timeMaxMillis, long timeMinMillis,
+ @PowerManager.ThermalStatus int thermalStatusLimit, boolean allowInLowPowerMode) {
this.minimumLux = minimumLux;
this.transitionPoint = transitionPoint;
this.timeWindowMillis = timeWindowMillis;
this.timeMaxMillis = timeMaxMillis;
this.timeMinMillis = timeMinMillis;
+ this.thermalStatusLimit = thermalStatusLimit;
+ this.allowInLowPowerMode = allowInLowPowerMode;
}
/**
@@ -807,10 +844,12 @@ public class DisplayDeviceConfig {
*/
public void copyTo(@NonNull HighBrightnessModeData other) {
other.minimumLux = minimumLux;
- other.transitionPoint = transitionPoint;
other.timeWindowMillis = timeWindowMillis;
other.timeMaxMillis = timeMaxMillis;
other.timeMinMillis = timeMinMillis;
+ other.transitionPoint = transitionPoint;
+ other.thermalStatusLimit = thermalStatusLimit;
+ other.allowInLowPowerMode = allowInLowPowerMode;
}
@Override
@@ -821,6 +860,8 @@ public class DisplayDeviceConfig {
+ ", timeWindow: " + timeWindowMillis + "ms"
+ ", timeMax: " + timeMaxMillis + "ms"
+ ", timeMin: " + timeMinMillis + "ms"
+ + ", thermalStatusLimit: " + thermalStatusLimit
+ + ", allowInLowPowerMode: " + allowInLowPowerMode
+ "} ";
}
}
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 555add4b027f..8396f664146d 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -1518,7 +1518,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
() -> {
sendUpdatePowerStateLocked();
mHandler.post(mOnBrightnessChangeRunnable);
- });
+ // TODO(b/192258832): Switch the HBMChangeCallback to a listener pattern.
+ mAutomaticBrightnessController.update();
+ }, mContext);
}
private void blockScreenOn() {
diff --git a/services/core/java/com/android/server/display/HighBrightnessModeController.java b/services/core/java/com/android/server/display/HighBrightnessModeController.java
index 57a8c4b998ae..d6294223556d 100644
--- a/services/core/java/com/android/server/display/HighBrightnessModeController.java
+++ b/services/core/java/com/android/server/display/HighBrightnessModeController.java
@@ -16,11 +16,21 @@
package com.android.server.display;
+import android.content.Context;
+import android.database.ContentObserver;
import android.hardware.display.BrightnessInfo;
+import android.net.Uri;
import android.os.Handler;
import android.os.IBinder;
+import android.os.IThermalEventListener;
+import android.os.IThermalService;
import android.os.PowerManager;
+import android.os.RemoteException;
+import android.os.ServiceManager;
import android.os.SystemClock;
+import android.os.Temperature;
+import android.os.UserHandle;
+import android.provider.Settings;
import android.util.Slog;
import android.util.TimeUtils;
import android.view.SurfaceControlHdrLayerInfoListener;
@@ -52,6 +62,10 @@ class HighBrightnessModeController {
private final Runnable mHbmChangeCallback;
private final Runnable mRecalcRunnable;
private final Clock mClock;
+ private final SkinThermalStatusObserver mSkinThermalStatusObserver;
+ private final Context mContext;
+ private final SettingsObserver mSettingsObserver;
+ private final Injector mInjector;
private SurfaceControlHdrLayerInfoListener mHdrListener;
private HighBrightnessModeData mHbmData;
@@ -63,6 +77,8 @@ class HighBrightnessModeController {
private float mAutoBrightness;
private int mHbmMode = BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF;
private boolean mIsHdrLayerPresent = false;
+ private boolean mIsThermalStatusWithinLimit = true;
+ private boolean mIsBlockedByLowPowerMode = false;
/**
* If HBM is currently running, this is the start time for the current HBM session.
@@ -72,29 +88,33 @@ class HighBrightnessModeController {
/**
* List of previous HBM-events ordered from most recent to least recent.
* Meant to store only the events that fall into the most recent
- * {@link mHbmData.timeWindowSecs}.
+ * {@link mHbmData.timeWindowMillis}.
*/
private LinkedList<HbmEvent> mEvents = new LinkedList<>();
HighBrightnessModeController(Handler handler, IBinder displayToken, float brightnessMin,
- float brightnessMax, HighBrightnessModeData hbmData, Runnable hbmChangeCallback) {
- this(SystemClock::uptimeMillis, handler, displayToken, brightnessMin, brightnessMax,
- hbmData, hbmChangeCallback);
+ float brightnessMax, HighBrightnessModeData hbmData, Runnable hbmChangeCallback,
+ Context context) {
+ this(new Injector(), handler, displayToken, brightnessMin, brightnessMax,
+ hbmData, hbmChangeCallback, context);
}
@VisibleForTesting
- HighBrightnessModeController(Clock clock, Handler handler, IBinder displayToken,
+ HighBrightnessModeController(Injector injector, Handler handler, IBinder displayToken,
float brightnessMin, float brightnessMax, HighBrightnessModeData hbmData,
- Runnable hbmChangeCallback) {
- mClock = clock;
+ Runnable hbmChangeCallback, Context context) {
+ mInjector = injector;
+ mClock = injector.getClock();
mHandler = handler;
mBrightnessMin = brightnessMin;
mBrightnessMax = brightnessMax;
mHbmChangeCallback = hbmChangeCallback;
+ mContext = context;
mAutoBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
mRecalcRunnable = this::recalculateTimeAllowance;
mHdrListener = new HdrListener();
-
+ mSkinThermalStatusObserver = new SkinThermalStatusObserver(mInjector, mHandler);
+ mSettingsObserver = new SettingsObserver(mHandler);
resetHbmData(displayToken, hbmData);
}
@@ -178,14 +198,26 @@ class HighBrightnessModeController {
void stop() {
registerHdrListener(null /*displayToken*/);
+ mSkinThermalStatusObserver.stopObserving();
+ mSettingsObserver.stopObserving();
}
void resetHbmData(IBinder displayToken, HighBrightnessModeData hbmData) {
mHbmData = hbmData;
unregisterHdrListener();
+ mSkinThermalStatusObserver.stopObserving();
+ mSettingsObserver.stopObserving();
if (deviceSupportsHbm()) {
registerHdrListener(displayToken);
recalculateTimeAllowance();
+ if (mHbmData.thermalStatusLimit > PowerManager.THERMAL_STATUS_NONE) {
+ mIsThermalStatusWithinLimit = true;
+ mSkinThermalStatusObserver.startObserving();
+ }
+ if (!mHbmData.allowInLowPowerMode) {
+ mIsBlockedByLowPowerMode = false;
+ mSettingsObserver.startObserving();
+ }
}
}
@@ -208,6 +240,8 @@ class HighBrightnessModeController {
pw.println(" mBrightnessMin=" + mBrightnessMin);
pw.println(" mBrightnessMax=" + mBrightnessMax);
pw.println(" mRunningStartTimeMillis=" + TimeUtils.formatUptime(mRunningStartTimeMillis));
+ pw.println(" mIsThermalStatusWithinLimit=" + mIsThermalStatusWithinLimit);
+ pw.println(" mIsBlockedByLowPowerMode=" + mIsBlockedByLowPowerMode);
pw.println(" mEvents=");
final long currentTime = mClock.uptimeMillis();
long lastStartTime = currentTime;
@@ -221,6 +255,8 @@ class HighBrightnessModeController {
}
lastStartTime = dumpHbmEvent(pw, event);
}
+
+ mSkinThermalStatusObserver.dump(pw);
}
private long dumpHbmEvent(PrintWriter pw, HbmEvent event) {
@@ -234,7 +270,8 @@ class HighBrightnessModeController {
private boolean isCurrentlyAllowed() {
return mIsHdrLayerPresent
- || (mIsAutoBrightnessEnabled && mIsTimeAvailable && mIsInAllowedAmbientRange);
+ || (mIsAutoBrightnessEnabled && mIsTimeAvailable && mIsInAllowedAmbientRange
+ && mIsThermalStatusWithinLimit && !mIsBlockedByLowPowerMode);
}
private boolean deviceSupportsHbm() {
@@ -327,6 +364,12 @@ class HighBrightnessModeController {
+ ", remainingAllowedTime: " + remainingTime
+ ", isLuxHigh: " + mIsInAllowedAmbientRange
+ ", isHBMCurrentlyAllowed: " + isCurrentlyAllowed()
+ + ", isHdrLayerPresent: " + mIsHdrLayerPresent
+ + ", isAutoBrightnessEnabled: " + mIsAutoBrightnessEnabled
+ + ", mIsTimeAvailable: " + mIsTimeAvailable
+ + ", mIsInAllowedAmbientRange: " + mIsInAllowedAmbientRange
+ + ", mIsThermalStatusWithinLimit: " + mIsThermalStatusWithinLimit
+ + ", mIsBlockedByLowPowerMode: " + mIsBlockedByLowPowerMode
+ ", brightness: " + mAutoBrightness
+ ", RunningStartTimeMillis: " + mRunningStartTimeMillis
+ ", nextTimeout: " + (nextTimeout != -1 ? (nextTimeout - currentTime) : -1)
@@ -337,8 +380,11 @@ class HighBrightnessModeController {
mHandler.removeCallbacks(mRecalcRunnable);
mHandler.postAtTime(mRecalcRunnable, nextTimeout + 1);
}
-
// Update the state of the world
+ updateHbmMode();
+ }
+
+ private void updateHbmMode() {
int newHbmMode = calculateHighBrightnessMode();
if (mHbmMode != newHbmMode) {
mHbmMode = newHbmMode;
@@ -409,4 +455,141 @@ class HighBrightnessModeController {
});
}
}
+
+ private final class SkinThermalStatusObserver extends IThermalEventListener.Stub {
+ private final Injector mInjector;
+ private final Handler mHandler;
+
+ private IThermalService mThermalService;
+ private boolean mStarted;
+
+ SkinThermalStatusObserver(Injector injector, Handler handler) {
+ mInjector = injector;
+ mHandler = handler;
+ }
+
+ @Override
+ public void notifyThrottling(Temperature temp) {
+ if (DEBUG) {
+ Slog.d(TAG, "New thermal throttling status "
+ + ", current thermal status = " + temp.getStatus()
+ + ", threshold = " + mHbmData.thermalStatusLimit);
+ }
+ mHandler.post(() -> {
+ mIsThermalStatusWithinLimit = temp.getStatus() <= mHbmData.thermalStatusLimit;
+ // This recalculates HbmMode and runs mHbmChangeCallback if the mode has changed
+ updateHbmMode();
+ });
+ }
+
+ void startObserving() {
+ if (mStarted) {
+ if (DEBUG) {
+ Slog.d(TAG, "Thermal status observer already started");
+ }
+ return;
+ }
+ mThermalService = mInjector.getThermalService();
+ if (mThermalService == null) {
+ Slog.w(TAG, "Could not observe thermal status. Service not available");
+ return;
+ }
+ try {
+ // We get a callback immediately upon registering so there's no need to query
+ // for the current value.
+ mThermalService.registerThermalEventListenerWithType(this, Temperature.TYPE_SKIN);
+ mStarted = true;
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to register thermal status listener", e);
+ }
+ }
+
+ void stopObserving() {
+ mIsThermalStatusWithinLimit = true;
+ if (!mStarted) {
+ if (DEBUG) {
+ Slog.d(TAG, "Stop skipped because thermal status observer not started");
+ }
+ return;
+ }
+ try {
+ mThermalService.unregisterThermalEventListener(this);
+ mStarted = false;
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to unregister thermal status listener", e);
+ }
+ mThermalService = null;
+ }
+
+ void dump(PrintWriter writer) {
+ writer.println(" SkinThermalStatusObserver:");
+ writer.println(" mStarted: " + mStarted);
+ if (mThermalService != null) {
+ writer.println(" ThermalService available");
+ } else {
+ writer.println(" ThermalService not available");
+ }
+ }
+ }
+
+ private final class SettingsObserver extends ContentObserver {
+ private final Uri mLowPowerModeSetting = Settings.Global.getUriFor(
+ Settings.Global.LOW_POWER_MODE);
+ private boolean mStarted;
+
+ SettingsObserver(Handler handler) {
+ super(handler);
+ }
+
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ updateLowPower();
+ }
+
+ void startObserving() {
+ if (!mStarted) {
+ mContext.getContentResolver().registerContentObserver(mLowPowerModeSetting,
+ false /*notifyForDescendants*/, this, UserHandle.USER_ALL);
+ mStarted = true;
+ updateLowPower();
+ }
+ }
+
+ void stopObserving() {
+ mIsBlockedByLowPowerMode = false;
+ if (mStarted) {
+ mContext.getContentResolver().unregisterContentObserver(this);
+ mStarted = false;
+ }
+ }
+
+ private void updateLowPower() {
+ final boolean isLowPowerMode = isLowPowerMode();
+ if (isLowPowerMode == mIsBlockedByLowPowerMode) {
+ return;
+ }
+ if (DEBUG) {
+ Slog.d(TAG, "Settings.Global.LOW_POWER_MODE enabled: " + isLowPowerMode);
+ }
+ mIsBlockedByLowPowerMode = isLowPowerMode;
+ // this recalculates HbmMode and runs mHbmChangeCallback if the mode has changed
+ updateHbmMode();
+ }
+
+ private boolean isLowPowerMode() {
+ return Settings.Global.getInt(
+ mContext.getContentResolver(), Settings.Global.LOW_POWER_MODE, 0) != 0;
+ }
+ }
+
+ public static class Injector {
+ public Clock getClock() {
+ return SystemClock::uptimeMillis;
+ }
+
+ public IThermalService getThermalService() {
+ return IThermalService.Stub.asInterface(
+ ServiceManager.getService(Context.THERMAL_SERVICE));
+ }
+ }
}
diff --git a/services/core/xsd/display-device-config/display-device-config.xsd b/services/core/xsd/display-device-config/display-device-config.xsd
index 82aaa61527d1..429edf175be4 100644
--- a/services/core/xsd/display-device-config/display-device-config.xsd
+++ b/services/core/xsd/display-device-config/display-device-config.xsd
@@ -81,6 +81,16 @@
<xs:annotation name="nullable"/>
<xs:annotation name="final"/>
</xs:element>
+ <!-- The highest (most severe) thermal status at which high-brightness-mode is allowed
+ to operate. -->
+ <xs:element name="thermalStatusLimit" type="thermalStatus" minOccurs="0" maxOccurs="1">
+ <xs:annotation name="nonnull"/>
+ <xs:annotation name="final"/>
+ </xs:element>
+ <xs:element name="allowInLowPowerMode" type="xs:boolean" minOccurs="0" maxOccurs="1">
+ <xs:annotation name="nonnull"/>
+ <xs:annotation name="final"/>
+ </xs:element>
</xs:all>
<xs:attribute name="enabled" type="xs:boolean" use="optional"/>
</xs:complexType>
@@ -102,6 +112,19 @@
</xs:all>
</xs:complexType>
+ <!-- Maps to PowerManager.THERMAL_STATUS_* values. -->
+ <xs:simpleType name="thermalStatus">
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="none"/>
+ <xs:enumeration value="light"/>
+ <xs:enumeration value="moderate"/>
+ <xs:enumeration value="severe"/>
+ <xs:enumeration value="critical"/>
+ <xs:enumeration value="emergency"/>
+ <xs:enumeration value="shutdown"/>
+ </xs:restriction>
+ </xs:simpleType>
+
<xs:complexType name="nitsMap">
<xs:sequence>
<xs:element name="point" type="point" maxOccurs="unbounded" minOccurs="2">
diff --git a/services/core/xsd/display-device-config/schema/current.txt b/services/core/xsd/display-device-config/schema/current.txt
index 6e2e3625f60c..ad186026d30c 100644
--- a/services/core/xsd/display-device-config/schema/current.txt
+++ b/services/core/xsd/display-device-config/schema/current.txt
@@ -42,14 +42,18 @@ package com.android.server.display.config {
public class HighBrightnessMode {
ctor public HighBrightnessMode();
+ method @NonNull public final boolean getAllowInLowPowerMode_all();
method public boolean getEnabled();
method @NonNull public final java.math.BigDecimal getMinimumLux_all();
method @Nullable public final com.android.server.display.config.RefreshRateRange getRefreshRate_all();
+ method @NonNull public final com.android.server.display.config.ThermalStatus getThermalStatusLimit_all();
method public com.android.server.display.config.HbmTiming getTiming_all();
method @NonNull public final java.math.BigDecimal getTransitionPoint_all();
+ method public final void setAllowInLowPowerMode_all(@NonNull boolean);
method public void setEnabled(boolean);
method public final void setMinimumLux_all(@NonNull java.math.BigDecimal);
method public final void setRefreshRate_all(@Nullable com.android.server.display.config.RefreshRateRange);
+ method public final void setThermalStatusLimit_all(@NonNull com.android.server.display.config.ThermalStatus);
method public void setTiming_all(com.android.server.display.config.HbmTiming);
method public final void setTransitionPoint_all(@NonNull java.math.BigDecimal);
}
@@ -85,6 +89,17 @@ package com.android.server.display.config {
method public final void setType(@Nullable String);
}
+ public enum ThermalStatus {
+ method public String getRawName();
+ enum_constant public static final com.android.server.display.config.ThermalStatus critical;
+ enum_constant public static final com.android.server.display.config.ThermalStatus emergency;
+ enum_constant public static final com.android.server.display.config.ThermalStatus light;
+ enum_constant public static final com.android.server.display.config.ThermalStatus moderate;
+ enum_constant public static final com.android.server.display.config.ThermalStatus none;
+ enum_constant public static final com.android.server.display.config.ThermalStatus severe;
+ enum_constant public static final com.android.server.display.config.ThermalStatus shutdown;
+ }
+
public class XmlParser {
ctor public XmlParser();
method public static com.android.server.display.config.DisplayConfiguration read(java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException;
diff --git a/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java b/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java
index 8e4cdc91d0e6..fbcf53d3bd4a 100644
--- a/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java
@@ -20,22 +20,43 @@ import static android.hardware.display.BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF;
import static android.hardware.display.BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT;
import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import android.content.Context;
+import android.content.ContextWrapper;
import android.os.Binder;
import android.os.Handler;
+import android.os.IThermalEventListener;
+import android.os.IThermalService;
import android.os.Message;
+import android.os.PowerManager;
+import android.os.Temperature;
+import android.os.Temperature.ThrottlingStatus;
import android.os.test.TestLooper;
import android.platform.test.annotations.Presubmit;
+import android.test.mock.MockContentResolver;
+import androidx.test.core.app.ApplicationProvider;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.internal.util.test.FakeSettingsProvider;
+import com.android.internal.util.test.FakeSettingsProviderRule;
import com.android.server.display.DisplayDeviceConfig.HighBrightnessModeData;
+import com.android.server.display.HighBrightnessModeController.Injector;
import com.android.server.testutils.OffsettableClock;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
@SmallTest
@Presubmit
@@ -47,6 +68,8 @@ public class HighBrightnessModeControllerTest {
private static final long TIME_WINDOW_MILLIS = 55 * 1000;
private static final long TIME_ALLOWED_IN_WINDOW_MILLIS = 12 * 1000;
private static final long TIME_MINIMUM_AVAILABLE_TO_ENABLE_MILLIS = 5 * 1000;
+ private static final int THERMAL_STATUS_LIMIT = PowerManager.THERMAL_STATUS_SEVERE;
+ private static final boolean ALLOW_IN_LOW_POWER_MODE = false;
private static final float DEFAULT_MIN = 0.01f;
private static final float DEFAULT_MAX = 0.80f;
@@ -57,22 +80,30 @@ public class HighBrightnessModeControllerTest {
private TestLooper mTestLooper;
private Handler mHandler;
private Binder mDisplayToken;
+ private Context mContextSpy;
+
+ @Rule
+ public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule();
+
+ @Mock IThermalService mThermalServiceMock;
+ @Mock Injector mInjectorMock;
+
+ @Captor ArgumentCaptor<IThermalEventListener> mThermalEventListenerCaptor;
private static final HighBrightnessModeData DEFAULT_HBM_DATA =
new HighBrightnessModeData(MINIMUM_LUX, TRANSITION_POINT, TIME_WINDOW_MILLIS,
- TIME_ALLOWED_IN_WINDOW_MILLIS, TIME_MINIMUM_AVAILABLE_TO_ENABLE_MILLIS);
+ TIME_ALLOWED_IN_WINDOW_MILLIS, TIME_MINIMUM_AVAILABLE_TO_ENABLE_MILLIS,
+ THERMAL_STATUS_LIMIT, ALLOW_IN_LOW_POWER_MODE);
@Before
public void setUp() {
- mClock = new OffsettableClock.Stopped();
- mTestLooper = new TestLooper(mClock::now);
+ MockitoAnnotations.initMocks(this);
mDisplayToken = null;
- mHandler = new Handler(mTestLooper.getLooper(), new Handler.Callback() {
- @Override
- public boolean handleMessage(Message msg) {
- return true;
- }
- });
+ mContextSpy = spy(new ContextWrapper(ApplicationProvider.getApplicationContext()));
+ final MockContentResolver resolver = mSettingsProviderRule.mockContentResolver(mContextSpy);
+ when(mContextSpy.getContentResolver()).thenReturn(resolver);
+
+ when(mInjectorMock.getThermalService()).thenReturn(mThermalServiceMock);
}
/////////////////
@@ -81,15 +112,19 @@ public class HighBrightnessModeControllerTest {
@Test
public void testNoHbmData() {
+ initHandler(null);
final HighBrightnessModeController hbmc = new HighBrightnessModeController(
- mClock::now, mHandler, mDisplayToken, DEFAULT_MIN, DEFAULT_MAX, null, () -> {});
+ mInjectorMock, mHandler, mDisplayToken, DEFAULT_MIN, DEFAULT_MAX, null,
+ () -> {}, mContextSpy);
assertState(hbmc, DEFAULT_MIN, DEFAULT_MAX, HIGH_BRIGHTNESS_MODE_OFF);
}
@Test
public void testNoHbmData_Enabled() {
+ initHandler(null);
final HighBrightnessModeController hbmc = new HighBrightnessModeController(
- mClock::now, mHandler, mDisplayToken, DEFAULT_MIN, DEFAULT_MAX, null, () -> {});
+ mInjectorMock, mHandler, mDisplayToken, DEFAULT_MIN, DEFAULT_MAX, null,
+ () -> {}, mContextSpy);
hbmc.setAutoBrightnessEnabled(true);
hbmc.onAmbientLuxChange(MINIMUM_LUX - 1); // below allowed range
assertState(hbmc, DEFAULT_MIN, DEFAULT_MAX, HIGH_BRIGHTNESS_MODE_OFF);
@@ -258,6 +293,54 @@ public class HighBrightnessModeControllerTest {
assertState(hbmc, DEFAULT_MIN, TRANSITION_POINT, HIGH_BRIGHTNESS_MODE_OFF);
}
+ @Test
+ public void testNoHbmInHighThermalState() throws Exception {
+ final HighBrightnessModeController hbmc = createDefaultHbm(new OffsettableClock());
+
+ verify(mThermalServiceMock).registerThermalEventListenerWithType(
+ mThermalEventListenerCaptor.capture(), eq(Temperature.TYPE_SKIN));
+ final IThermalEventListener listener = mThermalEventListenerCaptor.getValue();
+
+ // Set the thermal status too high.
+ listener.notifyThrottling(getSkinTemp(Temperature.THROTTLING_CRITICAL));
+
+ // Try to go into HBM mode but fail
+ hbmc.setAutoBrightnessEnabled(true);
+ hbmc.onAmbientLuxChange(MINIMUM_LUX + 1);
+ advanceTime(10);
+
+ assertEquals(HIGH_BRIGHTNESS_MODE_OFF, hbmc.getHighBrightnessMode());
+ }
+
+ @Test
+ public void testHbmTurnsOffInHighThermalState() throws Exception {
+ final HighBrightnessModeController hbmc = createDefaultHbm(new OffsettableClock());
+
+ verify(mThermalServiceMock).registerThermalEventListenerWithType(
+ mThermalEventListenerCaptor.capture(), eq(Temperature.TYPE_SKIN));
+ final IThermalEventListener listener = mThermalEventListenerCaptor.getValue();
+
+ // Set the thermal status tolerable
+ listener.notifyThrottling(getSkinTemp(Temperature.THROTTLING_LIGHT));
+
+ // Try to go into HBM mode
+ hbmc.setAutoBrightnessEnabled(true);
+ hbmc.onAmbientLuxChange(MINIMUM_LUX + 1);
+ advanceTime(1);
+
+ assertEquals(HIGH_BRIGHTNESS_MODE_SUNLIGHT, hbmc.getHighBrightnessMode());
+
+ // Set the thermal status too high and verify we're off.
+ listener.notifyThrottling(getSkinTemp(Temperature.THROTTLING_CRITICAL));
+ advanceTime(10);
+ assertEquals(HIGH_BRIGHTNESS_MODE_OFF, hbmc.getHighBrightnessMode());
+
+ // Set the thermal status low again and verify we're back on.
+ listener.notifyThrottling(getSkinTemp(Temperature.THROTTLING_SEVERE));
+ advanceTime(1);
+ assertEquals(HIGH_BRIGHTNESS_MODE_SUNLIGHT, hbmc.getHighBrightnessMode());
+ }
+
private void assertState(HighBrightnessModeController hbmc,
float brightnessMin, float brightnessMax, int hbmMode) {
assertEquals(brightnessMin, hbmc.getCurrentBrightnessMin(), EPSILON);
@@ -265,14 +348,35 @@ public class HighBrightnessModeControllerTest {
assertEquals(hbmMode, hbmc.getHighBrightnessMode());
}
- // Creates instance with standard initialization values.
private HighBrightnessModeController createDefaultHbm() {
- return new HighBrightnessModeController(mClock::now, mHandler, mDisplayToken, DEFAULT_MIN,
- DEFAULT_MAX, DEFAULT_HBM_DATA, () -> {});
+ return createDefaultHbm(null);
+ }
+
+ // Creates instance with standard initialization values.
+ private HighBrightnessModeController createDefaultHbm(OffsettableClock clock) {
+ initHandler(clock);
+ return new HighBrightnessModeController(mInjectorMock, mHandler, mDisplayToken, DEFAULT_MIN,
+ DEFAULT_MAX, DEFAULT_HBM_DATA, () -> {}, mContextSpy);
+ }
+
+ private void initHandler(OffsettableClock clock) {
+ mClock = clock != null ? clock : new OffsettableClock.Stopped();
+ when(mInjectorMock.getClock()).thenReturn(mClock::now);
+ mTestLooper = new TestLooper(mClock::now);
+ mHandler = new Handler(mTestLooper.getLooper(), new Handler.Callback() {
+ @Override
+ public boolean handleMessage(Message msg) {
+ return true;
+ }
+ });
}
private void advanceTime(long timeMs) {
mClock.fastForward(timeMs);
mTestLooper.dispatchAll();
}
+
+ private Temperature getSkinTemp(@ThrottlingStatus int status) {
+ return new Temperature(30.0f, Temperature.TYPE_SKIN, "test_skin_temp", status);
+ }
}