diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java index c04032fede2fd..ffed68ed7d38c 100644 --- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java +++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java @@ -54,6 +54,10 @@ class AutomaticBrightnessController { private static final boolean DEBUG_PRETEND_LIGHT_SENSOR_ABSENT = false; + public static final int AUTO_BRIGHTNESS_ENABLED = 1; + public static final int AUTO_BRIGHTNESS_DISABLED = 2; + public static final int AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE = 3; + // How long the current sensor reading is assumed to be valid beyond the current time. // This provides a bit of prediction, as well as ensures that the weight for the last sample is // non-zero, which in turn ensures that the total weight is non-zero. @@ -214,6 +218,7 @@ class AutomaticBrightnessController { private IActivityTaskManager mActivityTaskManager; private PackageManager mPackageManager; private Context mContext; + private int mState = AUTO_BRIGHTNESS_DISABLED; private final Injector mInjector; @@ -331,10 +336,11 @@ class AutomaticBrightnessController { return mCurrentBrightnessMapper.getAutoBrightnessAdjustment(); } - public void configure(boolean enable, @Nullable BrightnessConfiguration configuration, + public void configure(int state, @Nullable BrightnessConfiguration configuration, float brightness, boolean userChangedBrightness, float adjustment, boolean userChangedAutoBrightnessAdjustment, int displayPolicy) { - mHbmController.setAutoBrightnessEnabled(enable); + mState = state; + mHbmController.setAutoBrightnessEnabled(mState); // While dozing, the application processor may be suspended which will prevent us from // receiving new information from the light sensor. On some devices, we may be able to // switch to a wake-up light sensor instead but for now we will simply disable the sensor @@ -346,6 +352,7 @@ class AutomaticBrightnessController { if (userChangedAutoBrightnessAdjustment) { changed |= setAutoBrightnessAdjustment(adjustment); } + final boolean enable = mState == AUTO_BRIGHTNESS_ENABLED; if (userChangedBrightness && enable) { // Update the brightness curve with the new user control point. It's critical this // happens after we update the autobrightness adjustment since it may reset it. @@ -459,6 +466,7 @@ class AutomaticBrightnessController { public void dump(PrintWriter pw) { pw.println(); pw.println("Automatic Brightness Controller Configuration:"); + pw.println(" mState=" + configStateToString(mState)); pw.println(" mScreenBrightnessRangeMinimum=" + mScreenBrightnessRangeMinimum); pw.println(" mScreenBrightnessRangeMaximum=" + mScreenBrightnessRangeMaximum); pw.println(" mDozeScaleFactor=" + mDozeScaleFactor); @@ -520,6 +528,19 @@ class AutomaticBrightnessController { mScreenBrightnessThresholds.dump(pw); } + private String configStateToString(int state) { + switch (state) { + case AUTO_BRIGHTNESS_ENABLED: + return "AUTO_BRIGHTNESS_ENABLED"; + case AUTO_BRIGHTNESS_DISABLED: + return "AUTO_BRIGHTNESS_DISABLED"; + case AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE: + return "AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE"; + default: + return String.valueOf(state); + } + } + private boolean setLightSensorEnabled(boolean enable) { if (enable) { if (!mLightSensorEnabled) { diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java index c4d02c7abafb1..faf0038b29c8d 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController.java +++ b/services/core/java/com/android/server/display/DisplayPowerController.java @@ -62,6 +62,7 @@ import com.android.internal.app.IBatteryStats; import com.android.internal.display.BrightnessSynchronizer; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; +import com.android.internal.util.FrameworkStatsLog; import com.android.server.LocalServices; import com.android.server.am.BatteryStatsService; import com.android.server.display.RampAnimator.DualRampAnimator; @@ -127,6 +128,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call private static final int MSG_STOP = 9; private static final int MSG_UPDATE_BRIGHTNESS = 10; private static final int MSG_UPDATE_RBC = 11; + private static final int MSG_STATSD_HBM_BRIGHTNESS = 12; private static final int PROXIMITY_UNKNOWN = -1; private static final int PROXIMITY_NEGATIVE = 0; @@ -136,6 +138,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call private static final int PROXIMITY_SENSOR_POSITIVE_DEBOUNCE_DELAY = 0; private static final int PROXIMITY_SENSOR_NEGATIVE_DEBOUNCE_DELAY = 250; + private static final int BRIGHTNESS_CHANGE_STATSD_REPORT_INTERVAL_MS = 500; + // Trigger proximity if distance is less than 5 cm. private static final float TYPICAL_PROXIMITY_THRESHOLD = 5.0f; @@ -356,6 +360,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call private float mBrightnessRampRateSlowDecrease; private float mBrightnessRampRateSlowIncrease; + // Report HBM brightness change to StatsD + private int mDisplayStatsId; + private float mLastStatsBrightness = PowerManager.BRIGHTNESS_MIN; // Whether or not to skip the initial brightness ramps into STATE_ON. private final boolean mSkipScreenOnBrightnessRamp; @@ -466,6 +473,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call TAG = "DisplayPowerController[" + mDisplayId + "]"; mDisplayDevice = mLogicalDisplay.getPrimaryDisplayDeviceLocked(); mUniqueDisplayId = logicalDisplay.getPrimaryDisplayDeviceLocked().getUniqueId(); + mDisplayStatsId = mUniqueDisplayId.hashCode(); mHandler = new DisplayControllerHandler(handler.getLooper()); if (mDisplayId == Display.DEFAULT_DISPLAY) { @@ -763,6 +771,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call if (mDisplayDevice != device) { mDisplayDevice = device; mUniqueDisplayId = uniqueId; + mDisplayStatsId = mUniqueDisplayId.hashCode(); mDisplayDeviceConfig = config; loadFromDisplayDeviceConfig(token, info); updatePowerState(); @@ -816,7 +825,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call loadNitsRange(mContext.getResources()); setUpAutoBrightness(mContext.getResources(), mHandler); reloadReduceBrightColours(); - mHbmController.resetHbmData(info.width, info.height, token, + mHbmController.resetHbmData(info.width, info.height, token, info.uniqueId, mDisplayDeviceConfig.getHighBrightnessModeData()); } @@ -1004,7 +1013,15 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } }; - private final RampAnimator.Listener mRampAnimatorListener = this::sendUpdatePowerState; + private final RampAnimator.Listener mRampAnimatorListener = new RampAnimator.Listener() { + @Override + public void onAnimationEnd() { + sendUpdatePowerState(); + + final float brightness = mPowerState.getScreenBrightness(); + reportStats(brightness); + } + }; /** Clean up all resources that are accessed via the {@link #mHandler} thread. */ private void cleanupHandlerThreadAfterStop() { @@ -1179,6 +1196,13 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call && (state == Display.STATE_ON || autoBrightnessEnabledInDoze) && Float.isNaN(brightnessState) && mAutomaticBrightnessController != null; + final boolean autoBrightnessDisabledDueToDisplayOff = mPowerRequest.useAutoBrightness + && !(state == Display.STATE_ON || autoBrightnessEnabledInDoze); + final int autoBrightnessState = autoBrightnessEnabled + ? AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED + : autoBrightnessDisabledDueToDisplayOff + ? AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE + : AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED; final boolean userSetBrightnessChanged = updateUserSetScreenBrightness(); @@ -1229,7 +1253,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // Configure auto-brightness. if (mAutomaticBrightnessController != null) { hadUserBrightnessPoint = mAutomaticBrightnessController.hasUserDataPoints(); - mAutomaticBrightnessController.configure(autoBrightnessEnabled, + mAutomaticBrightnessController.configure(autoBrightnessState, mBrightnessConfiguration, mLastUserSetScreenBrightness, userSetBrightnessChanged, autoBrightnessAdjustment, @@ -1626,11 +1650,13 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call final DisplayDeviceConfig ddConfig = device.getDisplayDeviceConfig(); final IBinder displayToken = mLogicalDisplay.getPrimaryDisplayDeviceLocked().getDisplayTokenLocked(); + final String displayUniqueId = + mLogicalDisplay.getPrimaryDisplayDeviceLocked().getUniqueId(); final DisplayDeviceConfig.HighBrightnessModeData hbmData = ddConfig != null ? ddConfig.getHighBrightnessModeData() : null; final DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked(); return new HighBrightnessModeController(mHandler, info.width, info.height, displayToken, - PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX, hbmData, + displayUniqueId, PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX, hbmData, () -> { sendUpdatePowerStateLocked(); postBrightnessChangeRunnable(); @@ -2462,6 +2488,42 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } } + private void reportStats(float brightness) { + float hbmTransitionPoint = PowerManager.BRIGHTNESS_MAX; + synchronized(mCachedBrightnessInfo) { + if (mCachedBrightnessInfo.hbmTransitionPoint == null) { + return; + } + hbmTransitionPoint = mCachedBrightnessInfo.hbmTransitionPoint.value; + } + + final boolean aboveTransition = brightness > hbmTransitionPoint; + final boolean oldAboveTransition = mLastStatsBrightness > hbmTransitionPoint; + + if (aboveTransition || oldAboveTransition) { + mLastStatsBrightness = brightness; + mHandler.removeMessages(MSG_STATSD_HBM_BRIGHTNESS); + if (aboveTransition != oldAboveTransition) { + // report immediately + logHbmBrightnessStats(brightness, mDisplayStatsId); + } else { + // delay for rate limiting + Message msg = mHandler.obtainMessage(); + msg.what = MSG_STATSD_HBM_BRIGHTNESS; + msg.arg1 = Float.floatToIntBits(brightness); + msg.arg2 = mDisplayStatsId; + mHandler.sendMessageDelayed(msg, BRIGHTNESS_CHANGE_STATSD_REPORT_INTERVAL_MS); + } + } + } + + private final void logHbmBrightnessStats(float brightness, int displayStatsId) { + synchronized (mHandler) { + FrameworkStatsLog.write( + FrameworkStatsLog.DISPLAY_HBM_BRIGHTNESS_CHANGED, displayStatsId, brightness); + } + } + private final class DisplayControllerHandler extends Handler { public DisplayControllerHandler(Looper looper) { super(looper, null, true /*async*/); @@ -2526,6 +2588,10 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call final int justActivated = msg.arg2; handleRbcChanged(strengthChanged == 1, justActivated == 1); break; + + case MSG_STATSD_HBM_BRIGHTNESS: + logHbmBrightnessStats(Float.intBitsToFloat(msg.arg1), msg.arg2); + break; } } } diff --git a/services/core/java/com/android/server/display/HighBrightnessModeController.java b/services/core/java/com/android/server/display/HighBrightnessModeController.java index 1e1cfeb5b9dde..16273cef37b84 100644 --- a/services/core/java/com/android/server/display/HighBrightnessModeController.java +++ b/services/core/java/com/android/server/display/HighBrightnessModeController.java @@ -37,6 +37,7 @@ import android.util.TimeUtils; import android.view.SurfaceControlHdrLayerInfoListener; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.FrameworkStatsLog; import com.android.server.display.DisplayDeviceConfig.HighBrightnessModeData; import com.android.server.display.DisplayManagerService.Clock; @@ -80,6 +81,7 @@ class HighBrightnessModeController { private boolean mIsInAllowedAmbientRange = false; private boolean mIsTimeAvailable = false; private boolean mIsAutoBrightnessEnabled = false; + private boolean mIsAutoBrightnessOffByState = false; private float mBrightness; private int mHbmMode = BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF; private boolean mIsHdrLayerPresent = false; @@ -88,6 +90,7 @@ class HighBrightnessModeController { private int mWidth; private int mHeight; private float mAmbientLux; + private int mDisplayStatsId; /** * If HBM is currently running, this is the start time for the current HBM session. @@ -102,15 +105,15 @@ class HighBrightnessModeController { private LinkedList mEvents = new LinkedList<>(); HighBrightnessModeController(Handler handler, int width, int height, IBinder displayToken, - float brightnessMin, float brightnessMax, HighBrightnessModeData hbmData, - Runnable hbmChangeCallback, Context context) { - this(new Injector(), handler, width, height, displayToken, brightnessMin, brightnessMax, - hbmData, hbmChangeCallback, context); + String displayUniqueId, float brightnessMin, float brightnessMax, + HighBrightnessModeData hbmData, Runnable hbmChangeCallback, Context context) { + this(new Injector(), handler, width, height, displayToken, displayUniqueId, brightnessMin, + brightnessMax, hbmData, hbmChangeCallback, context); } @VisibleForTesting HighBrightnessModeController(Injector injector, Handler handler, int width, int height, - IBinder displayToken, float brightnessMin, float brightnessMax, + IBinder displayToken, String displayUniqueId, float brightnessMin, float brightnessMax, HighBrightnessModeData hbmData, Runnable hbmChangeCallback, Context context) { mInjector = injector; @@ -126,10 +129,13 @@ class HighBrightnessModeController { mRecalcRunnable = this::recalculateTimeAllowance; mHdrListener = new HdrListener(); - resetHbmData(width, height, displayToken, hbmData); + resetHbmData(width, height, displayToken, displayUniqueId, hbmData); } - void setAutoBrightnessEnabled(boolean isEnabled) { + void setAutoBrightnessEnabled(int state) { + final boolean isEnabled = state == AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED; + mIsAutoBrightnessOffByState = + state == AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE; if (!deviceSupportsHbm() || isEnabled == mIsAutoBrightnessEnabled) { return; } @@ -231,10 +237,12 @@ class HighBrightnessModeController { mSettingsObserver.stopObserving(); } - void resetHbmData(int width, int height, IBinder displayToken, HighBrightnessModeData hbmData) { + void resetHbmData(int width, int height, IBinder displayToken, String displayUniqueId, + HighBrightnessModeData hbmData) { mWidth = width; mHeight = height; mHbmData = hbmData; + mDisplayStatsId = displayUniqueId.hashCode(); unregisterHdrListener(); mSkinThermalStatusObserver.stopObserving(); @@ -275,6 +283,7 @@ class HighBrightnessModeController { + (mIsAutoBrightnessEnabled ? "" : " (old/invalid)")); pw.println(" mIsInAllowedAmbientRange=" + mIsInAllowedAmbientRange); pw.println(" mIsAutoBrightnessEnabled=" + mIsAutoBrightnessEnabled); + pw.println(" mIsAutoBrightnessOffByState=" + mIsAutoBrightnessOffByState); pw.println(" mIsHdrLayerPresent=" + mIsHdrLayerPresent); pw.println(" mBrightnessMin=" + mBrightnessMin); pw.println(" mBrightnessMax=" + mBrightnessMax); @@ -436,11 +445,52 @@ class HighBrightnessModeController { private void updateHbmMode() { int newHbmMode = calculateHighBrightnessMode(); if (mHbmMode != newHbmMode) { + updateHbmStats(mHbmMode, newHbmMode); mHbmMode = newHbmMode; mHbmChangeCallback.run(); } } + private void updateHbmStats(int mode, int newMode) { + int state = FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_OFF; + if (newMode == BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR) { + state = FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_HDR; + } else if (newMode == BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT) { + state = FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_SUNLIGHT; + } + + int reason = + FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_TRANSITION_REASON_UNKNOWN; + boolean oldHbmSv = (mode == BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT); + boolean newHbmSv = (newMode == BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT); + if (oldHbmSv && !newHbmSv) { + // If more than one conditions are flipped and turn off HBM sunlight + // visibility, only one condition will be reported to make it simple. + if (!mIsAutoBrightnessEnabled && mIsAutoBrightnessOffByState) { + reason = FrameworkStatsLog + .DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_DISPLAY_OFF; + } else if (!mIsAutoBrightnessEnabled) { + reason = FrameworkStatsLog + .DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_AUTOBRIGHTNESS_OFF; + } else if (!mIsInAllowedAmbientRange) { + reason = FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_LUX_DROP; + } else if (!mIsTimeAvailable) { + reason = FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_TIME_LIMIT; + } else if (!mIsThermalStatusWithinLimit) { + reason = FrameworkStatsLog + .DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_THERMAL_LIMIT; + } else if (mIsHdrLayerPresent) { + reason = FrameworkStatsLog + .DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_HDR_PLAYING; + } else if (mIsBlockedByLowPowerMode) { + reason = FrameworkStatsLog + .DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_BATTERY_SAVE_ON; + } + } + + mInjector.reportHbmStateChange(mDisplayStatsId, state, reason); + } + private int calculateHighBrightnessMode() { if (!deviceSupportsHbm()) { return BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF; @@ -642,5 +692,10 @@ class HighBrightnessModeController { return IThermalService.Stub.asInterface( ServiceManager.getService(Context.THERMAL_SERVICE)); } + + public void reportHbmStateChange(int display, int state, int reason) { + FrameworkStatsLog.write( + FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED, display, state, reason); + } } } diff --git a/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java b/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java index abe7d89dfa412..176e5a9fdc512 100644 --- a/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java @@ -16,6 +16,8 @@ package com.android.server.display; +import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED; + import static org.junit.Assert.assertEquals; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.any; @@ -111,7 +113,7 @@ public class AutomaticBrightnessControllerTest { // Configure the brightness controller and grab an instance of the sensor listener, // through which we can deliver fake (for test) sensor values. - controller.configure(true /* enable */, null /* configuration */, + controller.configure(AUTO_BRIGHTNESS_ENABLED, null /* configuration */, 0 /* brightness */, false /* userChangedBrightness */, 0 /* adjustment */, false /* userChanged */, DisplayPowerRequest.POLICY_BRIGHT); @@ -227,7 +229,7 @@ public class AutomaticBrightnessControllerTest { listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 1000)); // User sets brightness to 100 - mController.configure(true /* enable */, null /* configuration */, + mController.configure(AUTO_BRIGHTNESS_ENABLED, null /* configuration */, 0.5f /* brightness */, true /* userChangedBrightness */, 0 /* adjustment */, false /* userChanged */, DisplayPowerRequest.POLICY_BRIGHT); @@ -250,7 +252,7 @@ public class AutomaticBrightnessControllerTest { listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 1000)); // User sets brightness to 100 - mController.configure(true /* enable */, null /* configuration */, + mController.configure(AUTO_BRIGHTNESS_ENABLED, null /* configuration */, 0.5f /* brightness */, true /* userChangedBrightness */, 0 /* adjustment */, false /* userChanged */, DisplayPowerRequest.POLICY_BRIGHT); @@ -267,7 +269,7 @@ public class AutomaticBrightnessControllerTest { verifyNoMoreInteractions(mBrightnessMappingStrategy); // User sets idle brightness to 0.5 - mController.configure(true /* enable */, null /* configuration */, + mController.configure(AUTO_BRIGHTNESS_ENABLED, null /* configuration */, 0.5f /* brightness */, true /* userChangedBrightness */, 0 /* adjustment */, false /* userChanged */, DisplayPowerRequest.POLICY_BRIGHT); 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 aca863299b33a..beecdadcd546d 100644 --- a/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java @@ -20,6 +20,11 @@ import static android.hardware.display.BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR; import static android.hardware.display.BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF; import static android.hardware.display.BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT; +import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED; +import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED; +import static com.android.server.display.AutomaticBrightnessController + .AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE; + import static com.android.server.display.HighBrightnessModeController.HBM_TRANSITION_POINT_INVALID; import static org.junit.Assert.assertEquals; @@ -47,6 +52,7 @@ import androidx.test.core.app.ApplicationProvider; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; +import com.android.internal.util.FrameworkStatsLog; import com.android.internal.util.test.FakeSettingsProvider; import com.android.internal.util.test.FakeSettingsProviderRule; import com.android.server.display.DisplayDeviceConfig.HighBrightnessModeData; @@ -87,6 +93,7 @@ public class HighBrightnessModeControllerTest { private TestLooper mTestLooper; private Handler mHandler; private Binder mDisplayToken; + private String mDisplayUniqueId; private Context mContextSpy; @Rule @@ -108,6 +115,7 @@ public class HighBrightnessModeControllerTest { mClock = new OffsettableClock.Stopped(); mTestLooper = new TestLooper(mClock::now); mDisplayToken = null; + mDisplayUniqueId = "unique_id"; mContextSpy = spy(new ContextWrapper(ApplicationProvider.getApplicationContext())); final MockContentResolver resolver = mSettingsProviderRule.mockContentResolver(mContextSpy); when(mContextSpy.getContentResolver()).thenReturn(resolver); @@ -123,8 +131,8 @@ public class HighBrightnessModeControllerTest { public void testNoHbmData() { initHandler(null); final HighBrightnessModeController hbmc = new HighBrightnessModeController( - mInjectorMock, mHandler, DISPLAY_WIDTH, DISPLAY_HEIGHT, mDisplayToken, DEFAULT_MIN, - DEFAULT_MAX, null, () -> {}, mContextSpy); + mInjectorMock, mHandler, DISPLAY_WIDTH, DISPLAY_HEIGHT, mDisplayToken, + mDisplayUniqueId, DEFAULT_MIN, DEFAULT_MAX, null, () -> {}, mContextSpy); assertState(hbmc, DEFAULT_MIN, DEFAULT_MAX, HIGH_BRIGHTNESS_MODE_OFF); assertEquals(hbmc.getTransitionPoint(), HBM_TRANSITION_POINT_INVALID, 0.0f); } @@ -133,9 +141,9 @@ public class HighBrightnessModeControllerTest { public void testNoHbmData_Enabled() { initHandler(null); final HighBrightnessModeController hbmc = new HighBrightnessModeController( - mInjectorMock, mHandler, DISPLAY_WIDTH, DISPLAY_HEIGHT, mDisplayToken, DEFAULT_MIN, - DEFAULT_MAX, null, () -> {}, mContextSpy); - hbmc.setAutoBrightnessEnabled(true); + mInjectorMock, mHandler, DISPLAY_WIDTH, DISPLAY_HEIGHT, mDisplayToken, + mDisplayUniqueId, DEFAULT_MIN, DEFAULT_MAX, null, () -> {}, mContextSpy); + hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED); hbmc.onAmbientLuxChange(MINIMUM_LUX - 1); // below allowed range assertState(hbmc, DEFAULT_MIN, DEFAULT_MAX, HIGH_BRIGHTNESS_MODE_OFF); assertEquals(hbmc.getTransitionPoint(), HBM_TRANSITION_POINT_INVALID, 0.0f); @@ -152,7 +160,7 @@ public class HighBrightnessModeControllerTest { public void testAutoBrightnessEnabled_NoLux() { final HighBrightnessModeController hbmc = createDefaultHbm(); - hbmc.setAutoBrightnessEnabled(true); + hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED); assertState(hbmc, DEFAULT_MIN, TRANSITION_POINT, HIGH_BRIGHTNESS_MODE_OFF); } @@ -160,7 +168,7 @@ public class HighBrightnessModeControllerTest { public void testAutoBrightnessEnabled_LowLux() { final HighBrightnessModeController hbmc = createDefaultHbm(); - hbmc.setAutoBrightnessEnabled(true); + hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED); hbmc.onAmbientLuxChange(MINIMUM_LUX - 1); // below allowed range assertState(hbmc, DEFAULT_MIN, TRANSITION_POINT, HIGH_BRIGHTNESS_MODE_OFF); } @@ -169,7 +177,7 @@ public class HighBrightnessModeControllerTest { public void testAutoBrightnessEnabled_HighLux() { final HighBrightnessModeController hbmc = createDefaultHbm(); - hbmc.setAutoBrightnessEnabled(true); + hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED); hbmc.onAmbientLuxChange(MINIMUM_LUX + 1); assertState(hbmc, DEFAULT_MIN, DEFAULT_MAX, HIGH_BRIGHTNESS_MODE_SUNLIGHT); } @@ -178,9 +186,9 @@ public class HighBrightnessModeControllerTest { public void testAutoBrightnessEnabled_HighLux_ThenDisable() { final HighBrightnessModeController hbmc = createDefaultHbm(); - hbmc.setAutoBrightnessEnabled(true); + hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED); hbmc.onAmbientLuxChange(MINIMUM_LUX + 1); - hbmc.setAutoBrightnessEnabled(false); + hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_DISABLED); assertState(hbmc, DEFAULT_MIN, TRANSITION_POINT, HIGH_BRIGHTNESS_MODE_OFF); } @@ -189,7 +197,7 @@ public class HighBrightnessModeControllerTest { public void testWithinHighRange_thenOverTime_thenEarnBackTime() { final HighBrightnessModeController hbmc = createDefaultHbm(); - hbmc.setAutoBrightnessEnabled(true); + hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED); hbmc.onAmbientLuxChange(MINIMUM_LUX + 1); hbmc.onBrightnessChanged(TRANSITION_POINT + 0.01f); @@ -221,7 +229,7 @@ public class HighBrightnessModeControllerTest { public void testInHBM_ThenLowLux() { final HighBrightnessModeController hbmc = createDefaultHbm(); - hbmc.setAutoBrightnessEnabled(true); + hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED); hbmc.onAmbientLuxChange(MINIMUM_LUX + 1); hbmc.onBrightnessChanged(TRANSITION_POINT + 0.01f); @@ -245,7 +253,7 @@ public class HighBrightnessModeControllerTest { public void testInHBM_TestMultipleEvents_DueToAutoBrightness() { final HighBrightnessModeController hbmc = createDefaultHbm(); - hbmc.setAutoBrightnessEnabled(true); + hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED); hbmc.onAmbientLuxChange(MINIMUM_LUX + 1); hbmc.onBrightnessChanged(TRANSITION_POINT + 0.01f); @@ -274,7 +282,7 @@ public class HighBrightnessModeControllerTest { public void testInHBM_TestMultipleEvents_DueToLux() { final HighBrightnessModeController hbmc = createDefaultHbm(); - hbmc.setAutoBrightnessEnabled(true); + hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED); hbmc.onAmbientLuxChange(MINIMUM_LUX + 1); // Go into HBM for half the allowed window @@ -316,7 +324,7 @@ public class HighBrightnessModeControllerTest { listener.notifyThrottling(getSkinTemp(Temperature.THROTTLING_CRITICAL)); // Try to go into HBM mode but fail - hbmc.setAutoBrightnessEnabled(true); + hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED); hbmc.onAmbientLuxChange(MINIMUM_LUX + 1); advanceTime(10); @@ -335,7 +343,7 @@ public class HighBrightnessModeControllerTest { listener.notifyThrottling(getSkinTemp(Temperature.THROTTLING_LIGHT)); // Try to go into HBM mode - hbmc.setAutoBrightnessEnabled(true); + hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED); hbmc.onAmbientLuxChange(MINIMUM_LUX + 1); advanceTime(1); @@ -378,7 +386,7 @@ public class HighBrightnessModeControllerTest { final HighBrightnessModeController hbmc = createDefaultHbm(new OffsettableClock()); // Turn on sunlight - hbmc.setAutoBrightnessEnabled(true); + hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED); hbmc.onAmbientLuxChange(MINIMUM_LUX + 1); advanceTime(0); assertEquals(HIGH_BRIGHTNESS_MODE_SUNLIGHT, hbmc.getHighBrightnessMode()); @@ -451,6 +459,137 @@ public class HighBrightnessModeControllerTest { assertEquals(expectedHdrBrightness, hbmc.getHdrBrightnessValue(), EPSILON); } + @Test + public void testHbmStats_StateChange() { + final HighBrightnessModeController hbmc = createDefaultHbm(new OffsettableClock()); + final int displayStatsId = mDisplayUniqueId.hashCode(); + + hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED); + hbmc.onBrightnessChanged(TRANSITION_POINT); + hbmc.getHdrListener().onHdrInfoChanged(null /*displayToken*/, 1 /*numberOfHdrLayers*/, + DISPLAY_WIDTH, DISPLAY_HEIGHT, 0 /*flags*/); + advanceTime(0); + assertEquals(HIGH_BRIGHTNESS_MODE_HDR, hbmc.getHighBrightnessMode()); + + // Verify Stats HBM_ON_HDR + verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId), + eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_HDR), + eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_TRANSITION_REASON_UNKNOWN)); + + hbmc.getHdrListener().onHdrInfoChanged(null /*displayToken*/, 0 /*numberOfHdrLayers*/, + 0, 0, 0 /*flags*/); + advanceTime(0); + + // Verify Stats HBM_OFF + verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId), + eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_OFF), + eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_TRANSITION_REASON_UNKNOWN)); + + hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED); + hbmc.onAmbientLuxChange(MINIMUM_LUX + 1); + hbmc.onBrightnessChanged(TRANSITION_POINT + 0.01f); + + // Verify Stats HBM_ON_SUNLIGHT + verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId), + eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_SUNLIGHT), + eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_TRANSITION_REASON_UNKNOWN)); + + hbmc.onAmbientLuxChange(1); + advanceTime(TIME_ALLOWED_IN_WINDOW_MILLIS / 2 + 1); + + // Verify Stats HBM_OFF + verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId), + eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_OFF), + eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_LUX_DROP)); + } + + @Test + public void testHbmStats_ThermalOff() throws Exception { + final HighBrightnessModeController hbmc = createDefaultHbm(new OffsettableClock()); + final int displayStatsId = mDisplayUniqueId.hashCode(); + + verify(mThermalServiceMock).registerThermalEventListenerWithType( + mThermalEventListenerCaptor.capture(), eq(Temperature.TYPE_SKIN)); + final IThermalEventListener thermListener = mThermalEventListenerCaptor.getValue(); + + hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED); + hbmc.onAmbientLuxChange(MINIMUM_LUX + 1); + hbmc.onBrightnessChanged(TRANSITION_POINT + 0.01f); + advanceTime(1); + verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId), + eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_SUNLIGHT), + eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_TRANSITION_REASON_UNKNOWN)); + + thermListener.notifyThrottling(getSkinTemp(Temperature.THROTTLING_CRITICAL)); + advanceTime(10); + assertEquals(HIGH_BRIGHTNESS_MODE_OFF, hbmc.getHighBrightnessMode()); + verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId), + eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_OFF), + eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_THERMAL_LIMIT)); + } + + @Test + public void testHbmStats_TimeOut() { + final HighBrightnessModeController hbmc = createDefaultHbm(new OffsettableClock()); + final int displayStatsId = mDisplayUniqueId.hashCode(); + + hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED); + hbmc.onAmbientLuxChange(MINIMUM_LUX + 1); + hbmc.onBrightnessChanged(TRANSITION_POINT + 0.01f); + advanceTime(0); + verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId), + eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_SUNLIGHT), + eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_TRANSITION_REASON_UNKNOWN)); + + // Use up all the time in the window. + advanceTime(TIME_WINDOW_MILLIS + 1); + + verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId), + eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_OFF), + eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_TIME_LIMIT)); + } + + @Test + public void testHbmStats_DisplayOff() { + final HighBrightnessModeController hbmc = createDefaultHbm(); + final int displayStatsId = mDisplayUniqueId.hashCode(); + + hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED); + hbmc.onAmbientLuxChange(MINIMUM_LUX + 1); + hbmc.onBrightnessChanged(TRANSITION_POINT + 0.01f); + advanceTime(0); + verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId), + eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_SUNLIGHT), + eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_TRANSITION_REASON_UNKNOWN)); + + hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE); + verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId), + eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_OFF), + eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_DISPLAY_OFF)); + } + + @Test + public void testHbmStats_HdrPlaying() { + final HighBrightnessModeController hbmc = createDefaultHbm(new OffsettableClock()); + final int displayStatsId = mDisplayUniqueId.hashCode(); + + hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED); + hbmc.onAmbientLuxChange(MINIMUM_LUX + 1); + hbmc.onBrightnessChanged(TRANSITION_POINT + 0.01f); + advanceTime(0); + verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId), + eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_SUNLIGHT), + eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_TRANSITION_REASON_UNKNOWN)); + + hbmc.getHdrListener().onHdrInfoChanged(null /*displayToken*/, 1 /*numberOfHdrLayers*/, + DISPLAY_WIDTH, DISPLAY_HEIGHT, 0 /*flags*/); + advanceTime(0); + + verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId), + eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_HDR), + eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_HDR_PLAYING)); + } + private void assertState(HighBrightnessModeController hbmc, float brightnessMin, float brightnessMax, int hbmMode) { assertEquals(brightnessMin, hbmc.getCurrentBrightnessMin(), EPSILON); @@ -466,8 +605,8 @@ public class HighBrightnessModeControllerTest { private HighBrightnessModeController createDefaultHbm(OffsettableClock clock) { initHandler(clock); return new HighBrightnessModeController(mInjectorMock, mHandler, DISPLAY_WIDTH, - DISPLAY_HEIGHT, mDisplayToken, DEFAULT_MIN, DEFAULT_MAX, DEFAULT_HBM_DATA, () -> {}, - mContextSpy); + DISPLAY_HEIGHT, mDisplayToken, mDisplayUniqueId, DEFAULT_MIN, DEFAULT_MAX, + DEFAULT_HBM_DATA, () -> {}, mContextSpy); } private void initHandler(OffsettableClock clock) {