From 87546f50343c61b93c7a391d7736b5f022f550af Mon Sep 17 00:00:00 2001 From: Daniel Solomon Date: Wed, 12 Jan 2022 19:08:47 -0800 Subject: [PATCH] Add display brightness throttler This change adds BrightnessThrottler, which is responsible for limiting the usable range of display brightness based on skin temperature thermal throttling (extensible to account for other events/conditions in the future). Brightness constraints calculated by BrightnessThrottling are applied by DisplayPowerController as a brightness transform similarly to dimming and low power states. This change also adds a field to BrightnessInfo in order to capture the reason for an unusual max brightness (currently only accounts for a thermal reason). Finally, change HighBrightnessModeController (HBMC) so that HBM thermal throttling is still reported correctly to FrameworkStatsLog when brightness throttling is performed in DisplayPowerController (through BrightnessThrottler) rather than through HBMC's own thermal throttling mechanism. HBMC's own thermal throttling mechanism will be deprecated in a future change. Bug: 206857086 Bug: 212634465 Test: atest BrightnessThrottlerTest DisplayModeDirectorTest BrightnessLevelPreferenceControllerTest HighBrightnessModeControllerTest Test: Manually trigger and clear thermal throttling, and verify transitions, 1. While in manual brightness mode 2. While in automatic Brightness mode 3. While high brightness mode is enabled for HDR 3. Across display suspend/resume events Change-Id: I4c10f037a0a616e84fc109cc755bf3a5eaa3d111 --- .../hardware/display/BrightnessInfo.java | 46 ++- .../server/display/BrightnessThrottler.java | 262 ++++++++++++++++++ .../display/DisplayPowerController.java | 74 ++++- .../display/HighBrightnessModeController.java | 41 ++- .../display/BrightnessThrottlerTest.java | 227 ++++++++++++++- .../display/DisplayModeDirectorTest.java | 49 ++-- .../HighBrightnessModeControllerTest.java | 85 ++++-- 7 files changed, 708 insertions(+), 76 deletions(-) create mode 100644 services/core/java/com/android/server/display/BrightnessThrottler.java diff --git a/core/java/android/hardware/display/BrightnessInfo.java b/core/java/android/hardware/display/BrightnessInfo.java index 0dc8f92967fb9..99f3d156cfa95 100644 --- a/core/java/android/hardware/display/BrightnessInfo.java +++ b/core/java/android/hardware/display/BrightnessInfo.java @@ -57,6 +57,23 @@ public final class BrightnessInfo implements Parcelable { */ public static final int HIGH_BRIGHTNESS_MODE_HDR = 2; + @IntDef(prefix = {"BRIGHTNESS_MAX_REASON_"}, value = { + BRIGHTNESS_MAX_REASON_NONE, + BRIGHTNESS_MAX_REASON_THERMAL + }) + @Retention(RetentionPolicy.SOURCE) + public @interface BrightnessMaxReason {} + + /** + * Maximum brightness is unrestricted. + */ + public static final int BRIGHTNESS_MAX_REASON_NONE = 0; + + /** + * Maximum brightness is restricted due to thermal throttling. + */ + public static final int BRIGHTNESS_MAX_REASON_THERMAL = 1; + /** Brightness */ public final float brightness; @@ -78,21 +95,29 @@ public final class BrightnessInfo implements Parcelable { */ public final int highBrightnessMode; + /** + * The current reason for restricting maximum brightness. + * Can be any of BRIGHTNESS_MAX_REASON_* values. + */ + public final int brightnessMaxReason; + public BrightnessInfo(float brightness, float brightnessMinimum, float brightnessMaximum, - @HighBrightnessMode int highBrightnessMode, float highBrightnessTransitionPoint) { + @HighBrightnessMode int highBrightnessMode, float highBrightnessTransitionPoint, + @BrightnessMaxReason int brightnessMaxReason) { this(brightness, brightness, brightnessMinimum, brightnessMaximum, highBrightnessMode, - highBrightnessTransitionPoint); + highBrightnessTransitionPoint, brightnessMaxReason); } public BrightnessInfo(float brightness, float adjustedBrightness, float brightnessMinimum, float brightnessMaximum, @HighBrightnessMode int highBrightnessMode, - float highBrightnessTransitionPoint) { + float highBrightnessTransitionPoint, @BrightnessMaxReason int brightnessMaxReason) { this.brightness = brightness; this.adjustedBrightness = adjustedBrightness; this.brightnessMinimum = brightnessMinimum; this.brightnessMaximum = brightnessMaximum; this.highBrightnessMode = highBrightnessMode; this.highBrightnessTransitionPoint = highBrightnessTransitionPoint; + this.brightnessMaxReason = brightnessMaxReason; } /** @@ -110,6 +135,19 @@ public final class BrightnessInfo implements Parcelable { return "invalid"; } + /** + * @return User-friendly string for specified {@link BrightnessMaxReason} parameter. + */ + public static String briMaxReasonToString(@BrightnessMaxReason int reason) { + switch (reason) { + case BRIGHTNESS_MAX_REASON_NONE: + return "none"; + case BRIGHTNESS_MAX_REASON_THERMAL: + return "thermal"; + } + return "invalid"; + } + @Override public int describeContents() { return 0; @@ -123,6 +161,7 @@ public final class BrightnessInfo implements Parcelable { dest.writeFloat(brightnessMaximum); dest.writeInt(highBrightnessMode); dest.writeFloat(highBrightnessTransitionPoint); + dest.writeInt(brightnessMaxReason); } public static final @android.annotation.NonNull Creator CREATOR = @@ -145,6 +184,7 @@ public final class BrightnessInfo implements Parcelable { brightnessMaximum = source.readFloat(); highBrightnessMode = source.readInt(); highBrightnessTransitionPoint = source.readFloat(); + brightnessMaxReason = source.readInt(); } } diff --git a/services/core/java/com/android/server/display/BrightnessThrottler.java b/services/core/java/com/android/server/display/BrightnessThrottler.java new file mode 100644 index 0000000000000..767b2d18a69a0 --- /dev/null +++ b/services/core/java/com/android/server/display/BrightnessThrottler.java @@ -0,0 +1,262 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.display; + +import android.content.Context; +import android.hardware.display.BrightnessInfo; +import android.os.Handler; +import android.os.IThermalEventListener; +import android.os.IThermalService; +import android.os.PowerManager; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.os.Temperature; +import android.util.Slog; + +import com.android.server.display.DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel; +import com.android.server.display.DisplayDeviceConfig.BrightnessThrottlingData; + +import java.io.PrintWriter; + +/** + * This class monitors various conditions, such as skin temperature throttling status, and limits + * the allowed brightness range accordingly. + */ +class BrightnessThrottler { + private static final String TAG = "BrightnessThrottler"; + private static final boolean DEBUG = false; + + private static final int THROTTLING_INVALID = -1; + + private final Injector mInjector; + private final Handler mHandler; + private BrightnessThrottlingData mThrottlingData; + private final Runnable mThrottlingChangeCallback; + private final SkinThermalStatusObserver mSkinThermalStatusObserver; + private int mThrottlingStatus; + private float mBrightnessCap = PowerManager.BRIGHTNESS_MAX; + private @BrightnessInfo.BrightnessMaxReason int mBrightnessMaxReason = + BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE; + + BrightnessThrottler(Handler handler, BrightnessThrottlingData throttlingData, + Runnable throttlingChangeCallback) { + this(new Injector(), handler, throttlingData, throttlingChangeCallback); + } + + BrightnessThrottler(Injector injector, Handler handler, BrightnessThrottlingData throttlingData, + Runnable throttlingChangeCallback) { + mInjector = injector; + mHandler = handler; + mThrottlingData = throttlingData; + mThrottlingChangeCallback = throttlingChangeCallback; + mSkinThermalStatusObserver = new SkinThermalStatusObserver(mInjector, mHandler); + + resetThrottlingData(mThrottlingData); + } + + boolean deviceSupportsThrottling() { + return mThrottlingData != null; + } + + float getBrightnessCap() { + return mBrightnessCap; + } + + int getBrightnessMaxReason() { + return mBrightnessMaxReason; + } + + boolean isThrottled() { + return mBrightnessMaxReason != BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE; + } + + void stop() { + mSkinThermalStatusObserver.stopObserving(); + + // We're asked to stop throttling, so reset brightness restrictions. + mBrightnessCap = PowerManager.BRIGHTNESS_MAX; + mBrightnessMaxReason = BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE; + + // We set throttling status to an invalid value here so that we act on the first throttling + // value received from the thermal service after registration, even if that throttling value + // is THROTTLING_NONE. + mThrottlingStatus = THROTTLING_INVALID; + } + + void resetThrottlingData(BrightnessThrottlingData throttlingData) { + stop(); + mThrottlingData = throttlingData; + + if (deviceSupportsThrottling()) { + mSkinThermalStatusObserver.startObserving(); + } + } + + private float verifyAndConstrainBrightnessCap(float brightness) { + if (brightness < PowerManager.BRIGHTNESS_MIN) { + Slog.e(TAG, "brightness " + brightness + " is lower than the minimum possible " + + "brightness " + PowerManager.BRIGHTNESS_MIN); + brightness = PowerManager.BRIGHTNESS_MIN; + } + + if (brightness > PowerManager.BRIGHTNESS_MAX) { + Slog.e(TAG, "brightness " + brightness + " is higher than the maximum possible " + + "brightness " + PowerManager.BRIGHTNESS_MAX); + brightness = PowerManager.BRIGHTNESS_MAX; + } + + return brightness; + } + + private void thermalStatusChanged(@Temperature.ThrottlingStatus int newStatus) { + if (mThrottlingStatus != newStatus) { + mThrottlingStatus = newStatus; + updateThrottling(); + } + } + + private void updateThrottling() { + if (!deviceSupportsThrottling()) { + return; + } + + float brightnessCap = PowerManager.BRIGHTNESS_MAX; + int brightnessMaxReason = BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE; + + if (mThrottlingStatus != THROTTLING_INVALID) { + // Throttling levels are sorted by increasing severity + for (ThrottlingLevel level : mThrottlingData.throttlingLevels) { + if (level.thermalStatus <= mThrottlingStatus) { + brightnessCap = level.brightness; + brightnessMaxReason = BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL; + } else { + // Throttling levels that are greater than the current status are irrelevant + break; + } + } + } + + if (mBrightnessCap != brightnessCap || mBrightnessMaxReason != brightnessMaxReason) { + mBrightnessCap = verifyAndConstrainBrightnessCap(brightnessCap); + mBrightnessMaxReason = brightnessMaxReason; + + if (DEBUG) { + Slog.d(TAG, "State changed: mBrightnessCap = " + mBrightnessCap + + ", mBrightnessMaxReason = " + + BrightnessInfo.briMaxReasonToString(mBrightnessMaxReason)); + } + + if (mThrottlingChangeCallback != null) { + mThrottlingChangeCallback.run(); + } + } + } + + void dump(PrintWriter pw) { + mHandler.runWithScissors(() -> dumpLocal(pw), 1000); + } + + private void dumpLocal(PrintWriter pw) { + pw.println("BrightnessThrottler:"); + pw.println(" mThrottlingData=" + mThrottlingData); + pw.println(" mThrottlingStatus=" + mThrottlingStatus); + pw.println(" mBrightnessCap=" + mBrightnessCap); + pw.println(" mBrightnessMaxReason=" + + BrightnessInfo.briMaxReasonToString(mBrightnessMaxReason)); + + mSkinThermalStatusObserver.dump(pw); + } + + 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 = " + temp.getStatus()); + } + mHandler.post(() -> { + final @Temperature.ThrottlingStatus int status = temp.getStatus(); + thermalStatusChanged(status); + }); + } + + void startObserving() { + if (mStarted) { + if (DEBUG) { + Slog.d(TAG, "Thermal status observer already started"); + } + return; + } + mThermalService = mInjector.getThermalService(); + if (mThermalService == null) { + Slog.e(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() { + 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"); + } + } + } + + public static class Injector { + public IThermalService getThermalService() { + return IThermalService.Stub.asInterface( + ServiceManager.getService(Context.THERMAL_SERVICE)); + } + } +} diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java index ec4b91a79ae62..1f448549c59c7 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController.java +++ b/services/core/java/com/android/server/display/DisplayPowerController.java @@ -346,6 +346,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call private boolean mAppliedTemporaryBrightness; private boolean mAppliedTemporaryAutoBrightnessAdjustment; private boolean mAppliedBrightnessBoost; + private boolean mAppliedThrottling; // Reason for which the brightness was last changed. See {@link BrightnessReason} for more // information. @@ -379,6 +380,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call private final HighBrightnessModeController mHbmController; + private final BrightnessThrottler mBrightnessThrottler; + private final BrightnessSetting mBrightnessSetting; private final Runnable mOnBrightnessChangeRunnable; @@ -538,6 +541,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mHbmController = createHbmControllerLocked(); + mBrightnessThrottler = createBrightnessThrottlerLocked(); + // Seed the cached brightness saveBrightnessInfo(getScreenBrightnessSetting()); @@ -827,6 +832,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call reloadReduceBrightColours(); mHbmController.resetHbmData(info.width, info.height, token, info.uniqueId, mDisplayDeviceConfig.getHighBrightnessModeData()); + mBrightnessThrottler.resetThrottlingData( + mDisplayDeviceConfig.getBrightnessThrottlingData()); } private void sendUpdatePowerState() { @@ -1039,6 +1046,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call private void cleanupHandlerThreadAfterStop() { setProximitySensorEnabled(false); mHbmController.stop(); + mBrightnessThrottler.stop(); mHandler.removeCallbacksAndMessages(null); if (mUnfinishedBusiness) { mCallbacks.releaseSuspendBlocker(); @@ -1336,14 +1344,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mBrightnessReasonTemp.setReason(BrightnessReason.REASON_MANUAL); } - // The current brightness to use has been calculated at this point (minus the adjustments - // like low-power and dim), and HbmController should be notified so that it can accurately - // calculate HDR or HBM levels. We specifically do it here instead of having HbmController - // listen to the brightness setting because certain brightness sources (just as an app - // override) are not saved to the setting, but should be reflected in HBM - // calculations. - mHbmController.onBrightnessChanged(brightnessState); - if (updateScreenBrightnessSetting) { // Tell the rest of the system about the new brightness in case we had to change it // for things like auto-brightness or high-brightness-mode. Note that we do this @@ -1390,6 +1390,28 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mAppliedLowPower = false; } + // Apply brightness throttling after applying all other transforms + final float unthrottledBrightnessState = brightnessState; + if (mBrightnessThrottler.isThrottled()) { + brightnessState = Math.min(brightnessState, mBrightnessThrottler.getBrightnessCap()); + mBrightnessReasonTemp.addModifier(BrightnessReason.MODIFIER_THROTTLED); + if (!mAppliedThrottling) { + slowChange = false; + } + mAppliedThrottling = true; + } else if (mAppliedThrottling) { + slowChange = false; + mAppliedThrottling = false; + } + + // The current brightness to use has been calculated at this point, and HbmController should + // be notified so that it can accurately calculate HDR or HBM levels. We specifically do it + // here instead of having HbmController listen to the brightness setting because certain + // brightness sources (such as an app override) are not saved to the setting, but should be + // reflected in HBM calculations. + mHbmController.onBrightnessChanged(brightnessState, unthrottledBrightnessState, + mBrightnessThrottler.getBrightnessMaxReason()); + // Animate the screen brightness when the screen is on or dozing. // Skip the animation when the screen is off or suspended or transition to/from VR. boolean brightnessAdjusted = false; @@ -1441,6 +1463,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // use instead. We still preserve the calculated brightness for Standard Dynamic Range // (SDR) layers, but the main brightness value will be the one for HDR. float sdrAnimateValue = animateValue; + // TODO(b/216365040): The decision to prevent HBM for HDR in low power mode should be + // done in HighBrightnessModeController. if (mHbmController.getHighBrightnessMode() == BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR && ((mBrightnessReason.modifier & BrightnessReason.MODIFIER_DIMMED) == 0 || (mBrightnessReason.modifier & BrightnessReason.MODIFIER_LOW_POWER) == 0)) { @@ -1618,7 +1642,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mCachedBrightnessInfo.brightnessMin.value, mCachedBrightnessInfo.brightnessMax.value, mCachedBrightnessInfo.hbmMode.value, - mCachedBrightnessInfo.hbmTransitionPoint.value); + mCachedBrightnessInfo.hbmTransitionPoint.value, + mCachedBrightnessInfo.brightnessMaxReason.value); } } @@ -1648,6 +1673,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call changed |= mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.hbmTransitionPoint, mHbmController.getTransitionPoint()); + changed |= + mCachedBrightnessInfo.checkAndSetInt(mCachedBrightnessInfo.brightnessMaxReason, + mBrightnessThrottler.getBrightnessMaxReason()); return changed; } @@ -1679,6 +1707,18 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call }, mContext); } + private BrightnessThrottler createBrightnessThrottlerLocked() { + final DisplayDevice device = mLogicalDisplay.getPrimaryDisplayDeviceLocked(); + final DisplayDeviceConfig ddConfig = device.getDisplayDeviceConfig(); + final DisplayDeviceConfig.BrightnessThrottlingData data = + ddConfig != null ? ddConfig.getBrightnessThrottlingData() : null; + return new BrightnessThrottler(mHandler, data, + () -> { + sendUpdatePowerStateLocked(); + postBrightnessChangeRunnable(); + }); + } + private void blockScreenOn() { if (mPendingScreenOnUnblocker == null) { Trace.asyncTraceBegin(Trace.TRACE_TAG_POWER, SCREEN_ON_BLOCKED_TRACE_NAME, 0); @@ -2346,6 +2386,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call pw.println(" mCachedBrightnessInfo.hbmMode=" + mCachedBrightnessInfo.hbmMode.value); pw.println(" mCachedBrightnessInfo.hbmTransitionPoint=" + mCachedBrightnessInfo.hbmTransitionPoint.value); + pw.println(" mCachedBrightnessInfo.brightnessMaxReason =" + + mCachedBrightnessInfo.brightnessMaxReason .value); } pw.println(" mDisplayBlanksAfterDozeConfig=" + mDisplayBlanksAfterDozeConfig); pw.println(" mBrightnessBucketsInDozeConfig=" + mBrightnessBucketsInDozeConfig); @@ -2384,6 +2426,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call pw.println(" mAppliedAutoBrightness=" + mAppliedAutoBrightness); pw.println(" mAppliedDimming=" + mAppliedDimming); pw.println(" mAppliedLowPower=" + mAppliedLowPower); + pw.println(" mAppliedThrottling=" + mAppliedThrottling); pw.println(" mAppliedScreenBrightnessOverride=" + mAppliedScreenBrightnessOverride); pw.println(" mAppliedTemporaryBrightness=" + mAppliedTemporaryBrightness); pw.println(" mDozing=" + mDozing); @@ -2422,6 +2465,10 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mHbmController.dump(pw); } + if (mBrightnessThrottler != null) { + mBrightnessThrottler.dump(pw); + } + pw.println(); if (mDisplayWhiteBalanceController != null) { mDisplayWhiteBalanceController.dump(pw); @@ -2702,7 +2749,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call static final int MODIFIER_DIMMED = 0x1; static final int MODIFIER_LOW_POWER = 0x2; static final int MODIFIER_HDR = 0x4; - static final int MODIFIER_MASK = MODIFIER_DIMMED | MODIFIER_LOW_POWER | MODIFIER_HDR; + static final int MODIFIER_THROTTLED = 0x8; + static final int MODIFIER_MASK = MODIFIER_DIMMED | MODIFIER_LOW_POWER | MODIFIER_HDR + | MODIFIER_THROTTLED; // ADJUSTMENT_* // These things can happen at any point, even if the main brightness reason doesn't @@ -2777,6 +2826,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call if ((modifier & MODIFIER_HDR) != 0) { sb.append(" hdr"); } + if ((modifier & MODIFIER_THROTTLED) != 0) { + sb.append(" throttled"); + } int strlen = sb.length(); if (sb.charAt(strlen - 1) == '[') { sb.setLength(strlen - 2); @@ -2813,6 +2865,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call public MutableInt hbmMode = new MutableInt(BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF); public MutableFloat hbmTransitionPoint = new MutableFloat(HighBrightnessModeController.HBM_TRANSITION_POINT_INVALID); + public MutableInt brightnessMaxReason = + new MutableInt(BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE); public boolean checkAndSetFloat(MutableFloat mf, float f) { if (mf.value != f) { diff --git a/services/core/java/com/android/server/display/HighBrightnessModeController.java b/services/core/java/com/android/server/display/HighBrightnessModeController.java index 5f8a4ab71e88e..534ed5d8491a9 100644 --- a/services/core/java/com/android/server/display/HighBrightnessModeController.java +++ b/services/core/java/com/android/server/display/HighBrightnessModeController.java @@ -82,7 +82,15 @@ class HighBrightnessModeController { private boolean mIsTimeAvailable = false; private boolean mIsAutoBrightnessEnabled = false; private boolean mIsAutoBrightnessOffByState = false; + + // The following values are typically reported by DisplayPowerController. + // This value includes brightness throttling effects. private float mBrightness; + // This value excludes brightness throttling effects. + private float mUnthrottledBrightness; + private @BrightnessInfo.BrightnessMaxReason int mThrottlingReason = + BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE; + private int mHbmMode = BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF; private boolean mIsHdrLayerPresent = false; private boolean mIsThermalStatusWithinLimit = true; @@ -192,11 +200,14 @@ class HighBrightnessModeController { } } - void onBrightnessChanged(float brightness) { + void onBrightnessChanged(float brightness, float unthrottledBrightness, + @BrightnessInfo.BrightnessMaxReason int throttlingReason) { if (!deviceSupportsHbm()) { return; } mBrightness = brightness; + mUnthrottledBrightness = unthrottledBrightness; + mThrottlingReason = throttlingReason; // If we are starting or ending a high brightness mode session, store the current // session in mRunningStartTimeMillis, or the old one in mEvents. @@ -274,6 +285,8 @@ class HighBrightnessModeController { private void dumpLocal(PrintWriter pw) { pw.println("HighBrightnessModeController:"); pw.println(" mBrightness=" + mBrightness); + pw.println(" mUnthrottledBrightness=" + mUnthrottledBrightness); + pw.println(" mThrottlingReason=" + BrightnessInfo.briMaxReasonToString(mThrottlingReason)); pw.println(" mCurrentMin=" + getCurrentBrightnessMin()); pw.println(" mCurrentMax=" + getCurrentBrightnessMax()); pw.println(" mHbmMode=" + BrightnessInfo.hbmToString(mHbmMode) @@ -431,6 +444,9 @@ class HighBrightnessModeController { + ", mIsThermalStatusWithinLimit: " + mIsThermalStatusWithinLimit + ", mIsBlockedByLowPowerMode: " + mIsBlockedByLowPowerMode + ", mBrightness: " + mBrightness + + ", mUnthrottledBrightness: " + mUnthrottledBrightness + + ", mThrottlingReason: " + + BrightnessInfo.briMaxReasonToString(mThrottlingReason) + ", RunningStartTimeMillis: " + mRunningStartTimeMillis + ", nextTimeout: " + (nextTimeout != -1 ? (nextTimeout - currentTime) : -1) + ", events: " + mEvents); @@ -454,12 +470,13 @@ class HighBrightnessModeController { } private void updateHbmStats(int newMode) { + final float transitionPoint = mHbmData.transitionPoint; int state = FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_OFF; if (newMode == BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR - && getHdrBrightnessValue() > mHbmData.transitionPoint) { + && getHdrBrightnessValue() > transitionPoint) { state = FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_HDR; } else if (newMode == BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT - && mBrightness > mHbmData.transitionPoint) { + && mBrightness > transitionPoint) { state = FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_SUNLIGHT; } if (state == mHbmStatsState) { @@ -468,11 +485,21 @@ class HighBrightnessModeController { int reason = FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_TRANSITION_REASON_UNKNOWN; - boolean oldHbmSv = (mHbmStatsState + final boolean oldHbmSv = (mHbmStatsState == FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_SUNLIGHT); - boolean newHbmSv = + final boolean newHbmSv = (state == FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_SUNLIGHT); if (oldHbmSv && !newHbmSv) { + // HighBrightnessModeController (HBMC) currently supports throttling from two sources: + // 1. Internal, received from HBMC.SkinThermalStatusObserver.notifyThrottling() + // 2. External, received from HBMC.onBrightnessChanged() + // TODO(b/216373254): Deprecate internal throttling source + final boolean internalThermalThrottling = !mIsThermalStatusWithinLimit; + final boolean externalThermalThrottling = + mUnthrottledBrightness > transitionPoint && // We would've liked HBM brightness... + mBrightness <= transitionPoint && // ...but we got NBM, because of... + mThrottlingReason == BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL; // ...thermals. + // 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) { @@ -485,7 +512,7 @@ class HighBrightnessModeController { 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) { + } else if (internalThermalThrottling || externalThermalThrottling) { reason = FrameworkStatsLog .DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_THERMAL_LIMIT; } else if (mIsHdrLayerPresent) { @@ -575,7 +602,7 @@ class HighBrightnessModeController { >= ((float) (mWidth * mHeight) * HDR_PERCENT_OF_SCREEN_REQUIRED); // Calling the brightness update so that we can recalculate // brightness with HDR in mind. - onBrightnessChanged(mBrightness); + onBrightnessChanged(mBrightness, mUnthrottledBrightness, mThrottlingReason); }); } } diff --git a/services/tests/servicestests/src/com/android/server/display/BrightnessThrottlerTest.java b/services/tests/servicestests/src/com/android/server/display/BrightnessThrottlerTest.java index a65b41d68d551..0ed90d27db996 100644 --- a/services/tests/servicestests/src/com/android/server/display/BrightnessThrottlerTest.java +++ b/services/tests/servicestests/src/com/android/server/display/BrightnessThrottlerTest.java @@ -18,21 +18,40 @@ package com.android.server.display; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import android.hardware.display.BrightnessInfo; +import android.os.Handler; +import android.os.IThermalEventListener; +import android.os.IThermalService; +import android.os.Message; import android.os.PowerManager; +import android.os.Temperature.ThrottlingStatus; +import android.os.Temperature; +import android.os.test.TestLooper; import android.platform.test.annotations.Presubmit; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; +import com.android.server.display.BrightnessThrottler.Injector; import com.android.server.display.DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel; import com.android.server.display.DisplayDeviceConfig.BrightnessThrottlingData; import org.junit.Before; 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; import java.util.ArrayList; @@ -43,10 +62,28 @@ import java.util.List; @Presubmit @RunWith(AndroidJUnit4.class) public class BrightnessThrottlerTest { + private static final float EPSILON = 0.000001f; + + private Handler mHandler; + private TestLooper mTestLooper; + + @Mock IThermalService mThermalServiceMock; + @Mock Injector mInjectorMock; + + @Captor ArgumentCaptor mThermalEventListenerCaptor; @Before public void setUp() { MockitoAnnotations.initMocks(this); + when(mInjectorMock.getThermalService()).thenReturn(mThermalServiceMock); + mTestLooper = new TestLooper(); + mHandler = new Handler(mTestLooper.getLooper(), new Handler.Callback() { + @Override + public boolean handleMessage(Message msg) { + return true; + } + }); + } ///////////////// @@ -62,19 +99,23 @@ public class BrightnessThrottlerTest { validLevels.add(new ThrottlingLevel(PowerManager.THERMAL_STATUS_MODERATE, 0.62f)); validLevels.add(new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL, 0.25f)); - List invalidThermalLevels = new ArrayList<>(); - invalidThermalLevels.add(new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL, 0.62f)); - invalidThermalLevels.add(new ThrottlingLevel(PowerManager.THERMAL_STATUS_MODERATE, 0.25f)); + List unsortedThermalLevels = new ArrayList<>(); + unsortedThermalLevels.add(new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL, 0.62f)); + unsortedThermalLevels.add(new ThrottlingLevel(PowerManager.THERMAL_STATUS_MODERATE, 0.25f)); - List invalidBrightnessLevels = new ArrayList<>(); - invalidBrightnessLevels.add( + List unsortedBrightnessLevels = new ArrayList<>(); + unsortedBrightnessLevels.add( new ThrottlingLevel(PowerManager.THERMAL_STATUS_MODERATE, 0.25f)); - invalidBrightnessLevels.add( + unsortedBrightnessLevels.add( new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL, 0.62f)); - List invalidLevels = new ArrayList<>(); - invalidLevels.add(new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL, 0.25f)); - invalidLevels.add(new ThrottlingLevel(PowerManager.THERMAL_STATUS_MODERATE, 0.62f)); + List unsortedLevels = new ArrayList<>(); + unsortedLevels.add(new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL, 0.25f)); + unsortedLevels.add(new ThrottlingLevel(PowerManager.THERMAL_STATUS_MODERATE, 0.62f)); + + List invalidLevel = new ArrayList<>(); + invalidLevel.add(new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL, + PowerManager.BRIGHTNESS_MAX + EPSILON)); // Test invalid data BrightnessThrottlingData data; @@ -84,11 +125,13 @@ public class BrightnessThrottlerTest { assertEquals(data, null); data = BrightnessThrottlingData.create(new ArrayList()); assertEquals(data, null); - data = BrightnessThrottlingData.create(invalidThermalLevels); + data = BrightnessThrottlingData.create(unsortedThermalLevels); assertEquals(data, null); - data = BrightnessThrottlingData.create(invalidBrightnessLevels); + data = BrightnessThrottlingData.create(unsortedBrightnessLevels); assertEquals(data, null); - data = BrightnessThrottlingData.create(invalidLevels); + data = BrightnessThrottlingData.create(unsortedLevels); + assertEquals(data, null); + data = BrightnessThrottlingData.create(invalidLevel); assertEquals(data, null); // Test valid data @@ -101,6 +144,154 @@ public class BrightnessThrottlerTest { assertThrottlingLevelsEquals(validLevels, data.throttlingLevels); } + @Test + public void testThrottlingUnsupported() throws Exception { + final BrightnessThrottler throttler = createThrottlerUnsupported(); + assertFalse(throttler.deviceSupportsThrottling()); + + // Thermal listener shouldn't be registered if throttling is unsupported + verify(mInjectorMock, never()).getThermalService(); + + // Ensure that brightness is uncapped when the device doesn't support throttling + assertEquals(PowerManager.BRIGHTNESS_MAX, throttler.getBrightnessCap(), 0f); + } + + @Test + public void testThrottlingSingleLevel() throws Exception { + final ThrottlingLevel level = new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL, + 0.25f); + + List levels = new ArrayList<>(); + levels.add(level); + final BrightnessThrottlingData data = BrightnessThrottlingData.create(levels); + final BrightnessThrottler throttler = createThrottlerSupported(data); + assertTrue(throttler.deviceSupportsThrottling()); + + verify(mThermalServiceMock).registerThermalEventListenerWithType( + mThermalEventListenerCaptor.capture(), eq(Temperature.TYPE_SKIN)); + final IThermalEventListener listener = mThermalEventListenerCaptor.getValue(); + + // Set status too low to trigger throttling + listener.notifyThrottling(getSkinTemp(level.thermalStatus - 1)); + mTestLooper.dispatchAll(); + assertEquals(PowerManager.BRIGHTNESS_MAX, throttler.getBrightnessCap(), 0f); + assertFalse(throttler.isThrottled()); + assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE, throttler.getBrightnessMaxReason()); + + // Set status just high enough to trigger throttling + listener.notifyThrottling(getSkinTemp(level.thermalStatus)); + mTestLooper.dispatchAll(); + assertEquals(level.brightness, throttler.getBrightnessCap(), 0f); + assertTrue(throttler.isThrottled()); + assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL, + throttler.getBrightnessMaxReason()); + + // Set status more than high enough to trigger throttling + listener.notifyThrottling(getSkinTemp(level.thermalStatus + 1)); + mTestLooper.dispatchAll(); + assertEquals(level.brightness, throttler.getBrightnessCap(), 0f); + assertTrue(throttler.isThrottled()); + assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL, + throttler.getBrightnessMaxReason()); + + // Return to the lower throttling level + listener.notifyThrottling(getSkinTemp(level.thermalStatus)); + mTestLooper.dispatchAll(); + assertEquals(level.brightness, throttler.getBrightnessCap(), 0f); + assertTrue(throttler.isThrottled()); + assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL, + throttler.getBrightnessMaxReason()); + + // Cool down + listener.notifyThrottling(getSkinTemp(level.thermalStatus - 1)); + mTestLooper.dispatchAll(); + assertEquals(PowerManager.BRIGHTNESS_MAX, throttler.getBrightnessCap(), 0f); + assertFalse(throttler.isThrottled()); + assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE, + throttler.getBrightnessMaxReason()); + } + + @Test + public void testThrottlingMultiLevel() throws Exception { + final ThrottlingLevel levelLo = new ThrottlingLevel(PowerManager.THERMAL_STATUS_MODERATE, + 0.62f); + final ThrottlingLevel levelHi = new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL, + 0.25f); + + List levels = new ArrayList<>(); + levels.add(levelLo); + levels.add(levelHi); + final BrightnessThrottlingData data = BrightnessThrottlingData.create(levels); + final BrightnessThrottler throttler = createThrottlerSupported(data); + assertTrue(throttler.deviceSupportsThrottling()); + + verify(mThermalServiceMock).registerThermalEventListenerWithType( + mThermalEventListenerCaptor.capture(), eq(Temperature.TYPE_SKIN)); + final IThermalEventListener listener = mThermalEventListenerCaptor.getValue(); + + // Set status too low to trigger throttling + listener.notifyThrottling(getSkinTemp(levelLo.thermalStatus - 1)); + mTestLooper.dispatchAll(); + assertEquals(PowerManager.BRIGHTNESS_MAX, throttler.getBrightnessCap(), 0f); + assertFalse(throttler.isThrottled()); + assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE, throttler.getBrightnessMaxReason()); + + // Set status just high enough to trigger throttling + listener.notifyThrottling(getSkinTemp(levelLo.thermalStatus)); + mTestLooper.dispatchAll(); + assertEquals(levelLo.brightness, throttler.getBrightnessCap(), 0f); + assertTrue(throttler.isThrottled()); + assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL, + throttler.getBrightnessMaxReason()); + + // Set status to an intermediate throttling level + listener.notifyThrottling(getSkinTemp(levelLo.thermalStatus + 1)); + mTestLooper.dispatchAll(); + assertEquals(levelLo.brightness, throttler.getBrightnessCap(), 0f); + assertTrue(throttler.isThrottled()); + assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL, + throttler.getBrightnessMaxReason()); + + // Set status to the highest configured throttling level + listener.notifyThrottling(getSkinTemp(levelHi.thermalStatus)); + mTestLooper.dispatchAll(); + assertEquals(levelHi.brightness, throttler.getBrightnessCap(), 0f); + assertTrue(throttler.isThrottled()); + assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL, + throttler.getBrightnessMaxReason()); + + // Set status to exceed the highest configured throttling level + listener.notifyThrottling(getSkinTemp(levelHi.thermalStatus + 1)); + mTestLooper.dispatchAll(); + assertEquals(levelHi.brightness, throttler.getBrightnessCap(), 0f); + assertTrue(throttler.isThrottled()); + assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL, + throttler.getBrightnessMaxReason()); + + // Return to an intermediate throttling level + listener.notifyThrottling(getSkinTemp(levelLo.thermalStatus + 1)); + mTestLooper.dispatchAll(); + assertEquals(levelLo.brightness, throttler.getBrightnessCap(), 0f); + assertTrue(throttler.isThrottled()); + assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL, + throttler.getBrightnessMaxReason()); + + // Return to the lowest configured throttling level + listener.notifyThrottling(getSkinTemp(levelLo.thermalStatus)); + mTestLooper.dispatchAll(); + assertEquals(levelLo.brightness, throttler.getBrightnessCap(), 0f); + assertTrue(throttler.isThrottled()); + assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL, + throttler.getBrightnessMaxReason()); + + // Cool down + listener.notifyThrottling(getSkinTemp(levelLo.thermalStatus - 1)); + mTestLooper.dispatchAll(); + assertEquals(PowerManager.BRIGHTNESS_MAX, throttler.getBrightnessCap(), 0f); + assertFalse(throttler.isThrottled()); + assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE, throttler.getBrightnessMaxReason()); + } + private void assertThrottlingLevelsEquals( List expected, List actual) { @@ -115,4 +306,16 @@ public class BrightnessThrottlerTest { } } + private BrightnessThrottler createThrottlerUnsupported() { + return new BrightnessThrottler(mInjectorMock, mHandler, null, () -> {}); + } + + private BrightnessThrottler createThrottlerSupported(BrightnessThrottlingData data) { + assertNotNull(data); + return new BrightnessThrottler(mInjectorMock, mHandler, data, () -> {}); + } + + private Temperature getSkinTemp(@ThrottlingStatus int status) { + return new Temperature(30.0f, Temperature.TYPE_SKIN, "test_skin_temp", status); + } } diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java index 418831f47c1ae..40c039210939a 100644 --- a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java +++ b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java @@ -1461,7 +1461,8 @@ public class DisplayModeDirectorTest { // Turn on HBM, with brightness in the HBM range when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn( new BrightnessInfo(TRANSITION_POINT + FLOAT_TOLERANCE, 0.0f, 1.0f, - BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR, TRANSITION_POINT)); + BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR, TRANSITION_POINT, + BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE)); listener.onDisplayChanged(DISPLAY_ID); vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE); assertVoteForRefreshRate(vote, hbmRefreshRate); @@ -1469,7 +1470,8 @@ public class DisplayModeDirectorTest { // Turn on HBM, with brightness below the HBM range when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn( new BrightnessInfo(TRANSITION_POINT - FLOAT_TOLERANCE, 0.0f, 1.0f, - BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR, TRANSITION_POINT)); + BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR, TRANSITION_POINT, + BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE)); listener.onDisplayChanged(DISPLAY_ID); vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE); assertNull(vote); @@ -1477,7 +1479,8 @@ public class DisplayModeDirectorTest { // Turn off HBM when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn( new BrightnessInfo(0.45f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF, - TRANSITION_POINT)); + TRANSITION_POINT, + BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE)); listener.onDisplayChanged(DISPLAY_ID); vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE); assertNull(vote); @@ -1485,7 +1488,8 @@ public class DisplayModeDirectorTest { // Turn on HBM, with brightness in the HBM range when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn( new BrightnessInfo(TRANSITION_POINT + FLOAT_TOLERANCE, 0.0f, 1.0f, - BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR, TRANSITION_POINT)); + BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR, TRANSITION_POINT, + BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE)); listener.onDisplayChanged(DISPLAY_ID); vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE); assertVoteForRefreshRate(vote, hbmRefreshRate); @@ -1493,7 +1497,7 @@ public class DisplayModeDirectorTest { // Turn off HBM when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn( new BrightnessInfo(0.45f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF, - TRANSITION_POINT)); + TRANSITION_POINT, BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE)); listener.onDisplayChanged(DISPLAY_ID); vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE); assertNull(vote); @@ -1501,7 +1505,8 @@ public class DisplayModeDirectorTest { // Turn on HBM, with brightness below the HBM range when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn( new BrightnessInfo(TRANSITION_POINT - FLOAT_TOLERANCE, 0.0f, 1.0f, - BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR, TRANSITION_POINT)); + BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR, TRANSITION_POINT, + BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE)); listener.onDisplayChanged(DISPLAY_ID); vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE); assertNull(vote); @@ -1509,7 +1514,7 @@ public class DisplayModeDirectorTest { // Turn off HBM when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn( new BrightnessInfo(0.45f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF, - TRANSITION_POINT)); + TRANSITION_POINT, BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE)); listener.onDisplayChanged(DISPLAY_ID); vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE); assertNull(vote); @@ -1580,7 +1585,7 @@ public class DisplayModeDirectorTest { // Turn on HBM when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn( new BrightnessInfo(1.0f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT, - TRANSITION_POINT)); + TRANSITION_POINT, BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE)); listener.onDisplayChanged(DISPLAY_ID); vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE); assertVoteForRefreshRate(vote, initialRefreshRate); @@ -1598,7 +1603,7 @@ public class DisplayModeDirectorTest { // Turn off HBM when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn( new BrightnessInfo(0.43f, 0.1f, 0.8f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF, - TRANSITION_POINT)); + TRANSITION_POINT, BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE)); listener.onDisplayChanged(DISPLAY_ID); vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE); assertNull(vote); @@ -1606,7 +1611,7 @@ public class DisplayModeDirectorTest { // Turn HBM on again and ensure the updated vote value stuck when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn( new BrightnessInfo(1.0f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT, - TRANSITION_POINT)); + TRANSITION_POINT, BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE)); listener.onDisplayChanged(DISPLAY_ID); vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE); assertVoteForRefreshRate(vote, updatedRefreshRate); @@ -1622,7 +1627,7 @@ public class DisplayModeDirectorTest { // Turn off HBM when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn( new BrightnessInfo(0.43f, 0.1f, 0.8f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF, - TRANSITION_POINT)); + TRANSITION_POINT, BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE)); listener.onDisplayChanged(DISPLAY_ID); vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE); assertNull(vote); @@ -1654,7 +1659,7 @@ public class DisplayModeDirectorTest { // Turn on HBM when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn( new BrightnessInfo(1.0f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT, - TRANSITION_POINT)); + TRANSITION_POINT, BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE)); listener.onDisplayChanged(DISPLAY_ID); vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE); assertNull(vote); @@ -1662,7 +1667,7 @@ public class DisplayModeDirectorTest { // Turn off HBM when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn( new BrightnessInfo(0.43f, 0.1f, 0.8f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF, - TRANSITION_POINT)); + TRANSITION_POINT, BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE)); listener.onDisplayChanged(DISPLAY_ID); vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE); assertNull(vote); @@ -1694,7 +1699,7 @@ public class DisplayModeDirectorTest { // Turn on HBM when HBM is supported; expect a valid transition point and a vote. when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn( new BrightnessInfo(1.0f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT, - TRANSITION_POINT)); + TRANSITION_POINT, BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE)); listener.onDisplayChanged(DISPLAY_ID); vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE); assertVoteForRefreshRate(vote, 60.0f); @@ -1702,7 +1707,7 @@ public class DisplayModeDirectorTest { // Turn off HBM when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn( new BrightnessInfo(1.0f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF, - TRANSITION_POINT)); + TRANSITION_POINT, BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE)); listener.onDisplayChanged(DISPLAY_ID); vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE); assertNull(vote); @@ -1711,7 +1716,7 @@ public class DisplayModeDirectorTest { // no vote. when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn( new BrightnessInfo(1.0f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT, - HBM_TRANSITION_POINT_INVALID)); + HBM_TRANSITION_POINT_INVALID, BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE)); listener.onDisplayChanged(DISPLAY_ID); vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE); assertNull(vote); @@ -1720,7 +1725,7 @@ public class DisplayModeDirectorTest { // no vote. when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn( new BrightnessInfo(1.0f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR, - HBM_TRANSITION_POINT_INVALID)); + HBM_TRANSITION_POINT_INVALID, BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE)); listener.onDisplayChanged(DISPLAY_ID); vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE); assertNull(vote); @@ -1728,7 +1733,7 @@ public class DisplayModeDirectorTest { // Turn off HBM when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn( new BrightnessInfo(1.0f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF, - TRANSITION_POINT)); + TRANSITION_POINT, BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE)); listener.onDisplayChanged(DISPLAY_ID); vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE); assertNull(vote); @@ -1737,7 +1742,8 @@ public class DisplayModeDirectorTest { private void setHbmAndAssertRefreshRate( DisplayModeDirector director, DisplayListener listener, int mode, float rr) { when(mInjector.getBrightnessInfo(DISPLAY_ID)) - .thenReturn(new BrightnessInfo(1.0f, 0.0f, 1.0f, mode, TRANSITION_POINT)); + .thenReturn(new BrightnessInfo(1.0f, 0.0f, 1.0f, mode, TRANSITION_POINT, + BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE)); listener.onDisplayChanged(DISPLAY_ID); final Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE); @@ -1817,7 +1823,7 @@ public class DisplayModeDirectorTest { // Turn on HBM when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn( new BrightnessInfo(1.0f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT, - TRANSITION_POINT)); + TRANSITION_POINT, BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE)); listener.onDisplayChanged(DISPLAY_ID); vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE); assertVoteForRefreshRate(vote, 60.f); @@ -1978,7 +1984,8 @@ public class DisplayModeDirectorTest { when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn( new BrightnessInfo(floatBri, floatAdjBri, 0.0f, 1.0f, - BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF, TRANSITION_POINT)); + BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF, TRANSITION_POINT, + BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE)); listener.onDisplayChanged(DISPLAY_ID); } 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 67ac1dc188493..b7af010103bc2 100644 --- a/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java @@ -16,6 +16,8 @@ package com.android.server.display; +import static android.hardware.display.BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE; +import static android.hardware.display.BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL; 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; @@ -201,7 +203,7 @@ public class HighBrightnessModeControllerTest { hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED); hbmc.onAmbientLuxChange(MINIMUM_LUX + 1); - hbmc.onBrightnessChanged(TRANSITION_POINT + 0.01f); + hbmcOnBrightnessChanged(hbmc, TRANSITION_POINT + 0.01f); // Verify we are in HBM assertState(hbmc, DEFAULT_MIN, DEFAULT_MAX, HIGH_BRIGHTNESS_MODE_SUNLIGHT); @@ -233,7 +235,7 @@ public class HighBrightnessModeControllerTest { hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED); hbmc.onAmbientLuxChange(MINIMUM_LUX + 1); - hbmc.onBrightnessChanged(TRANSITION_POINT + 0.01f); + hbmcOnBrightnessChanged(hbmc, TRANSITION_POINT + 0.01f); // Verify we are in HBM assertState(hbmc, DEFAULT_MIN, DEFAULT_MAX, HIGH_BRIGHTNESS_MODE_SUNLIGHT); @@ -258,18 +260,18 @@ public class HighBrightnessModeControllerTest { hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED); hbmc.onAmbientLuxChange(MINIMUM_LUX + 1); - hbmc.onBrightnessChanged(TRANSITION_POINT + 0.01f); + hbmcOnBrightnessChanged(hbmc, TRANSITION_POINT + 0.01f); advanceTime(TIME_ALLOWED_IN_WINDOW_MILLIS / 2); // Verify we are in HBM assertState(hbmc, DEFAULT_MIN, DEFAULT_MAX, HIGH_BRIGHTNESS_MODE_SUNLIGHT); - hbmc.onBrightnessChanged(TRANSITION_POINT - 0.01f); + hbmcOnBrightnessChanged(hbmc, TRANSITION_POINT - 0.01f); advanceTime(1); assertState(hbmc, DEFAULT_MIN, DEFAULT_MAX, HIGH_BRIGHTNESS_MODE_SUNLIGHT); - hbmc.onBrightnessChanged(TRANSITION_POINT + 0.01f); + hbmcOnBrightnessChanged(hbmc, TRANSITION_POINT + 0.01f); advanceTime(TIME_ALLOWED_IN_WINDOW_MILLIS / 2); assertState(hbmc, DEFAULT_MIN, DEFAULT_MAX, HIGH_BRIGHTNESS_MODE_SUNLIGHT); @@ -288,13 +290,13 @@ public class HighBrightnessModeControllerTest { hbmc.onAmbientLuxChange(MINIMUM_LUX + 1); // Go into HBM for half the allowed window - hbmc.onBrightnessChanged(TRANSITION_POINT + 0.01f); + hbmcOnBrightnessChanged(hbmc, TRANSITION_POINT + 0.01f); advanceTime(TIME_ALLOWED_IN_WINDOW_MILLIS / 2); assertState(hbmc, DEFAULT_MIN, DEFAULT_MAX, HIGH_BRIGHTNESS_MODE_SUNLIGHT); // Move lux below threshold (ending first event); hbmc.onAmbientLuxChange(MINIMUM_LUX - 1); - hbmc.onBrightnessChanged(TRANSITION_POINT); + hbmcOnBrightnessChanged(hbmc, TRANSITION_POINT); assertState(hbmc, DEFAULT_MIN, TRANSITION_POINT, HIGH_BRIGHTNESS_MODE_OFF); // Move up some amount of time so that there's still time in the window even after a @@ -304,7 +306,7 @@ public class HighBrightnessModeControllerTest { // Go into HBM for just under the second half of allowed window hbmc.onAmbientLuxChange(MINIMUM_LUX + 1); - hbmc.onBrightnessChanged(TRANSITION_POINT + 1); + hbmcOnBrightnessChanged(hbmc, TRANSITION_POINT + 1); advanceTime((TIME_ALLOWED_IN_WINDOW_MILLIS / 2) - 1); assertState(hbmc, DEFAULT_MIN, DEFAULT_MAX, HIGH_BRIGHTNESS_MODE_SUNLIGHT); @@ -434,7 +436,7 @@ public class HighBrightnessModeControllerTest { float brightness = 0.5f; float expectedHdrBrightness = MathUtils.map(DEFAULT_MIN, TRANSITION_POINT, DEFAULT_MIN, DEFAULT_MAX, brightness); // map value from normal range to hdr range - hbmc.onBrightnessChanged(brightness); + hbmcOnBrightnessChanged(hbmc, brightness); advanceTime(0); assertEquals(expectedHdrBrightness, hbmc.getHdrBrightnessValue(), EPSILON); @@ -442,21 +444,21 @@ public class HighBrightnessModeControllerTest { brightness = 0.33f; expectedHdrBrightness = MathUtils.map(DEFAULT_MIN, TRANSITION_POINT, DEFAULT_MIN, DEFAULT_MAX, brightness); // map value from normal range to hdr range - hbmc.onBrightnessChanged(brightness); + hbmcOnBrightnessChanged(hbmc, brightness); advanceTime(0); assertEquals(expectedHdrBrightness, hbmc.getHdrBrightnessValue(), EPSILON); // Try the min value brightness = DEFAULT_MIN; expectedHdrBrightness = DEFAULT_MIN; - hbmc.onBrightnessChanged(brightness); + hbmcOnBrightnessChanged(hbmc, brightness); advanceTime(0); assertEquals(expectedHdrBrightness, hbmc.getHdrBrightnessValue(), EPSILON); // Try the max value brightness = TRANSITION_POINT; expectedHdrBrightness = DEFAULT_MAX; - hbmc.onBrightnessChanged(brightness); + hbmcOnBrightnessChanged(hbmc, brightness); advanceTime(0); assertEquals(expectedHdrBrightness, hbmc.getHdrBrightnessValue(), EPSILON); } @@ -467,7 +469,7 @@ public class HighBrightnessModeControllerTest { final int displayStatsId = mDisplayUniqueId.hashCode(); hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED); - hbmc.onBrightnessChanged(TRANSITION_POINT); + hbmcOnBrightnessChanged(hbmc, TRANSITION_POINT); hbmc.getHdrListener().onHdrInfoChanged(null /*displayToken*/, 1 /*numberOfHdrLayers*/, DISPLAY_WIDTH, DISPLAY_HEIGHT, 0 /*flags*/); advanceTime(0); @@ -489,7 +491,7 @@ public class HighBrightnessModeControllerTest { hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED); hbmc.onAmbientLuxChange(MINIMUM_LUX + 1); - hbmc.onBrightnessChanged(TRANSITION_POINT + 0.01f); + hbmcOnBrightnessChanged(hbmc, TRANSITION_POINT + 0.01f); // Verify Stats HBM_ON_SUNLIGHT verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId), @@ -506,12 +508,12 @@ public class HighBrightnessModeControllerTest { } @Test - public void tetHbmStats_NbmHdrNoReport() { + public void testHbmStats_NbmHdrNoReport() { final HighBrightnessModeController hbmc = createDefaultHbm(new OffsettableClock()); final int displayStatsId = mDisplayUniqueId.hashCode(); hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED); - hbmc.onBrightnessChanged(DEFAULT_MIN); + hbmcOnBrightnessChanged(hbmc, DEFAULT_MIN); hbmc.getHdrListener().onHdrInfoChanged(null /*displayToken*/, 1 /*numberOfHdrLayers*/, DISPLAY_WIDTH, DISPLAY_HEIGHT, 0 /*flags*/); advanceTime(0); @@ -524,13 +526,13 @@ public class HighBrightnessModeControllerTest { } @Test - public void tetHbmStats_HighLuxLowBrightnessNoReport() { + public void testHbmStats_HighLuxLowBrightnessNoReport() { final HighBrightnessModeController hbmc = createDefaultHbm(new OffsettableClock()); final int displayStatsId = mDisplayUniqueId.hashCode(); hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED); hbmc.onAmbientLuxChange(MINIMUM_LUX + 1); - hbmc.onBrightnessChanged(DEFAULT_MIN); + hbmcOnBrightnessChanged(hbmc, DEFAULT_MIN); advanceTime(0); // verify in HBM sunlight mode assertEquals(HIGH_BRIGHTNESS_MODE_SUNLIGHT, hbmc.getHighBrightnessMode()); @@ -541,8 +543,10 @@ public class HighBrightnessModeControllerTest { anyInt()); } + // Test reporting of thermal throttling when triggered by HighBrightnessModeController's + // internal thermal throttling. @Test - public void testHbmStats_ThermalOff() throws Exception { + public void testHbmStats_InternalThermalOff() throws Exception { final HighBrightnessModeController hbmc = createDefaultHbm(new OffsettableClock()); final int displayStatsId = mDisplayUniqueId.hashCode(); @@ -552,7 +556,7 @@ public class HighBrightnessModeControllerTest { hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED); hbmc.onAmbientLuxChange(MINIMUM_LUX + 1); - hbmc.onBrightnessChanged(TRANSITION_POINT + 0.01f); + hbmcOnBrightnessChanged(hbmc, TRANSITION_POINT + 0.01f); advanceTime(1); verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId), eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_SUNLIGHT), @@ -566,6 +570,37 @@ public class HighBrightnessModeControllerTest { eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_THERMAL_LIMIT)); } + // Test reporting of thermal throttling when triggered externally through + // HighBrightnessModeController.onBrightnessChanged() + @Test + public void testHbmStats_ExternalThermalOff() throws Exception { + final HighBrightnessModeController hbmc = createDefaultHbm(new OffsettableClock()); + final int displayStatsId = mDisplayUniqueId.hashCode(); + final float hbmBrightness = TRANSITION_POINT + 0.01f; + final float nbmBrightness = TRANSITION_POINT - 0.01f; + + hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED); + hbmc.onAmbientLuxChange(MINIMUM_LUX + 1); + // Brightness is unthrottled, HBM brightness granted + hbmc.onBrightnessChanged(hbmBrightness, hbmBrightness, BRIGHTNESS_MAX_REASON_NONE); + 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)); + + // Brightness is thermally throttled, HBM brightness denied (NBM brightness granted) + hbmc.onBrightnessChanged(nbmBrightness, hbmBrightness, BRIGHTNESS_MAX_REASON_THERMAL); + advanceTime(1); + // We expect HBM mode to remain set to sunlight, indicating that HBMC *allows* this mode. + // However, we expect the HBM state reported by HBMC to be off, since external thermal + // throttling (reported to HBMC through onBrightnessChanged()) lowers brightness to below + // the HBM transition point. + assertEquals(HIGH_BRIGHTNESS_MODE_SUNLIGHT, 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()); @@ -573,7 +608,7 @@ public class HighBrightnessModeControllerTest { hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED); hbmc.onAmbientLuxChange(MINIMUM_LUX + 1); - hbmc.onBrightnessChanged(TRANSITION_POINT + 0.01f); + hbmcOnBrightnessChanged(hbmc, TRANSITION_POINT + 0.01f); advanceTime(0); verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId), eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_SUNLIGHT), @@ -594,7 +629,7 @@ public class HighBrightnessModeControllerTest { hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED); hbmc.onAmbientLuxChange(MINIMUM_LUX + 1); - hbmc.onBrightnessChanged(TRANSITION_POINT + 0.01f); + hbmcOnBrightnessChanged(hbmc, TRANSITION_POINT + 0.01f); advanceTime(0); verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId), eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_SUNLIGHT), @@ -613,7 +648,7 @@ public class HighBrightnessModeControllerTest { hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED); hbmc.onAmbientLuxChange(MINIMUM_LUX + 1); - hbmc.onBrightnessChanged(TRANSITION_POINT + 0.01f); + hbmcOnBrightnessChanged(hbmc, TRANSITION_POINT + 0.01f); advanceTime(0); verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId), eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_SUNLIGHT), @@ -667,4 +702,8 @@ public class HighBrightnessModeControllerTest { private Temperature getSkinTemp(@ThrottlingStatus int status) { return new Temperature(30.0f, Temperature.TYPE_SKIN, "test_skin_temp", status); } + + private void hbmcOnBrightnessChanged(HighBrightnessModeController hbmc, float brightness) { + hbmc.onBrightnessChanged(brightness, brightness, BRIGHTNESS_MAX_REASON_NONE); + } }