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