1 /* 2 * Copyright (C) 2022 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; 18 19 import static com.android.server.display.DisplayDeviceConfig.DEFAULT_ID; 20 21 import android.annotation.NonNull; 22 import android.annotation.Nullable; 23 import android.content.Context; 24 import android.hardware.display.BrightnessInfo; 25 import android.os.Handler; 26 import android.os.HandlerExecutor; 27 import android.os.IThermalEventListener; 28 import android.os.IThermalService; 29 import android.os.PowerManager; 30 import android.os.RemoteException; 31 import android.os.ServiceManager; 32 import android.os.Temperature; 33 import android.provider.DeviceConfig; 34 import android.provider.DeviceConfigInterface; 35 import android.util.Slog; 36 37 import com.android.internal.annotations.VisibleForTesting; 38 import com.android.server.display.DisplayDeviceConfig.ThermalBrightnessThrottlingData; 39 import com.android.server.display.DisplayDeviceConfig.ThermalBrightnessThrottlingData.ThrottlingLevel; 40 import com.android.server.display.config.SensorData; 41 import com.android.server.display.feature.DeviceConfigParameterProvider; 42 import com.android.server.display.utils.DebugUtils; 43 import com.android.server.display.utils.DeviceConfigParsingUtils; 44 import com.android.server.display.utils.SensorUtils; 45 46 import java.io.PrintWriter; 47 import java.util.HashMap; 48 import java.util.List; 49 import java.util.Map; 50 import java.util.concurrent.Executor; 51 import java.util.function.BiFunction; 52 import java.util.function.Function; 53 54 /** 55 * This class monitors various conditions, such as skin temperature throttling status, and limits 56 * the allowed brightness range accordingly. 57 * 58 * @deprecated will be replaced by 59 * {@link com.android.server.display.brightness.clamper.BrightnessThermalClamper} 60 */ 61 @Deprecated 62 class BrightnessThrottler { 63 private static final String TAG = "BrightnessThrottler"; 64 65 // To enable these logs, run: 66 // 'adb shell setprop persist.log.tag.BrightnessThrottler DEBUG && adb reboot' 67 private static final boolean DEBUG = DebugUtils.isDebuggable(TAG); 68 private static final int THROTTLING_INVALID = -1; 69 70 private final Injector mInjector; 71 private final Handler mHandler; 72 // We need a separate handler for unit testing. These two handlers are the same throughout the 73 // non-test code. 74 private final Handler mDeviceConfigHandler; 75 private final Runnable mThrottlingChangeCallback; 76 private final SkinThermalStatusObserver mSkinThermalStatusObserver; 77 private final DeviceConfigListener mDeviceConfigListener; 78 private final DeviceConfigParameterProvider mConfigParameterProvider; 79 80 private int mThrottlingStatus; 81 82 // Maps the throttling ID to the data. Sourced from DisplayDeviceConfig. 83 @NonNull 84 private Map<String, ThermalBrightnessThrottlingData> mDdcThermalThrottlingDataMap; 85 86 // Current throttling data being used. 87 // Null if we do not support throttling. 88 @Nullable 89 private ThermalBrightnessThrottlingData mThermalThrottlingData; 90 91 private float mBrightnessCap = PowerManager.BRIGHTNESS_MAX; 92 private @BrightnessInfo.BrightnessMaxReason int mBrightnessMaxReason = 93 BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE; 94 private String mUniqueDisplayId; 95 96 // The most recent string that has been set from DeviceConfig 97 private String mThermalBrightnessThrottlingDataString; 98 99 // The brightness throttling configuration that should be used. 100 private String mThermalBrightnessThrottlingDataId; 101 102 // Temperature Sensor to be monitored for throttling. 103 @NonNull 104 private SensorData mTempSensor; 105 106 // This is a collection of brightness throttling data that has been written as overrides from 107 // the DeviceConfig. This will always take priority over the display device config data. 108 // We need to store the data for every display device, so we do not need to update this each 109 // time the underlying display device changes. 110 // This map is indexed by uniqueDisplayId, to provide maps for throttlingId -> throttlingData. 111 // HashMap< uniqueDisplayId, HashMap< throttlingDataId, ThermalBrightnessThrottlingData >> 112 private final Map<String, Map<String, ThermalBrightnessThrottlingData>> 113 mThermalBrightnessThrottlingDataOverride = new HashMap<>(); 114 115 private final BiFunction<String, String, ThrottlingLevel> mDataPointMapper = (key, value) -> { 116 try { 117 int status = DeviceConfigParsingUtils.parseThermalStatus(key); 118 float brightnessPoint = DeviceConfigParsingUtils.parseBrightness(value); 119 return new ThrottlingLevel(status, brightnessPoint); 120 } catch (IllegalArgumentException iae) { 121 return null; 122 } 123 }; 124 125 private final Function<List<ThrottlingLevel>, ThermalBrightnessThrottlingData> 126 mDataSetMapper = ThermalBrightnessThrottlingData::create; 127 BrightnessThrottler(Handler handler, Runnable throttlingChangeCallback, String uniqueDisplayId, String throttlingDataId, @NonNull DisplayDeviceConfig displayDeviceConfig)128 BrightnessThrottler(Handler handler, Runnable throttlingChangeCallback, String uniqueDisplayId, 129 String throttlingDataId, 130 @NonNull DisplayDeviceConfig displayDeviceConfig) { 131 this(new Injector(), handler, handler, throttlingChangeCallback, uniqueDisplayId, 132 throttlingDataId, 133 displayDeviceConfig.getThermalBrightnessThrottlingDataMapByThrottlingId(), 134 displayDeviceConfig.getTempSensor()); 135 } 136 137 @VisibleForTesting BrightnessThrottler(Injector injector, Handler handler, Handler deviceConfigHandler, Runnable throttlingChangeCallback, String uniqueDisplayId, String throttlingDataId, @NonNull Map<String, ThermalBrightnessThrottlingData> thermalBrightnessThrottlingDataMap, @NonNull SensorData tempSensor)138 BrightnessThrottler(Injector injector, Handler handler, Handler deviceConfigHandler, 139 Runnable throttlingChangeCallback, String uniqueDisplayId, String throttlingDataId, 140 @NonNull Map<String, ThermalBrightnessThrottlingData> 141 thermalBrightnessThrottlingDataMap, 142 @NonNull SensorData tempSensor) { 143 mInjector = injector; 144 145 mHandler = handler; 146 mDeviceConfigHandler = deviceConfigHandler; 147 mDdcThermalThrottlingDataMap = thermalBrightnessThrottlingDataMap; 148 mThrottlingChangeCallback = throttlingChangeCallback; 149 mSkinThermalStatusObserver = new SkinThermalStatusObserver(mInjector, mHandler); 150 151 mUniqueDisplayId = uniqueDisplayId; 152 mConfigParameterProvider = new DeviceConfigParameterProvider(injector.getDeviceConfig()); 153 mDeviceConfigListener = new DeviceConfigListener(); 154 mThermalBrightnessThrottlingDataId = throttlingDataId; 155 mDdcThermalThrottlingDataMap = thermalBrightnessThrottlingDataMap; 156 loadThermalBrightnessThrottlingDataFromDeviceConfig(); 157 loadThermalBrightnessThrottlingDataFromDisplayDeviceConfig(mDdcThermalThrottlingDataMap, 158 tempSensor, mThermalBrightnessThrottlingDataId, mUniqueDisplayId); 159 } 160 deviceSupportsThrottling()161 boolean deviceSupportsThrottling() { 162 return mThermalThrottlingData != null; 163 } 164 getBrightnessCap()165 float getBrightnessCap() { 166 return mBrightnessCap; 167 } 168 getBrightnessMaxReason()169 int getBrightnessMaxReason() { 170 return mBrightnessMaxReason; 171 } 172 isThrottled()173 boolean isThrottled() { 174 return mBrightnessMaxReason != BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE; 175 } 176 stop()177 void stop() { 178 mSkinThermalStatusObserver.stopObserving(); 179 mConfigParameterProvider.removeOnPropertiesChangedListener(mDeviceConfigListener); 180 // We're asked to stop throttling, so reset brightness restrictions. 181 mBrightnessCap = PowerManager.BRIGHTNESS_MAX; 182 mBrightnessMaxReason = BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE; 183 184 // We set throttling status to an invalid value here so that we act on the first throttling 185 // value received from the thermal service after registration, even if that throttling value 186 // is THROTTLING_NONE. 187 mThrottlingStatus = THROTTLING_INVALID; 188 } 189 loadThermalBrightnessThrottlingDataFromDisplayDeviceConfig( Map<String, ThermalBrightnessThrottlingData> ddcThrottlingDataMap, SensorData tempSensor, String brightnessThrottlingDataId, String uniqueDisplayId)190 void loadThermalBrightnessThrottlingDataFromDisplayDeviceConfig( 191 Map<String, ThermalBrightnessThrottlingData> ddcThrottlingDataMap, 192 SensorData tempSensor, 193 String brightnessThrottlingDataId, 194 String uniqueDisplayId) { 195 mDdcThermalThrottlingDataMap = ddcThrottlingDataMap; 196 mThermalBrightnessThrottlingDataId = brightnessThrottlingDataId; 197 mUniqueDisplayId = uniqueDisplayId; 198 mTempSensor = tempSensor; 199 resetThermalThrottlingData(); 200 } 201 verifyAndConstrainBrightnessCap(float brightness)202 private float verifyAndConstrainBrightnessCap(float brightness) { 203 if (brightness < PowerManager.BRIGHTNESS_MIN) { 204 Slog.e(TAG, "brightness " + brightness + " is lower than the minimum possible " 205 + "brightness " + PowerManager.BRIGHTNESS_MIN); 206 brightness = PowerManager.BRIGHTNESS_MIN; 207 } 208 209 if (brightness > PowerManager.BRIGHTNESS_MAX) { 210 Slog.e(TAG, "brightness " + brightness + " is higher than the maximum possible " 211 + "brightness " + PowerManager.BRIGHTNESS_MAX); 212 brightness = PowerManager.BRIGHTNESS_MAX; 213 } 214 215 return brightness; 216 } 217 thermalStatusChanged(@emperature.ThrottlingStatus int newStatus)218 private void thermalStatusChanged(@Temperature.ThrottlingStatus int newStatus) { 219 if (mThrottlingStatus != newStatus) { 220 mThrottlingStatus = newStatus; 221 updateThermalThrottling(); 222 } 223 } 224 updateThermalThrottling()225 private void updateThermalThrottling() { 226 if (!deviceSupportsThrottling()) { 227 return; 228 } 229 230 float brightnessCap = PowerManager.BRIGHTNESS_MAX; 231 int brightnessMaxReason = BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE; 232 233 if (mThrottlingStatus != THROTTLING_INVALID && mThermalThrottlingData != null) { 234 // Throttling levels are sorted by increasing severity 235 for (ThrottlingLevel level : mThermalThrottlingData.throttlingLevels) { 236 if (level.thermalStatus <= mThrottlingStatus) { 237 brightnessCap = level.brightness; 238 brightnessMaxReason = BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL; 239 } else { 240 // Throttling levels that are greater than the current status are irrelevant 241 break; 242 } 243 } 244 } 245 246 if (mBrightnessCap != brightnessCap || mBrightnessMaxReason != brightnessMaxReason) { 247 mBrightnessCap = verifyAndConstrainBrightnessCap(brightnessCap); 248 mBrightnessMaxReason = brightnessMaxReason; 249 250 if (DEBUG) { 251 Slog.d(TAG, "State changed: mBrightnessCap = " + mBrightnessCap 252 + ", mBrightnessMaxReason = " 253 + BrightnessInfo.briMaxReasonToString(mBrightnessMaxReason)); 254 } 255 256 if (mThrottlingChangeCallback != null) { 257 mThrottlingChangeCallback.run(); 258 } 259 } 260 } 261 dump(PrintWriter pw)262 void dump(PrintWriter pw) { 263 mHandler.runWithScissors(() -> dumpLocal(pw), 1000); 264 } 265 dumpLocal(PrintWriter pw)266 private void dumpLocal(PrintWriter pw) { 267 pw.println("BrightnessThrottler:"); 268 pw.println(" mThermalBrightnessThrottlingDataId=" + mThermalBrightnessThrottlingDataId); 269 pw.println(" mThermalThrottlingData=" + mThermalThrottlingData); 270 pw.println(" mUniqueDisplayId=" + mUniqueDisplayId); 271 pw.println(" mThrottlingStatus=" + mThrottlingStatus); 272 pw.println(" mBrightnessCap=" + mBrightnessCap); 273 pw.println(" mBrightnessMaxReason=" + 274 BrightnessInfo.briMaxReasonToString(mBrightnessMaxReason)); 275 pw.println(" mDdcThermalThrottlingDataMap=" + mDdcThermalThrottlingDataMap); 276 pw.println(" mThermalBrightnessThrottlingDataOverride=" 277 + mThermalBrightnessThrottlingDataOverride); 278 pw.println(" mThermalBrightnessThrottlingDataString=" 279 + mThermalBrightnessThrottlingDataString); 280 281 mSkinThermalStatusObserver.dump(pw); 282 } 283 284 // The brightness throttling data id may or may not be specified in the string that is passed 285 // in, if there is none specified, we assume it is for the default case. Each string passed in 286 // here must be for one display and one throttling id. 287 // 123,1,critical,0.8 288 // 456,2,moderate,0.9,critical,0.7 289 // 456,2,moderate,0.9,critical,0.7,default 290 // 456,2,moderate,0.9,critical,0.7,id_2 291 // displayId, number, <state, val> * number 292 // displayId, <number, <state, val> * number>, throttlingId loadThermalBrightnessThrottlingDataFromDeviceConfig()293 private void loadThermalBrightnessThrottlingDataFromDeviceConfig() { 294 mThermalBrightnessThrottlingDataString = 295 mConfigParameterProvider.getBrightnessThrottlingData(); 296 mThermalBrightnessThrottlingDataOverride.clear(); 297 if (mThermalBrightnessThrottlingDataString != null) { 298 Map<String, Map<String, ThermalBrightnessThrottlingData>> tempThrottlingData = 299 DeviceConfigParsingUtils.parseDeviceConfigMap( 300 mThermalBrightnessThrottlingDataString, mDataPointMapper, mDataSetMapper); 301 mThermalBrightnessThrottlingDataOverride.putAll(tempThrottlingData); 302 } else { 303 Slog.w(TAG, "DeviceConfig ThermalBrightnessThrottlingData is null"); 304 } 305 } 306 resetThermalThrottlingData()307 private void resetThermalThrottlingData() { 308 stop(); 309 310 mDeviceConfigListener.startListening(); 311 312 // Get throttling data for this id, if it exists 313 mThermalThrottlingData = getConfigFromId(mThermalBrightnessThrottlingDataId); 314 315 // Fallback to default id otherwise. 316 if (!DEFAULT_ID.equals(mThermalBrightnessThrottlingDataId) 317 && mThermalThrottlingData == null) { 318 mThermalThrottlingData = getConfigFromId(DEFAULT_ID); 319 Slog.d(TAG, "Falling back to default throttling Id"); 320 } 321 322 if (deviceSupportsThrottling()) { 323 mSkinThermalStatusObserver.startObserving(mTempSensor); 324 } 325 } 326 getConfigFromId(String id)327 private ThermalBrightnessThrottlingData getConfigFromId(String id) { 328 ThermalBrightnessThrottlingData returnValue; 329 330 // Fallback pattern for fetching correct throttling data for this display and id. 331 // 1) throttling data from device config for this throttling data id 332 returnValue = mThermalBrightnessThrottlingDataOverride.get(mUniqueDisplayId) == null 333 ? null 334 : mThermalBrightnessThrottlingDataOverride.get(mUniqueDisplayId).get(id); 335 // 2) throttling data from ddc for this throttling data id 336 returnValue = returnValue == null 337 ? mDdcThermalThrottlingDataMap.get(id) 338 : returnValue; 339 340 return returnValue; 341 } 342 343 /** 344 * Listens to config data change and updates the brightness throttling data using 345 * DisplayManager#KEY_BRIGHTNESS_THROTTLING_DATA. 346 * The format should be a string similar to: "local:4619827677550801152,2,moderate,0.5,severe, 347 * 0.379518072;local:4619827677550801151,1,moderate,0.75" 348 * In this order: 349 * <displayId>,<no of throttling levels>,[<severity as string>,<brightness cap>][,throttlingId]? 350 * Where [<severity as string>,<brightness cap>] is repeated for each throttling level, and the 351 * entirety is repeated for each display & throttling data id, separated by a semicolon. 352 */ 353 public class DeviceConfigListener implements DeviceConfig.OnPropertiesChangedListener { 354 public Executor mExecutor = new HandlerExecutor(mDeviceConfigHandler); 355 startListening()356 public void startListening() { 357 mConfigParameterProvider.addOnPropertiesChangedListener(mExecutor, this); 358 } 359 360 @Override onPropertiesChanged(DeviceConfig.Properties properties)361 public void onPropertiesChanged(DeviceConfig.Properties properties) { 362 loadThermalBrightnessThrottlingDataFromDeviceConfig(); 363 resetThermalThrottlingData(); 364 } 365 } 366 367 private final class SkinThermalStatusObserver extends IThermalEventListener.Stub { 368 private final Injector mInjector; 369 private final Handler mHandler; 370 private SensorData mObserverTempSensor; 371 372 private IThermalService mThermalService; 373 private boolean mStarted; 374 SkinThermalStatusObserver(Injector injector, Handler handler)375 SkinThermalStatusObserver(Injector injector, Handler handler) { 376 mInjector = injector; 377 mHandler = handler; 378 } 379 380 @Override notifyThrottling(Temperature temp)381 public void notifyThrottling(Temperature temp) { 382 if (DEBUG) { 383 Slog.d(TAG, "New thermal throttling status = " + temp.getStatus()); 384 } 385 386 if (mObserverTempSensor.name != null 387 && !mObserverTempSensor.name.equals(temp.getName())) { 388 Slog.i(TAG, "Skipping thermal throttling notification as monitored sensor: " 389 + mObserverTempSensor.name 390 + " != notified sensor: " 391 + temp.getName()); 392 return; 393 } 394 mHandler.post(() -> { 395 final @Temperature.ThrottlingStatus int status = temp.getStatus(); 396 thermalStatusChanged(status); 397 }); 398 } 399 startObserving(SensorData tempSensor)400 void startObserving(SensorData tempSensor) { 401 if (!mStarted || mObserverTempSensor == null) { 402 mObserverTempSensor = tempSensor; 403 registerThermalListener(); 404 return; 405 } 406 407 String curType = mObserverTempSensor.type; 408 mObserverTempSensor = tempSensor; 409 if (curType.equals(tempSensor.type)) { 410 if (DEBUG) { 411 Slog.d(TAG, "Thermal status observer already started"); 412 } 413 return; 414 } 415 stopObserving(); 416 registerThermalListener(); 417 } 418 registerThermalListener()419 void registerThermalListener() { 420 mThermalService = mInjector.getThermalService(); 421 if (mThermalService == null) { 422 Slog.e(TAG, "Could not observe thermal status. Service not available"); 423 return; 424 } 425 int temperatureType = SensorUtils.getSensorTemperatureType(mObserverTempSensor); 426 try { 427 // We get a callback immediately upon registering so there's no need to query 428 // for the current value. 429 mThermalService.registerThermalEventListenerWithType(this, temperatureType); 430 mStarted = true; 431 } catch (RemoteException e) { 432 Slog.e(TAG, "Failed to register thermal status listener", e); 433 } 434 } 435 stopObserving()436 void stopObserving() { 437 if (!mStarted) { 438 if (DEBUG) { 439 Slog.d(TAG, "Stop skipped because thermal status observer not started"); 440 } 441 return; 442 } 443 try { 444 mThermalService.unregisterThermalEventListener(this); 445 mStarted = false; 446 } catch (RemoteException e) { 447 Slog.e(TAG, "Failed to unregister thermal status listener", e); 448 } 449 mThermalService = null; 450 } 451 dump(PrintWriter writer)452 void dump(PrintWriter writer) { 453 writer.println(" SkinThermalStatusObserver:"); 454 writer.println(" mStarted: " + mStarted); 455 writer.println(" mObserverTempSensor: " + mObserverTempSensor); 456 if (mThermalService != null) { 457 writer.println(" ThermalService available"); 458 } else { 459 writer.println(" ThermalService not available"); 460 } 461 } 462 } 463 464 public static class Injector { getThermalService()465 public IThermalService getThermalService() { 466 return IThermalService.Stub.asInterface( 467 ServiceManager.getService(Context.THERMAL_SERVICE)); 468 } 469 470 @NonNull getDeviceConfig()471 public DeviceConfigInterface getDeviceConfig() { 472 return DeviceConfigInterface.REAL; 473 } 474 } 475 } 476