Add low-power/thermal checks to highBrightnessMode

Adds an observer to HighBrightnessModeController to monitor the system's
thermal status and ensure HBM is off if the status is above the new
limit defined in the display-device-config file.

Also fixes a bug where AutomaticBrightnessController does not
automatically recalculate a value when HBM mode changes and the ambient
lux does not.

Bug: 179019497
Test: atest HighBrightnessModeControllerTest
Change-Id: I38a805671cee0f63e858b7ec0b6a45ca43df1737
This commit is contained in:
Philip Junker
2021-06-09 20:12:05 +02:00
committed by Santos Cordon
parent 3f32db86fb
commit 8f1a5c14ad
7 changed files with 408 additions and 28 deletions

View File

@@ -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;

View File

@@ -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
+ "} ";
}
}

View File

@@ -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() {

View File

@@ -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));
}
}
}

View File

@@ -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">

View File

@@ -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;

View File

@@ -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);
}
}