1 /* 2 * Copyright (C) 2023 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.display.brightness.clamper; 18 19 import static android.view.Display.STATE_ON; 20 21 import android.annotation.NonNull; 22 import android.annotation.Nullable; 23 import android.content.Context; 24 import android.hardware.SensorManager; 25 import android.hardware.display.BrightnessInfo; 26 import android.hardware.display.DisplayManagerInternal; 27 import android.os.Handler; 28 import android.os.HandlerExecutor; 29 import android.os.IBinder; 30 import android.os.PowerManager; 31 import android.provider.DeviceConfig; 32 import android.provider.DeviceConfigInterface; 33 import android.util.IndentingPrintWriter; 34 import android.util.Spline; 35 import android.view.Display; 36 37 import com.android.internal.annotations.VisibleForTesting; 38 import com.android.internal.display.BrightnessSynchronizer; 39 import com.android.server.display.DisplayBrightnessState; 40 import com.android.server.display.DisplayDeviceConfig; 41 import com.android.server.display.DisplayDeviceConfig.PowerThrottlingConfigData; 42 import com.android.server.display.DisplayDeviceConfig.PowerThrottlingData; 43 import com.android.server.display.DisplayDeviceConfig.ThermalBrightnessThrottlingData; 44 import com.android.server.display.config.SensorData; 45 import com.android.server.display.feature.DeviceConfigParameterProvider; 46 import com.android.server.display.feature.DisplayManagerFlags; 47 48 import java.io.PrintWriter; 49 import java.util.ArrayList; 50 import java.util.List; 51 import java.util.concurrent.Executor; 52 53 /** 54 * Clampers controller, all in DisplayControllerHandler 55 */ 56 public class BrightnessClamperController { 57 private static final String TAG = "BrightnessClamperController"; 58 59 private final DeviceConfigParameterProvider mDeviceConfigParameterProvider; 60 private final Handler mHandler; 61 private final LightSensorController mLightSensorController; 62 private int mDisplayState = Display.STATE_OFF; 63 64 private final ClamperChangeListener mClamperChangeListenerExternal; 65 private final Executor mExecutor; 66 67 private final List<BrightnessStateModifier> mModifiers; 68 69 private final List<DisplayDeviceDataListener> mDisplayDeviceDataListeners = new ArrayList<>(); 70 private final List<StatefulModifier> mStatefulModifiers = new ArrayList<>(); 71 private final List<UserSwitchListener> mUserSwitchListeners = new ArrayList<>(); 72 private final List<DeviceConfigListener> mDeviceConfigListeners = new ArrayList<>(); 73 74 private ModifiersAggregatedState mModifiersAggregatedState = new ModifiersAggregatedState(); 75 76 private final DeviceConfig.OnPropertiesChangedListener mOnPropertiesChangedListener; 77 78 private final LightSensorController.LightSensorListener mLightSensorListener = 79 new LightSensorController.LightSensorListener() { 80 @Override 81 public void onAmbientLuxChange(float lux) { 82 mModifiers.forEach(mModifier -> mModifier.setAmbientLux(lux)); 83 } 84 }; 85 86 private volatile boolean mStarted = false; 87 BrightnessClamperController(Handler handler, ClamperChangeListener clamperChangeListener, DisplayDeviceData data, Context context, DisplayManagerFlags flags, SensorManager sensorManager, float currentBrightness)88 public BrightnessClamperController(Handler handler, 89 ClamperChangeListener clamperChangeListener, DisplayDeviceData data, Context context, 90 DisplayManagerFlags flags, SensorManager sensorManager, float currentBrightness) { 91 this(new Injector(), handler, clamperChangeListener, data, context, flags, sensorManager, 92 currentBrightness); 93 } 94 95 @VisibleForTesting BrightnessClamperController(Injector injector, Handler handler, ClamperChangeListener clamperChangeListener, DisplayDeviceData data, Context context, DisplayManagerFlags flags, SensorManager sensorManager, float currentBrightness)96 BrightnessClamperController(Injector injector, Handler handler, 97 ClamperChangeListener clamperChangeListener, DisplayDeviceData data, Context context, 98 DisplayManagerFlags flags, SensorManager sensorManager, float currentBrightness) { 99 mDeviceConfigParameterProvider = injector.getDeviceConfigParameterProvider(); 100 mHandler = handler; 101 mLightSensorController = injector.getLightSensorController(sensorManager, context, 102 mLightSensorListener, mHandler); 103 104 mClamperChangeListenerExternal = clamperChangeListener; 105 mExecutor = new HandlerExecutor(handler); 106 107 Runnable modifiersChangeRunnableInternal = this::recalculateModifiersState; 108 ClamperChangeListener clamperChangeListenerInternal = () -> { 109 if (mStarted && !mHandler.hasCallbacks(modifiersChangeRunnableInternal)) { 110 mHandler.post(modifiersChangeRunnableInternal); 111 } 112 }; 113 114 mModifiers = injector.getModifiers(flags, context, handler, clamperChangeListenerInternal, 115 data, currentBrightness); 116 117 mModifiers.forEach(m -> { 118 if (m instanceof DisplayDeviceDataListener l) { 119 mDisplayDeviceDataListeners.add(l); 120 } 121 if (m instanceof StatefulModifier s) { 122 mStatefulModifiers.add(s); 123 } 124 if (m instanceof UserSwitchListener l) { 125 mUserSwitchListeners.add(l); 126 } 127 if (m instanceof DeviceConfigListener l) { 128 mDeviceConfigListeners.add(l); 129 } 130 }); 131 mOnPropertiesChangedListener = properties -> { 132 mDeviceConfigListeners.forEach(DeviceConfigListener::onDeviceConfigChanged); 133 }; 134 mLightSensorController.configure(data.getAmbientLightSensor(), data.getDisplayId()); 135 start(); 136 } 137 138 /** 139 * Should be called when display changed. Forwards the call to individual clampers 140 */ onDisplayChanged(DisplayDeviceData data)141 public void onDisplayChanged(DisplayDeviceData data) { 142 mLightSensorController.configure(data.getAmbientLightSensor(), data.getDisplayId()); 143 mDisplayDeviceDataListeners.forEach(l -> l.onDisplayChanged(data)); 144 adjustLightSensorSubscription(); 145 } 146 147 /** 148 * Applies clamping 149 * Called in DisplayControllerHandler 150 */ clamp(DisplayBrightnessState displayBrightnessState, DisplayManagerInternal.DisplayPowerRequest request, float brightnessValue, boolean slowChange, int displayState)151 public DisplayBrightnessState clamp(DisplayBrightnessState displayBrightnessState, 152 DisplayManagerInternal.DisplayPowerRequest request, 153 float brightnessValue, boolean slowChange, int displayState) { 154 mDisplayState = displayState; 155 DisplayBrightnessState.Builder builder = DisplayBrightnessState.Builder.from( 156 displayBrightnessState); 157 builder.setIsSlowChange(slowChange); 158 builder.setBrightness(brightnessValue); 159 160 adjustLightSensorSubscription(); 161 162 for (int i = 0; i < mModifiers.size(); i++) { 163 mModifiers.get(i).apply(request, builder); 164 } 165 166 return builder.build(); 167 } 168 169 /** 170 * Called when the user switches. 171 */ onUserSwitch()172 public void onUserSwitch() { 173 mUserSwitchListeners.forEach(UserSwitchListener::onSwitchUser); 174 } 175 176 /** 177 * Used to dump ClampersController state. 178 */ dump(PrintWriter writer)179 public void dump(PrintWriter writer) { 180 writer.println("BrightnessClamperController:"); 181 IndentingPrintWriter ipw = new IndentingPrintWriter(writer, " "); 182 mLightSensorController.dump(ipw); 183 mModifiers.forEach(modifier -> modifier.dump(ipw)); 184 } 185 186 /** 187 * This method should be called when the ClamperController is no longer in use. 188 * Called in DisplayControllerHandler 189 */ stop()190 public void stop() { 191 mStarted = false; 192 mDeviceConfigParameterProvider.removeOnPropertiesChangedListener( 193 mOnPropertiesChangedListener); 194 mLightSensorController.stop(); 195 mModifiers.forEach(BrightnessStateModifier::stop); 196 } 197 198 /** 199 * returns max allowed brightness. 200 * TODO(b/387452517): introduce constrainBrightness method 201 */ getMaxBrightness()202 public float getMaxBrightness() { 203 return mModifiersAggregatedState.mMaxBrightness; 204 } 205 isThrottled()206 public boolean isThrottled() { 207 return mModifiersAggregatedState.mMaxBrightnessReason 208 != BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE; 209 } 210 211 212 // Called in DisplayControllerHandler recalculateModifiersState()213 private void recalculateModifiersState() { 214 ModifiersAggregatedState newAggregatedState = new ModifiersAggregatedState(); 215 mStatefulModifiers.forEach((modifier) -> modifier.applyStateChange(newAggregatedState)); 216 217 if (needToNotifyExternalListener(mModifiersAggregatedState, newAggregatedState)) { 218 mClamperChangeListenerExternal.onChanged(); 219 } 220 mModifiersAggregatedState = newAggregatedState; 221 } 222 needToNotifyExternalListener(ModifiersAggregatedState state1, ModifiersAggregatedState state2)223 private boolean needToNotifyExternalListener(ModifiersAggregatedState state1, 224 ModifiersAggregatedState state2) { 225 return !BrightnessSynchronizer.floatEquals(state1.mMaxDesiredHdrRatio, 226 state2.mMaxDesiredHdrRatio) 227 || !BrightnessSynchronizer.floatEquals(state1.mMaxHdrBrightness, 228 state2.mMaxHdrBrightness) 229 || state1.mSdrHdrRatioSpline != state2.mSdrHdrRatioSpline 230 || state1.mMaxBrightnessReason != state2.mMaxBrightnessReason 231 || !BrightnessSynchronizer.floatEquals(state1.mMaxBrightness, 232 state2.mMaxBrightness); 233 } 234 start()235 private void start() { 236 if (!mDeviceConfigListeners.isEmpty()) { 237 mDeviceConfigParameterProvider.addOnPropertiesChangedListener( 238 mExecutor, mOnPropertiesChangedListener); 239 } 240 adjustLightSensorSubscription(); 241 mStarted = true; 242 } 243 adjustLightSensorSubscription()244 private void adjustLightSensorSubscription() { 245 if (mDisplayState == STATE_ON && mModifiers.stream() 246 .anyMatch(BrightnessStateModifier::shouldListenToLightSensor)) { 247 mLightSensorController.restart(); 248 } else { 249 mLightSensorController.stop(); 250 } 251 } 252 253 /** 254 * Clampers change listener 255 */ 256 public interface ClamperChangeListener { 257 /** 258 * Notifies that clamper state changed 259 */ onChanged()260 void onChanged(); 261 } 262 263 @VisibleForTesting 264 static class Injector { getDeviceConfigParameterProvider()265 DeviceConfigParameterProvider getDeviceConfigParameterProvider() { 266 return new DeviceConfigParameterProvider(DeviceConfigInterface.REAL); 267 } 268 getModifiers(DisplayManagerFlags flags, Context context, Handler handler, ClamperChangeListener listener, DisplayDeviceData data, float currentBrightness)269 List<BrightnessStateModifier> getModifiers(DisplayManagerFlags flags, Context context, 270 Handler handler, ClamperChangeListener listener, 271 DisplayDeviceData data, float currentBrightness) { 272 List<BrightnessStateModifier> modifiers = new ArrayList<>(); 273 modifiers.add(new BrightnessThermalModifier(handler, listener, data)); 274 if (flags.isBrightnessWearBedtimeModeClamperEnabled()) { 275 modifiers.add(new BrightnessWearBedtimeModeModifier(handler, context, 276 listener, data)); 277 } 278 if (flags.isPowerThrottlingClamperEnabled()) { 279 // Check if power-throttling config is present. 280 PowerThrottlingConfigData configData = data.getPowerThrottlingConfigData(); 281 if (configData != null) { 282 modifiers.add(new BrightnessPowerModifier(handler, listener, 283 data, currentBrightness)); 284 } 285 } 286 287 modifiers.add(new DisplayDimModifier(data.mDisplayId, context)); 288 modifiers.add(new BrightnessLowPowerModeModifier()); 289 if (flags.isEvenDimmerEnabled() && data.mDisplayDeviceConfig.isEvenDimmerAvailable()) { 290 modifiers.add(new BrightnessLowLuxModifier(handler, listener, context, 291 data.mDisplayDeviceConfig)); 292 } 293 if (flags.useNewHdrBrightnessModifier()) { 294 modifiers.add(new HdrBrightnessModifier(handler, context, listener, data)); 295 } 296 return modifiers; 297 } 298 getLightSensorController(SensorManager sensorManager, Context context, LightSensorController.LightSensorListener listener, Handler handler)299 LightSensorController getLightSensorController(SensorManager sensorManager, 300 Context context, LightSensorController.LightSensorListener listener, 301 Handler handler) { 302 return new LightSensorController(sensorManager, context.getResources(), 303 listener, handler); 304 } 305 } 306 307 /** 308 * Modifier should implement this interface in order to receive display change updates 309 */ 310 interface DisplayDeviceDataListener { onDisplayChanged(DisplayDeviceData displayData)311 void onDisplayChanged(DisplayDeviceData displayData); 312 } 313 314 /** 315 * Config Data for clampers/modifiers 316 */ 317 public static class DisplayDeviceData implements BrightnessThermalModifier.ThermalData, 318 BrightnessPowerModifier.PowerData, 319 BrightnessWearBedtimeModeModifier.WearBedtimeModeData { 320 @NonNull 321 private final String mUniqueDisplayId; 322 @Nullable 323 private final String mThermalThrottlingDataId; 324 @NonNull 325 private final String mPowerThrottlingDataId; 326 @NonNull 327 final DisplayDeviceConfig mDisplayDeviceConfig; 328 329 final int mWidth; 330 331 final int mHeight; 332 333 final IBinder mDisplayToken; 334 335 final int mDisplayId; 336 DisplayDeviceData(@onNull String uniqueDisplayId, @Nullable String thermalThrottlingDataId, @NonNull String powerThrottlingDataId, @NonNull DisplayDeviceConfig displayDeviceConfig, int width, int height, IBinder displayToken, int displayId)337 public DisplayDeviceData(@NonNull String uniqueDisplayId, 338 @Nullable String thermalThrottlingDataId, 339 @NonNull String powerThrottlingDataId, 340 @NonNull DisplayDeviceConfig displayDeviceConfig, 341 int width, 342 int height, 343 IBinder displayToken, 344 int displayId) { 345 mUniqueDisplayId = uniqueDisplayId; 346 mThermalThrottlingDataId = thermalThrottlingDataId; 347 mPowerThrottlingDataId = powerThrottlingDataId; 348 mDisplayDeviceConfig = displayDeviceConfig; 349 mWidth = width; 350 mHeight = height; 351 mDisplayToken = displayToken; 352 mDisplayId = displayId; 353 } 354 355 @NonNull 356 @Override getUniqueDisplayId()357 public String getUniqueDisplayId() { 358 return mUniqueDisplayId; 359 } 360 361 @Nullable 362 @Override getThermalThrottlingDataId()363 public String getThermalThrottlingDataId() { 364 return mThermalThrottlingDataId; 365 } 366 367 @Nullable 368 @Override getThermalBrightnessThrottlingData()369 public ThermalBrightnessThrottlingData getThermalBrightnessThrottlingData() { 370 if (mThermalThrottlingDataId == null) { 371 return null; 372 } 373 return mDisplayDeviceConfig.getThermalBrightnessThrottlingDataMapByThrottlingId().get( 374 mThermalThrottlingDataId); 375 } 376 377 @NonNull 378 @Override getPowerThrottlingDataId()379 public String getPowerThrottlingDataId() { 380 return mPowerThrottlingDataId; 381 } 382 383 @Nullable 384 @Override getPowerThrottlingData()385 public PowerThrottlingData getPowerThrottlingData() { 386 return mDisplayDeviceConfig.getPowerThrottlingDataMapByThrottlingId().get( 387 mPowerThrottlingDataId); 388 } 389 390 @Nullable 391 @Override getPowerThrottlingConfigData()392 public PowerThrottlingConfigData getPowerThrottlingConfigData() { 393 return mDisplayDeviceConfig.getPowerThrottlingConfigData(); 394 } 395 396 @Override getBrightnessWearBedtimeModeCap()397 public float getBrightnessWearBedtimeModeCap() { 398 return mDisplayDeviceConfig.getBrightnessCapForWearBedtimeMode(); 399 } 400 401 @NonNull 402 @Override getTempSensor()403 public SensorData getTempSensor() { 404 return mDisplayDeviceConfig.getTempSensor(); 405 } 406 407 @NonNull getAmbientLightSensor()408 SensorData getAmbientLightSensor() { 409 return mDisplayDeviceConfig.getAmbientLightSensor(); 410 } 411 getDisplayId()412 int getDisplayId() { 413 return mDisplayId; 414 } 415 } 416 417 /** 418 * Stateful modifier should implement this interface and modify aggregatedState. 419 * AggregatedState is used by Controller to determine if updatePowerState call is needed 420 * to correctly adjust brightness 421 */ 422 interface StatefulModifier { applyStateChange(ModifiersAggregatedState aggregatedState)423 void applyStateChange(ModifiersAggregatedState aggregatedState); 424 } 425 426 /** 427 * A clamper/modifier should implement this interface if it reads user-specific settings 428 */ 429 interface UserSwitchListener { onSwitchUser()430 void onSwitchUser(); 431 } 432 433 /** 434 * Modifier should implement this interface in order to receive device config updates 435 */ 436 interface DeviceConfigListener { onDeviceConfigChanged()437 void onDeviceConfigChanged(); 438 } 439 440 /** 441 * StatefulModifiers contribute to AggregatedState, that is used to decide if brightness 442 * adjustment is needed 443 */ 444 public static class ModifiersAggregatedState { 445 float mMaxDesiredHdrRatio = HdrBrightnessModifier.DEFAULT_MAX_HDR_SDR_RATIO; 446 float mMaxHdrBrightness = PowerManager.BRIGHTNESS_MAX; 447 @Nullable 448 Spline mSdrHdrRatioSpline = null; 449 @BrightnessInfo.BrightnessMaxReason 450 int mMaxBrightnessReason = BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE; 451 float mMaxBrightness = PowerManager.BRIGHTNESS_MAX; 452 } 453 } 454