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 static com.android.server.display.brightness.clamper.BrightnessClamper.Type; 22 23 import android.annotation.NonNull; 24 import android.annotation.Nullable; 25 import android.content.Context; 26 import android.content.res.Resources; 27 import android.hardware.Sensor; 28 import android.hardware.SensorEvent; 29 import android.hardware.SensorEventListener; 30 import android.hardware.SensorManager; 31 import android.hardware.display.BrightnessInfo; 32 import android.hardware.display.DisplayManagerInternal; 33 import android.os.Handler; 34 import android.os.HandlerExecutor; 35 import android.os.PowerManager; 36 import android.os.SystemClock; 37 import android.provider.DeviceConfig; 38 import android.provider.DeviceConfigInterface; 39 import android.util.IndentingPrintWriter; 40 import android.util.Slog; 41 42 import com.android.internal.R; 43 import com.android.internal.annotations.VisibleForTesting; 44 import com.android.server.display.DisplayBrightnessState; 45 import com.android.server.display.DisplayDeviceConfig; 46 import com.android.server.display.DisplayDeviceConfig.PowerThrottlingConfigData; 47 import com.android.server.display.DisplayDeviceConfig.PowerThrottlingData; 48 import com.android.server.display.DisplayDeviceConfig.ThermalBrightnessThrottlingData; 49 import com.android.server.display.brightness.BrightnessReason; 50 import com.android.server.display.config.SensorData; 51 import com.android.server.display.feature.DeviceConfigParameterProvider; 52 import com.android.server.display.feature.DisplayManagerFlags; 53 import com.android.server.display.utils.AmbientFilter; 54 import com.android.server.display.utils.AmbientFilterFactory; 55 import com.android.server.display.utils.DebugUtils; 56 import com.android.server.display.utils.SensorUtils; 57 58 import java.io.PrintWriter; 59 import java.util.ArrayList; 60 import java.util.List; 61 import java.util.concurrent.Executor; 62 import java.util.concurrent.TimeUnit; 63 64 /** 65 * Clampers controller, all in DisplayControllerHandler 66 */ 67 public class BrightnessClamperController { 68 private static final String TAG = "BrightnessClamperController"; 69 // To enable these logs, run: 70 // 'adb shell setprop persist.log.tag.BrightnessClamperController DEBUG && adb reboot' 71 private static final boolean DEBUG = DebugUtils.isDebuggable(TAG); 72 public static final float INVALID_LUX = -1f; 73 74 private final DeviceConfigParameterProvider mDeviceConfigParameterProvider; 75 private final Handler mHandler; 76 private final SensorManager mSensorManager; 77 private final ClamperChangeListener mClamperChangeListenerExternal; 78 private final Executor mExecutor; 79 private final List<BrightnessClamper<? super DisplayDeviceData>> mClampers; 80 81 private final List<BrightnessStateModifier> mModifiers; 82 private final DeviceConfig.OnPropertiesChangedListener mOnPropertiesChangedListener; 83 private float mBrightnessCap = PowerManager.BRIGHTNESS_MAX; 84 85 private float mCustomAnimationRate = DisplayBrightnessState.CUSTOM_ANIMATION_RATE_NOT_SET; 86 @Nullable 87 private Type mClamperType = null; 88 private final SensorEventListener mLightSensorListener; 89 private Sensor mRegisteredLightSensor = null; 90 private Sensor mLightSensor; 91 private String mLightSensorType; 92 private String mLightSensorName; 93 private AmbientFilter mAmbientFilter; 94 private final DisplayDeviceConfig mDisplayDeviceConfig; 95 private final Resources mResources; 96 private final int mLightSensorRate; 97 98 private final Injector mInjector; 99 private boolean mClamperApplied = false; 100 BrightnessClamperController(Handler handler, ClamperChangeListener clamperChangeListener, DisplayDeviceData data, Context context, DisplayManagerFlags flags, SensorManager sensorManager)101 public BrightnessClamperController(Handler handler, 102 ClamperChangeListener clamperChangeListener, DisplayDeviceData data, Context context, 103 DisplayManagerFlags flags, SensorManager sensorManager) { 104 this(null, handler, clamperChangeListener, data, context, flags, sensorManager); 105 } 106 107 @VisibleForTesting BrightnessClamperController(Injector injector, Handler handler, ClamperChangeListener clamperChangeListener, DisplayDeviceData data, Context context, DisplayManagerFlags flags, SensorManager sensorManager)108 BrightnessClamperController(Injector injector, Handler handler, 109 ClamperChangeListener clamperChangeListener, DisplayDeviceData data, Context context, 110 DisplayManagerFlags flags, SensorManager sensorManager) { 111 mInjector = injector == null ? new Injector() : injector; 112 mDeviceConfigParameterProvider = mInjector.getDeviceConfigParameterProvider(); 113 mHandler = handler; 114 mSensorManager = sensorManager; 115 mDisplayDeviceConfig = data.mDisplayDeviceConfig; 116 mLightSensorListener = new SensorEventListener() { 117 @Override 118 public void onSensorChanged(SensorEvent event) { 119 long now = SystemClock.elapsedRealtime(); 120 mAmbientFilter.addValue(TimeUnit.NANOSECONDS.toMillis(event.timestamp), 121 event.values[0]); 122 final float lux = mAmbientFilter.getEstimate(now); 123 mModifiers.forEach(mModifier -> mModifier.setAmbientLux(lux)); 124 } 125 126 @Override 127 public void onAccuracyChanged(Sensor sensor, int accuracy) { 128 // unused 129 } 130 }; 131 132 mClamperChangeListenerExternal = clamperChangeListener; 133 mExecutor = new HandlerExecutor(handler); 134 mResources = context.getResources(); 135 mLightSensorRate = context.getResources().getInteger( 136 R.integer.config_autoBrightnessLightSensorRate); 137 138 Runnable clamperChangeRunnableInternal = this::recalculateBrightnessCap; 139 140 ClamperChangeListener clamperChangeListenerInternal = () -> { 141 if (!mHandler.hasCallbacks(clamperChangeRunnableInternal)) { 142 mHandler.post(clamperChangeRunnableInternal); 143 } 144 }; 145 146 mClampers = mInjector.getClampers(handler, clamperChangeListenerInternal, data, flags, 147 context); 148 mModifiers = mInjector.getModifiers(flags, context, handler, clamperChangeListener, 149 data.mDisplayDeviceConfig, mSensorManager); 150 mOnPropertiesChangedListener = 151 properties -> mClampers.forEach(BrightnessClamper::onDeviceConfigChanged); 152 start(); 153 } 154 155 /** 156 * Should be called when display changed. Forwards the call to individual clampers 157 */ onDisplayChanged(DisplayDeviceData data)158 public void onDisplayChanged(DisplayDeviceData data) { 159 mClampers.forEach(clamper -> clamper.onDisplayChanged(data)); 160 } 161 162 /** 163 * Applies clamping 164 * Called in DisplayControllerHandler 165 */ clamp(DisplayManagerInternal.DisplayPowerRequest request, float brightnessValue, boolean slowChange, int displayState)166 public DisplayBrightnessState clamp(DisplayManagerInternal.DisplayPowerRequest request, 167 float brightnessValue, boolean slowChange, int displayState) { 168 float cappedBrightness = Math.min(brightnessValue, mBrightnessCap); 169 170 DisplayBrightnessState.Builder builder = DisplayBrightnessState.builder(); 171 builder.setIsSlowChange(slowChange); 172 builder.setBrightness(cappedBrightness); 173 builder.setMaxBrightness(mBrightnessCap); 174 builder.setCustomAnimationRate(mCustomAnimationRate); 175 176 if (mClamperType != null) { 177 builder.getBrightnessReason().addModifier(BrightnessReason.MODIFIER_THROTTLED); 178 if (!mClamperApplied) { 179 builder.setIsSlowChange(false); 180 } 181 mClamperApplied = true; 182 } else { 183 mClamperApplied = false; 184 } 185 186 if (displayState != STATE_ON) { 187 unregisterSensorListener(); 188 } else { 189 maybeRegisterLightSensor(); 190 } 191 192 for (int i = 0; i < mModifiers.size(); i++) { 193 mModifiers.get(i).apply(request, builder); 194 } 195 196 return builder.build(); 197 } 198 199 /** 200 * See BrightnessThrottler.getBrightnessMaxReason: 201 * used in: 202 * 1) DPC2.CachedBrightnessInfo to determine changes 203 * 2) DPC2.logBrightnessEvent 204 * 3) HBMController - for logging 205 * Method is called in mHandler thread (DisplayControllerHandler), in the same thread 206 * recalculateBrightnessCap and DPC2.updatePowerStateInternal are called. 207 * Should be moved to DisplayBrightnessState OR derived from DisplayBrightnessState 208 * TODO: b/263362199 209 */ 210 @BrightnessInfo.BrightnessMaxReason getBrightnessMaxReason()211 public int getBrightnessMaxReason() { 212 if (mClamperType == null) { 213 return BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE; 214 } else if (mClamperType == Type.THERMAL) { 215 return BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL; 216 } else if (mClamperType == Type.POWER) { 217 return BrightnessInfo.BRIGHTNESS_MAX_REASON_POWER_IC; 218 } else if (mClamperType == Type.WEAR_BEDTIME_MODE) { 219 return BrightnessInfo.BRIGHTNESS_MAX_REASON_WEAR_BEDTIME_MODE; 220 } else { 221 Slog.wtf(TAG, "BrightnessMaxReason not mapped for type=" + mClamperType); 222 return BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE; 223 } 224 } 225 226 /** 227 * Used to dump ClampersController state. 228 */ dump(PrintWriter writer)229 public void dump(PrintWriter writer) { 230 writer.println("BrightnessClamperController:"); 231 writer.println(" mBrightnessCap: " + mBrightnessCap); 232 writer.println(" mClamperType: " + mClamperType); 233 writer.println(" mClamperApplied: " + mClamperApplied); 234 writer.println(" mLightSensor=" + mLightSensor); 235 writer.println(" mRegisteredLightSensor=" + mRegisteredLightSensor); 236 IndentingPrintWriter ipw = new IndentingPrintWriter(writer, " "); 237 mClampers.forEach(clamper -> clamper.dump(ipw)); 238 mModifiers.forEach(modifier -> modifier.dump(ipw)); 239 } 240 241 /** 242 * This method should be called when the ClamperController is no longer in use. 243 * Called in DisplayControllerHandler 244 */ stop()245 public void stop() { 246 mDeviceConfigParameterProvider.removeOnPropertiesChangedListener( 247 mOnPropertiesChangedListener); 248 mClampers.forEach(BrightnessClamper::stop); 249 mModifiers.forEach(BrightnessStateModifier::stop); 250 } 251 252 253 // Called in DisplayControllerHandler recalculateBrightnessCap()254 private void recalculateBrightnessCap() { 255 float brightnessCap = PowerManager.BRIGHTNESS_MAX; 256 Type clamperType = null; 257 float customAnimationRate = DisplayBrightnessState.CUSTOM_ANIMATION_RATE_NOT_SET; 258 259 BrightnessClamper<?> minClamper = mClampers.stream() 260 .filter(BrightnessClamper::isActive) 261 .min((clamper1, clamper2) -> Float.compare(clamper1.getBrightnessCap(), 262 clamper2.getBrightnessCap())).orElse(null); 263 264 if (minClamper != null) { 265 brightnessCap = minClamper.getBrightnessCap(); 266 clamperType = minClamper.getType(); 267 customAnimationRate = minClamper.getCustomAnimationRate(); 268 } 269 270 if (mBrightnessCap != brightnessCap 271 || mClamperType != clamperType 272 || mCustomAnimationRate != customAnimationRate) { 273 mBrightnessCap = brightnessCap; 274 mClamperType = clamperType; 275 mCustomAnimationRate = customAnimationRate; 276 mClamperChangeListenerExternal.onChanged(); 277 } 278 } 279 start()280 private void start() { 281 if (!mClampers.isEmpty()) { 282 mDeviceConfigParameterProvider.addOnPropertiesChangedListener( 283 mExecutor, mOnPropertiesChangedListener); 284 reloadLightSensorData(mDisplayDeviceConfig); 285 mLightSensor = mInjector.getLightSensor( 286 mSensorManager, mLightSensorType, mLightSensorName); 287 maybeRegisterLightSensor(); 288 } 289 } 290 291 /** 292 * Clampers change listener 293 */ 294 public interface ClamperChangeListener { 295 /** 296 * Notifies that clamper state changed 297 */ onChanged()298 void onChanged(); 299 } 300 301 @VisibleForTesting 302 static class Injector { getDeviceConfigParameterProvider()303 DeviceConfigParameterProvider getDeviceConfigParameterProvider() { 304 return new DeviceConfigParameterProvider(DeviceConfigInterface.REAL); 305 } 306 getClampers(Handler handler, ClamperChangeListener clamperChangeListener, DisplayDeviceData data, DisplayManagerFlags flags, Context context)307 List<BrightnessClamper<? super DisplayDeviceData>> getClampers(Handler handler, 308 ClamperChangeListener clamperChangeListener, DisplayDeviceData data, 309 DisplayManagerFlags flags, Context context) { 310 List<BrightnessClamper<? super DisplayDeviceData>> clampers = new ArrayList<>(); 311 clampers.add( 312 new BrightnessThermalClamper(handler, clamperChangeListener, data)); 313 if (flags.isPowerThrottlingClamperEnabled()) { 314 clampers.add(new BrightnessPowerClamper(handler, clamperChangeListener, 315 data)); 316 } 317 if (flags.isBrightnessWearBedtimeModeClamperEnabled()) { 318 clampers.add(new BrightnessWearBedtimeModeClamper(handler, context, 319 clamperChangeListener, data)); 320 } 321 return clampers; 322 } 323 getModifiers(DisplayManagerFlags flags, Context context, Handler handler, ClamperChangeListener listener, DisplayDeviceConfig displayDeviceConfig, SensorManager sensorManager)324 List<BrightnessStateModifier> getModifiers(DisplayManagerFlags flags, Context context, 325 Handler handler, ClamperChangeListener listener, 326 DisplayDeviceConfig displayDeviceConfig, SensorManager sensorManager) { 327 List<BrightnessStateModifier> modifiers = new ArrayList<>(); 328 modifiers.add(new DisplayDimModifier(context)); 329 modifiers.add(new BrightnessLowPowerModeModifier()); 330 if (flags.isEvenDimmerEnabled() && displayDeviceConfig != null 331 && displayDeviceConfig.isEvenDimmerAvailable()) { 332 modifiers.add(new BrightnessLowLuxModifier(handler, listener, context, 333 displayDeviceConfig)); 334 } 335 return modifiers; 336 } 337 getLightSensor(SensorManager sensorManager, String type, String name)338 Sensor getLightSensor(SensorManager sensorManager, String type, String name) { 339 return SensorUtils.findSensor(sensorManager, type, 340 name, Sensor.TYPE_LIGHT); 341 } 342 343 } 344 345 /** 346 * Config Data for clampers 347 */ 348 public static class DisplayDeviceData implements BrightnessThermalClamper.ThermalData, 349 BrightnessPowerClamper.PowerData, 350 BrightnessWearBedtimeModeClamper.WearBedtimeModeData { 351 @NonNull 352 private final String mUniqueDisplayId; 353 @NonNull 354 private final String mThermalThrottlingDataId; 355 @NonNull 356 private final String mPowerThrottlingDataId; 357 358 private final DisplayDeviceConfig mDisplayDeviceConfig; 359 DisplayDeviceData(@onNull String uniqueDisplayId, @NonNull String thermalThrottlingDataId, @NonNull String powerThrottlingDataId, @NonNull DisplayDeviceConfig displayDeviceConfig)360 public DisplayDeviceData(@NonNull String uniqueDisplayId, 361 @NonNull String thermalThrottlingDataId, 362 @NonNull String powerThrottlingDataId, 363 @NonNull DisplayDeviceConfig displayDeviceConfig) { 364 mUniqueDisplayId = uniqueDisplayId; 365 mThermalThrottlingDataId = thermalThrottlingDataId; 366 mPowerThrottlingDataId = powerThrottlingDataId; 367 mDisplayDeviceConfig = displayDeviceConfig; 368 } 369 370 371 @NonNull 372 @Override getUniqueDisplayId()373 public String getUniqueDisplayId() { 374 return mUniqueDisplayId; 375 } 376 377 @NonNull 378 @Override getThermalThrottlingDataId()379 public String getThermalThrottlingDataId() { 380 return mThermalThrottlingDataId; 381 } 382 383 @Nullable 384 @Override getThermalBrightnessThrottlingData()385 public ThermalBrightnessThrottlingData getThermalBrightnessThrottlingData() { 386 return mDisplayDeviceConfig.getThermalBrightnessThrottlingDataMapByThrottlingId().get( 387 mThermalThrottlingDataId); 388 } 389 390 @NonNull 391 @Override getPowerThrottlingDataId()392 public String getPowerThrottlingDataId() { 393 return mPowerThrottlingDataId; 394 } 395 396 @Nullable 397 @Override getPowerThrottlingData()398 public PowerThrottlingData getPowerThrottlingData() { 399 return mDisplayDeviceConfig.getPowerThrottlingDataMapByThrottlingId().get( 400 mPowerThrottlingDataId); 401 } 402 403 @Nullable 404 @Override getPowerThrottlingConfigData()405 public PowerThrottlingConfigData getPowerThrottlingConfigData() { 406 return mDisplayDeviceConfig.getPowerThrottlingConfigData(); 407 } 408 409 @Override getBrightnessWearBedtimeModeCap()410 public float getBrightnessWearBedtimeModeCap() { 411 return mDisplayDeviceConfig.getBrightnessCapForWearBedtimeMode(); 412 } 413 414 @NonNull getTempSensor()415 public SensorData getTempSensor() { 416 return mDisplayDeviceConfig.getTempSensor(); 417 } 418 } 419 maybeRegisterLightSensor()420 private void maybeRegisterLightSensor() { 421 if (mModifiers.stream().noneMatch(BrightnessStateModifier::shouldListenToLightSensor)) { 422 return; 423 } 424 425 if (mRegisteredLightSensor == mLightSensor) { 426 return; 427 } 428 429 if (mRegisteredLightSensor != null) { 430 unregisterSensorListener(); 431 } 432 433 mAmbientFilter = AmbientFilterFactory.createBrightnessFilter(TAG, mResources); 434 mSensorManager.registerListener(mLightSensorListener, 435 mLightSensor, mLightSensorRate * 1000, mHandler); 436 mRegisteredLightSensor = mLightSensor; 437 438 if (DEBUG) { 439 Slog.d(TAG, "maybeRegisterLightSensor"); 440 } 441 } 442 unregisterSensorListener()443 private void unregisterSensorListener() { 444 mSensorManager.unregisterListener(mLightSensorListener); 445 mRegisteredLightSensor = null; 446 mModifiers.forEach(mModifier -> mModifier.setAmbientLux(INVALID_LUX)); // set lux to invalid 447 if (DEBUG) { 448 Slog.d(TAG, "unregisterSensorListener"); 449 } 450 } 451 reloadLightSensorData(DisplayDeviceConfig displayDeviceConfig)452 private void reloadLightSensorData(DisplayDeviceConfig displayDeviceConfig) { 453 // The displayDeviceConfig (ddc) contains display specific preferences. When loaded, 454 // it naturally falls back to the global config.xml. 455 if (displayDeviceConfig != null 456 && displayDeviceConfig.getAmbientLightSensor() != null) { 457 // This covers both the ddc and the config.xml fallback 458 mLightSensorType = displayDeviceConfig.getAmbientLightSensor().type; 459 mLightSensorName = displayDeviceConfig.getAmbientLightSensor().name; 460 } else if (mLightSensorName == null && mLightSensorType == null) { 461 mLightSensorType = mResources.getString( 462 com.android.internal.R.string.config_displayLightSensorType); 463 mLightSensorName = ""; 464 } 465 } 466 } 467