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 com.android.server.display.DisplayDeviceConfig.DEFAULT_ID; 20 import static com.android.server.display.brightness.clamper.BrightnessClamperController.ClamperChangeListener; 21 22 import android.annotation.NonNull; 23 import android.annotation.Nullable; 24 import android.content.Context; 25 import android.hardware.display.BrightnessInfo; 26 import android.hardware.display.DisplayManagerInternal; 27 import android.os.Handler; 28 import android.os.IThermalEventListener; 29 import android.os.IThermalService; 30 import android.os.PowerManager; 31 import android.os.RemoteException; 32 import android.os.ServiceManager; 33 import android.os.Temperature; 34 import android.provider.DeviceConfigInterface; 35 import android.util.Slog; 36 37 import com.android.internal.annotations.VisibleForTesting; 38 import com.android.server.display.DisplayBrightnessState; 39 import com.android.server.display.DisplayDeviceConfig.PowerThrottlingConfigData; 40 import com.android.server.display.DisplayDeviceConfig.PowerThrottlingData; 41 import com.android.server.display.DisplayDeviceConfig.PowerThrottlingData.ThrottlingLevel; 42 import com.android.server.display.brightness.BrightnessReason; 43 import com.android.server.display.brightness.BrightnessUtils; 44 import com.android.server.display.feature.DeviceConfigParameterProvider; 45 import com.android.server.display.utils.DeviceConfigParsingUtils; 46 47 import java.io.PrintWriter; 48 import java.util.List; 49 import java.util.Map; 50 import java.util.function.BiFunction; 51 import java.util.function.Function; 52 53 54 class BrightnessPowerModifier implements BrightnessStateModifier, 55 BrightnessClamperController.DisplayDeviceDataListener, 56 BrightnessClamperController.StatefulModifier, 57 BrightnessClamperController.DeviceConfigListener { 58 59 private static final String TAG = "BrightnessPowerClamper"; 60 @NonNull 61 private final DeviceConfigParameterProvider mConfigParameterProvider; 62 @NonNull 63 private final PmicMonitor mPmicMonitor; 64 // data from DeviceConfig, for all displays, for all dataSets 65 // mapOf(uniqueDisplayId to mapOf(dataSetId to PowerThrottlingData)) 66 @NonNull 67 private Map<String, Map<String, PowerThrottlingData>> 68 mPowerThrottlingDataOverride = Map.of(); 69 // data from DisplayDeviceConfig, for particular display+dataSet 70 @Nullable 71 private PowerThrottlingData mPowerThrottlingDataFromDDC = null; 72 // Active data, if mPowerThrottlingDataOverride contains data for mUniqueDisplayId, 73 // mDataId, then use it, otherwise mPowerThrottlingDataFromDDC. 74 @Nullable 75 private PowerThrottlingData mPowerThrottlingDataActive = null; 76 @Nullable 77 private PowerThrottlingConfigData mPowerThrottlingConfigData; 78 @NonNull 79 private final ThermalLevelListener mThermalLevelListener; 80 private @Temperature.ThrottlingStatus int mCurrentThermalLevel = Temperature.THROTTLING_NONE; 81 private boolean mCurrentThermalLevelChanged = false; 82 private float mCurrentAvgPowerConsumed = 0; 83 @Nullable 84 private String mUniqueDisplayId = null; 85 @Nullable 86 private String mDataId = null; 87 private float mCurrentBrightness = PowerManager.BRIGHTNESS_INVALID; 88 private float mCustomAnimationRateSec = DisplayBrightnessState.CUSTOM_ANIMATION_RATE_NOT_SET; 89 private float mCustomAnimationRateDeviceConfig = 90 DisplayBrightnessState.CUSTOM_ANIMATION_RATE_NOT_SET; 91 private final BiFunction<String, String, ThrottlingLevel> mDataPointMapper = (key, value) -> { 92 try { 93 int status = DeviceConfigParsingUtils.parseThermalStatus(key); 94 float powerQuota = Float.parseFloat(value); 95 return new ThrottlingLevel(status, powerQuota); 96 } catch (IllegalArgumentException iae) { 97 return null; 98 } 99 }; 100 101 private final Function<List<ThrottlingLevel>, PowerThrottlingData> 102 mDataSetMapper = PowerThrottlingData::create; 103 104 protected final Handler mHandler; 105 protected final BrightnessClamperController.ClamperChangeListener mChangeListener; 106 107 private float mBrightnessCap = PowerManager.BRIGHTNESS_MAX;; 108 private boolean mApplied = false; 109 110 BrightnessPowerModifier(Handler handler, ClamperChangeListener listener, PowerData powerData, float currentBrightness)111 BrightnessPowerModifier(Handler handler, ClamperChangeListener listener, 112 PowerData powerData, float currentBrightness) { 113 this(new Injector(), handler, listener, powerData, currentBrightness); 114 } 115 116 @VisibleForTesting BrightnessPowerModifier(@onNull Injector injector, Handler handler, ClamperChangeListener listener, PowerData powerData, float currentBrightness)117 BrightnessPowerModifier(@NonNull Injector injector, Handler handler, 118 ClamperChangeListener listener, PowerData powerData, float currentBrightness) { 119 mHandler = handler; 120 mChangeListener = listener; 121 mCurrentBrightness = currentBrightness; 122 123 mPowerThrottlingConfigData = powerData.getPowerThrottlingConfigData(); 124 if (mPowerThrottlingConfigData != null) { 125 mCustomAnimationRateDeviceConfig = mPowerThrottlingConfigData.customAnimationRate; 126 } 127 mThermalLevelListener = new ThermalLevelListener(handler); 128 mPmicMonitor = 129 injector.getPmicMonitor(this::recalculatePowerQuotaChange, 130 mThermalLevelListener.getThermalService(), 131 mPowerThrottlingConfigData.pollingWindowMaxMillis, 132 mPowerThrottlingConfigData.pollingWindowMinMillis); 133 134 mConfigParameterProvider = injector.getDeviceConfigParameterProvider(); 135 mHandler.post(() -> { 136 setDisplayData(powerData); 137 loadOverrideData(); 138 start(); 139 }); 140 } 141 142 //region BrightnessStateModifier 143 @Override apply(DisplayManagerInternal.DisplayPowerRequest request, DisplayBrightnessState.Builder stateBuilder)144 public void apply(DisplayManagerInternal.DisplayPowerRequest request, 145 DisplayBrightnessState.Builder stateBuilder) { 146 if (stateBuilder.getMaxBrightness() > mBrightnessCap) { 147 stateBuilder.setMaxBrightness(mBrightnessCap); 148 stateBuilder.setBrightness(Math.min(stateBuilder.getBrightness(), mBrightnessCap)); 149 stateBuilder.setBrightnessMaxReason(BrightnessInfo.BRIGHTNESS_MAX_REASON_POWER_IC); 150 stateBuilder.getBrightnessReason().addModifier(BrightnessReason.MODIFIER_THROTTLED); 151 // set custom animation rate only when modifier is activated. 152 // this will allow auto brightness to apply slow change even when modifier is active 153 if (!mApplied) { 154 stateBuilder.setCustomAnimationRate(mCustomAnimationRateSec); 155 mCustomAnimationRateSec = DisplayBrightnessState.CUSTOM_ANIMATION_RATE_NOT_SET; 156 } 157 mApplied = true; 158 } else { 159 mApplied = false; 160 } 161 mCurrentBrightness = stateBuilder.getBrightness(); 162 } 163 164 @Override stop()165 public void stop() { 166 mPmicMonitor.shutdown(); 167 mThermalLevelListener.stop(); 168 } 169 170 /** 171 * Dumps the state of BrightnessPowerClamper. 172 */ dump(PrintWriter pw)173 public void dump(PrintWriter pw) { 174 pw.println("BrightnessPowerClamper:"); 175 pw.println(" mCurrentAvgPowerConsumed=" + mCurrentAvgPowerConsumed); 176 pw.println(" mUniqueDisplayId=" + mUniqueDisplayId); 177 pw.println(" mCurrentThermalLevel=" + mCurrentThermalLevel); 178 pw.println(" mCurrentThermalLevelChanged=" + mCurrentThermalLevelChanged); 179 pw.println(" mPowerThrottlingDataFromDDC=" + (mPowerThrottlingDataFromDDC == null ? "null" 180 : mPowerThrottlingDataFromDDC.toString())); 181 pw.println(" mBrightnessCap: " + mBrightnessCap); 182 pw.println(" mApplied: " + mApplied); 183 mThermalLevelListener.dump(pw); 184 } 185 186 @Override shouldListenToLightSensor()187 public boolean shouldListenToLightSensor() { 188 return false; 189 } 190 191 @Override setAmbientLux(float lux)192 public void setAmbientLux(float lux) { 193 // noop 194 } 195 //endregion 196 197 //region DisplayDeviceDataListener 198 @Override onDisplayChanged(BrightnessClamperController.DisplayDeviceData data)199 public void onDisplayChanged(BrightnessClamperController.DisplayDeviceData data) { 200 mHandler.post(() -> { 201 setDisplayData(data); 202 recalculateActiveData(); 203 }); 204 } 205 //endregion 206 207 //region StatefulModifier 208 @Override applyStateChange( BrightnessClamperController.ModifiersAggregatedState aggregatedState)209 public void applyStateChange( 210 BrightnessClamperController.ModifiersAggregatedState aggregatedState) { 211 if (aggregatedState.mMaxBrightness > mBrightnessCap) { 212 aggregatedState.mMaxBrightness = mBrightnessCap; 213 aggregatedState.mMaxBrightnessReason = BrightnessInfo.BRIGHTNESS_MAX_REASON_POWER_IC; 214 } 215 } 216 //endregion 217 218 //region DeviceConfigListener 219 @Override onDeviceConfigChanged()220 public void onDeviceConfigChanged() { 221 mHandler.post(() -> { 222 loadOverrideData(); 223 recalculateActiveData(); 224 }); 225 } 226 //endregion 227 recalculateActiveData()228 private void recalculateActiveData() { 229 if (mUniqueDisplayId == null || mDataId == null) { 230 return; 231 } 232 mPowerThrottlingDataActive = mPowerThrottlingDataOverride 233 .getOrDefault(mUniqueDisplayId, Map.of()).getOrDefault(mDataId, 234 mPowerThrottlingDataFromDDC); 235 if (mPowerThrottlingDataActive == null) { 236 mPmicMonitor.stop(); 237 } 238 } 239 loadOverrideData()240 private void loadOverrideData() { 241 String throttlingDataOverride = mConfigParameterProvider.getPowerThrottlingData(); 242 mPowerThrottlingDataOverride = DeviceConfigParsingUtils.parseDeviceConfigMap( 243 throttlingDataOverride, mDataPointMapper, mDataSetMapper); 244 } 245 setDisplayData(@onNull PowerData data)246 private void setDisplayData(@NonNull PowerData data) { 247 mUniqueDisplayId = data.getUniqueDisplayId(); 248 mDataId = data.getPowerThrottlingDataId(); 249 mPowerThrottlingDataFromDDC = data.getPowerThrottlingData(); 250 if (mPowerThrottlingDataFromDDC == null && !DEFAULT_ID.equals(mDataId)) { 251 Slog.wtf(TAG, 252 "Power throttling data is missing for powerThrottlingDataId=" + mDataId); 253 } 254 255 mPowerThrottlingConfigData = data.getPowerThrottlingConfigData(); 256 } 257 recalculateBrightnessCap()258 private void recalculateBrightnessCap() { 259 float targetBrightnessCap = PowerManager.BRIGHTNESS_MAX; 260 float powerQuota = getPowerQuotaForThermalStatus(mCurrentThermalLevel); 261 if (mPowerThrottlingDataActive == null) { 262 return; 263 } 264 if (powerQuota > 0) { 265 if (BrightnessUtils.isValidBrightnessValue(mCurrentBrightness) 266 && (mCurrentAvgPowerConsumed > powerQuota)) { 267 // calculate new brightness Cap. 268 // Brightness has a linear relation to power-consumed. 269 targetBrightnessCap = 270 (powerQuota / mCurrentAvgPowerConsumed) * mCurrentBrightness; 271 } else if (mCurrentThermalLevelChanged) { 272 if (mCurrentThermalLevel == Temperature.THROTTLING_NONE) { 273 // reset pmic and remove the power-throttling cap. 274 targetBrightnessCap = PowerManager.BRIGHTNESS_MAX; 275 mPmicMonitor.stop(); 276 } else { 277 // Since the thermal status has changed, we need to remove power-throttling cap. 278 // Instead of recalculating and changing brightness again, adding flicker, 279 // we will wait for the next pmic cycle to re-evaluate this value 280 // make act on it, if needed. 281 targetBrightnessCap = PowerManager.BRIGHTNESS_MAX; 282 if (mPmicMonitor.isStopped()) { 283 mPmicMonitor.start(); 284 } 285 } 286 } else { // Current power consumed is under the quota. 287 targetBrightnessCap = PowerManager.BRIGHTNESS_MAX; 288 } 289 } 290 291 // Cap to lowest allowed brightness on device. 292 if (mPowerThrottlingConfigData != null) { 293 targetBrightnessCap = Math.max(targetBrightnessCap, 294 mPowerThrottlingConfigData.brightnessLowestCapAllowed); 295 } 296 297 if (mBrightnessCap != targetBrightnessCap) { 298 Slog.i(TAG, "Power clamper changing current brightness cap mBrightnessCap: " 299 + mBrightnessCap + " to target brightness cap:" + targetBrightnessCap 300 + " for current screen brightness: " + mCurrentBrightness + "\n" 301 + "Power clamper changed state: thermalStatus:" + mCurrentThermalLevel 302 + " mCurrentThermalLevelChanged:" + mCurrentThermalLevelChanged 303 + " mCurrentAvgPowerConsumed:" + mCurrentAvgPowerConsumed 304 + " mCustomAnimationRateSec:" + mCustomAnimationRateDeviceConfig); 305 mBrightnessCap = targetBrightnessCap; 306 mCustomAnimationRateSec = mCustomAnimationRateDeviceConfig; 307 mChangeListener.onChanged(); 308 } else { 309 mCustomAnimationRateSec = DisplayBrightnessState.CUSTOM_ANIMATION_RATE_NOT_SET; 310 } 311 } 312 getPowerQuotaForThermalStatus(@emperature.ThrottlingStatus int thermalStatus)313 private float getPowerQuotaForThermalStatus(@Temperature.ThrottlingStatus int thermalStatus) { 314 float powerQuota = 0f; 315 if (mPowerThrottlingDataActive != null) { 316 // Throttling levels are sorted by increasing severity 317 for (ThrottlingLevel level : mPowerThrottlingDataActive.throttlingLevels) { 318 if (level.thermalStatus <= thermalStatus) { 319 powerQuota = level.powerQuotaMilliWatts; 320 } else { 321 // Throttling levels that are greater than the current status are irrelevant 322 break; 323 } 324 } 325 } 326 return powerQuota; 327 } 328 recalculatePowerQuotaChange(float avgPowerConsumed, int thermalStatus)329 private void recalculatePowerQuotaChange(float avgPowerConsumed, int thermalStatus) { 330 mHandler.post(() -> { 331 mCurrentThermalLevelChanged = mCurrentThermalLevel != thermalStatus; 332 mCurrentThermalLevel = thermalStatus; 333 mCurrentAvgPowerConsumed = avgPowerConsumed; 334 recalculateBrightnessCap(); 335 }); 336 } 337 start()338 private void start() { 339 if (mPowerThrottlingConfigData == null) { 340 return; 341 } 342 if (mPowerThrottlingConfigData.pollingWindowMaxMillis 343 <= mPowerThrottlingConfigData.pollingWindowMinMillis) { 344 Slog.e(TAG, "Brightness power max polling window:" 345 + mPowerThrottlingConfigData.pollingWindowMaxMillis 346 + " msec, should be greater than brightness min polling window:" 347 + mPowerThrottlingConfigData.pollingWindowMinMillis + " msec."); 348 return; 349 } 350 if ((mPowerThrottlingConfigData.pollingWindowMaxMillis 351 % mPowerThrottlingConfigData.pollingWindowMinMillis) != 0) { 352 Slog.e(TAG, "Brightness power max polling window:" 353 + mPowerThrottlingConfigData.pollingWindowMaxMillis 354 + " msec, is not divisible by brightness min polling window:" 355 + mPowerThrottlingConfigData.pollingWindowMinMillis + " msec."); 356 return; 357 } 358 mCustomAnimationRateDeviceConfig = mPowerThrottlingConfigData.customAnimationRate; 359 mThermalLevelListener.start(); 360 } 361 activatePmicMonitor()362 private void activatePmicMonitor() { 363 if (!mPmicMonitor.isStopped()) { 364 return; 365 } 366 mPmicMonitor.start(); 367 } 368 deactivatePmicMonitor(@emperature.ThrottlingStatus int status)369 private void deactivatePmicMonitor(@Temperature.ThrottlingStatus int status) { 370 if (status != Temperature.THROTTLING_NONE) { 371 return; 372 } 373 if (mPmicMonitor.isStopped()) { 374 return; 375 } 376 mPmicMonitor.stop(); 377 } 378 379 private final class ThermalLevelListener extends IThermalEventListener.Stub { 380 private final Handler mHandler; 381 private IThermalService mThermalService; 382 private boolean mStarted; 383 ThermalLevelListener(Handler handler)384 ThermalLevelListener(Handler handler) { 385 mHandler = handler; 386 mStarted = false; 387 mThermalService = IThermalService.Stub.asInterface( 388 ServiceManager.getService(Context.THERMAL_SERVICE)); 389 } 390 getThermalService()391 IThermalService getThermalService() { 392 return mThermalService; 393 } 394 start()395 void start() { 396 if (mStarted) { 397 return; 398 } 399 if (mThermalService == null) { 400 return; 401 } 402 try { 403 // TODO b/279114539 Try DISPLAY first and then fallback to SKIN. 404 mThermalService.registerThermalEventListenerWithType(this, Temperature.TYPE_SKIN); 405 mStarted = true; 406 } catch (RemoteException e) { 407 Slog.e(TAG, "Failed to register thermal status listener", e); 408 } 409 } 410 411 @Override notifyThrottling(Temperature temp)412 public void notifyThrottling(Temperature temp) { 413 @Temperature.ThrottlingStatus int status = temp.getStatus(); 414 if (status >= Temperature.THROTTLING_LIGHT) { 415 Slog.d(TAG, "Activating pmic monitor due to thermal state:" + status); 416 mHandler.post(BrightnessPowerModifier.this::activatePmicMonitor); 417 } else { 418 if (!mPmicMonitor.isStopped()) { 419 mHandler.post(() -> deactivatePmicMonitor(status)); 420 } 421 } 422 } 423 stop()424 void stop() { 425 if (!mStarted) { 426 return; 427 } 428 try { 429 mThermalService.unregisterThermalEventListener(this); 430 mStarted = false; 431 } catch (RemoteException e) { 432 Slog.e(TAG, "Failed to unregister thermal status listener", e); 433 } 434 mThermalService = null; 435 } 436 dump(PrintWriter writer)437 void dump(PrintWriter writer) { 438 writer.println(" ThermalLevelObserver:"); 439 writer.println(" mStarted: " + mStarted); 440 } 441 } 442 443 public interface PowerData { 444 @NonNull getUniqueDisplayId()445 String getUniqueDisplayId(); 446 447 @NonNull getPowerThrottlingDataId()448 String getPowerThrottlingDataId(); 449 450 @Nullable getPowerThrottlingData()451 PowerThrottlingData getPowerThrottlingData(); 452 453 @Nullable getPowerThrottlingConfigData()454 PowerThrottlingConfigData getPowerThrottlingConfigData(); 455 } 456 457 /** 458 * Power change listener 459 */ 460 @FunctionalInterface 461 public interface PowerChangeListener { 462 /** 463 * Notifies that power state changed from power controller. 464 */ onChanged(float avgPowerConsumed, @Temperature.ThrottlingStatus int thermalStatus)465 void onChanged(float avgPowerConsumed, @Temperature.ThrottlingStatus int thermalStatus); 466 } 467 468 @VisibleForTesting 469 static class Injector { 470 @NonNull getPmicMonitor(PowerChangeListener powerChangeListener, IThermalService thermalService, int pollingMaxTimeMillis, int pollingMinTimeMillis)471 PmicMonitor getPmicMonitor(PowerChangeListener powerChangeListener, 472 IThermalService thermalService, 473 int pollingMaxTimeMillis, 474 int pollingMinTimeMillis) { 475 return new PmicMonitor(powerChangeListener, thermalService, pollingMaxTimeMillis, 476 pollingMinTimeMillis); 477 } 478 getDeviceConfigParameterProvider()479 DeviceConfigParameterProvider getDeviceConfigParameterProvider() { 480 return new DeviceConfigParameterProvider(DeviceConfigInterface.REAL); 481 } 482 } 483 } 484