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