• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.ThermalBrightnessThrottlingData;
40 import com.android.server.display.DisplayDeviceConfig.ThermalBrightnessThrottlingData.ThrottlingLevel;
41 import com.android.server.display.brightness.BrightnessReason;
42 import com.android.server.display.config.SensorData;
43 import com.android.server.display.feature.DeviceConfigParameterProvider;
44 import com.android.server.display.utils.DeviceConfigParsingUtils;
45 import com.android.server.display.utils.SensorUtils;
46 
47 import java.io.PrintWriter;
48 import java.util.List;
49 import java.util.Map;
50 import java.util.Objects;
51 import java.util.function.BiFunction;
52 import java.util.function.Function;
53 
54 
55 class BrightnessThermalModifier implements BrightnessStateModifier,
56         BrightnessClamperController.DisplayDeviceDataListener,
57         BrightnessClamperController.StatefulModifier,
58         BrightnessClamperController.DeviceConfigListener {
59 
60     private static final String TAG = "BrightnessThermalClamper";
61     @NonNull
62     private final ThermalStatusObserver mThermalStatusObserver;
63     @NonNull
64     private final DeviceConfigParameterProvider mConfigParameterProvider;
65     // data from DeviceConfig, for all displays, for all dataSets
66     // mapOf(uniqueDisplayId to mapOf(dataSetId to ThermalBrightnessThrottlingData))
67     @NonNull
68     protected final Handler mHandler;
69     @NonNull
70     protected final BrightnessClamperController.ClamperChangeListener mChangeListener;
71 
72     @NonNull
73     private Map<String, Map<String, ThermalBrightnessThrottlingData>>
74             mThermalThrottlingDataOverride = Map.of();
75     // data from DisplayDeviceConfig, for particular display+dataSet
76     @Nullable
77     private ThermalBrightnessThrottlingData mThermalThrottlingDataFromDeviceConfig = null;
78     // Active data, if mDataOverride contains data for mUniqueDisplayId, mDataId, then use it,
79     // otherwise mDataFromDeviceConfig
80     @Nullable
81     private ThermalBrightnessThrottlingData mThermalThrottlingDataActive = null;
82     @Nullable
83     private String mUniqueDisplayId = null;
84     @Nullable
85     private String mDataId = null;
86     @Temperature.ThrottlingStatus
87     private int mThrottlingStatus = Temperature.THROTTLING_NONE;
88     private float mBrightnessCap = PowerManager.BRIGHTNESS_MAX;
89     private boolean mApplied = false;
90 
91     private final BiFunction<String, String, ThrottlingLevel> mDataPointMapper = (key, value) -> {
92         try {
93             int status = DeviceConfigParsingUtils.parseThermalStatus(key);
94             float brightnessPoint = DeviceConfigParsingUtils.parseBrightness(value);
95             return new ThrottlingLevel(status, brightnessPoint);
96         } catch (IllegalArgumentException iae) {
97             return null;
98         }
99     };
100 
101     private final Function<List<ThrottlingLevel>, ThermalBrightnessThrottlingData>
102             mDataSetMapper = ThermalBrightnessThrottlingData::create;
103 
104 
BrightnessThermalModifier(Handler handler, ClamperChangeListener listener, BrightnessClamperController.DisplayDeviceData data)105     BrightnessThermalModifier(Handler handler, ClamperChangeListener listener,
106             BrightnessClamperController.DisplayDeviceData data) {
107         this(new Injector(), handler, listener, data);
108     }
109 
110     @VisibleForTesting
BrightnessThermalModifier(Injector injector, @NonNull Handler handler, @NonNull ClamperChangeListener listener, @NonNull BrightnessClamperController.DisplayDeviceData data)111     BrightnessThermalModifier(Injector injector, @NonNull Handler handler,
112             @NonNull ClamperChangeListener listener,
113             @NonNull BrightnessClamperController.DisplayDeviceData data) {
114         mHandler = handler;
115         mChangeListener = listener;
116         mConfigParameterProvider = injector.getDeviceConfigParameterProvider();
117         mThermalStatusObserver = new ThermalStatusObserver(injector, handler);
118         mHandler.post(() -> {
119             setDisplayData(data);
120             loadOverrideData();
121         });
122     }
123     //region BrightnessStateModifier
124     @Override
apply(DisplayManagerInternal.DisplayPowerRequest request, DisplayBrightnessState.Builder stateBuilder)125     public void apply(DisplayManagerInternal.DisplayPowerRequest request,
126             DisplayBrightnessState.Builder stateBuilder) {
127         if (stateBuilder.getMaxBrightness() > mBrightnessCap) {
128             stateBuilder.setMaxBrightness(mBrightnessCap);
129             stateBuilder.setBrightness(Math.min(stateBuilder.getBrightness(), mBrightnessCap));
130             stateBuilder.setBrightnessMaxReason(BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL);
131             stateBuilder.getBrightnessReason().addModifier(BrightnessReason.MODIFIER_THROTTLED);
132             // set fast change only when modifier is activated.
133             // this will allow auto brightness to apply slow change even when modifier is active
134             if (!mApplied) {
135                 stateBuilder.setIsSlowChange(false);
136             }
137             mApplied = true;
138         } else {
139             mApplied = false;
140         }
141     }
142 
143     @Override
stop()144     public void stop() {
145         mThermalStatusObserver.stopObserving();
146     }
147 
148     @Override
dump(PrintWriter writer)149     public void dump(PrintWriter writer) {
150         writer.println("BrightnessThermalClamper:");
151         writer.println("  mThrottlingStatus: " + mThrottlingStatus);
152         writer.println("  mUniqueDisplayId: " + mUniqueDisplayId);
153         writer.println("  mDataId: " + mDataId);
154         writer.println("  mDataOverride: " + mThermalThrottlingDataOverride);
155         writer.println("  mDataFromDeviceConfig: " + mThermalThrottlingDataFromDeviceConfig);
156         writer.println("  mDataActive: " + mThermalThrottlingDataActive);
157         writer.println("  mBrightnessCap:" + mBrightnessCap);
158         writer.println("  mApplied:" + mApplied);
159         mThermalStatusObserver.dump(writer);
160     }
161 
162     @Override
shouldListenToLightSensor()163     public boolean shouldListenToLightSensor() {
164         return false;
165     }
166 
167     @Override
setAmbientLux(float lux)168     public void setAmbientLux(float lux) {
169         // noop
170     }
171     //endregion
172 
173     //region DisplayDeviceDataListener
174     @Override
onDisplayChanged(BrightnessClamperController.DisplayDeviceData data)175     public void onDisplayChanged(BrightnessClamperController.DisplayDeviceData data) {
176         mHandler.post(() -> {
177             setDisplayData(data);
178             recalculateActiveData();
179         });
180     }
181     //endregion
182 
183     //region StatefulModifier
184     @Override
applyStateChange( BrightnessClamperController.ModifiersAggregatedState aggregatedState)185     public void applyStateChange(
186             BrightnessClamperController.ModifiersAggregatedState aggregatedState) {
187         if (aggregatedState.mMaxBrightness > mBrightnessCap) {
188             aggregatedState.mMaxBrightness = mBrightnessCap;
189             aggregatedState.mMaxBrightnessReason = BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL;
190         }
191     }
192     //endregion
193 
194     //region DeviceConfigListener
195     @Override
onDeviceConfigChanged()196     public void onDeviceConfigChanged() {
197         mHandler.post(() -> {
198             loadOverrideData();
199             recalculateActiveData();
200         });
201     }
202     //endregion
203 
recalculateActiveData()204     private void recalculateActiveData() {
205         if (mUniqueDisplayId == null || mDataId == null) {
206             return;
207         }
208         mThermalThrottlingDataActive = mThermalThrottlingDataOverride
209                 .getOrDefault(mUniqueDisplayId, Map.of()).getOrDefault(mDataId,
210                         mThermalThrottlingDataFromDeviceConfig);
211 
212         recalculateBrightnessCap();
213     }
214 
loadOverrideData()215     private void loadOverrideData() {
216         String throttlingDataOverride = mConfigParameterProvider.getBrightnessThrottlingData();
217         mThermalThrottlingDataOverride = DeviceConfigParsingUtils.parseDeviceConfigMap(
218                 throttlingDataOverride, mDataPointMapper, mDataSetMapper);
219     }
220 
setDisplayData(@onNull ThermalData data)221     private void setDisplayData(@NonNull ThermalData data) {
222         mUniqueDisplayId = data.getUniqueDisplayId();
223         mDataId = data.getThermalThrottlingDataId();
224         mThermalThrottlingDataFromDeviceConfig = data.getThermalBrightnessThrottlingData();
225         if (mThermalThrottlingDataFromDeviceConfig == null && !DEFAULT_ID.equals(mDataId)) {
226             Slog.wtf(TAG,
227                     "Thermal throttling data is missing for thermalThrottlingDataId=" + mDataId);
228         }
229         mThermalStatusObserver.registerSensor(data.getTempSensor());
230     }
231 
recalculateBrightnessCap()232     private void recalculateBrightnessCap() {
233         float brightnessCap = PowerManager.BRIGHTNESS_MAX;
234         if (mThermalThrottlingDataActive != null) {
235             // Throttling levels are sorted by increasing severity
236             for (ThrottlingLevel level : mThermalThrottlingDataActive.throttlingLevels) {
237                 if (level.thermalStatus <= mThrottlingStatus) {
238                     brightnessCap = level.brightness;
239                 } else {
240                     // Throttling levels that are greater than the current status are irrelevant
241                     break;
242                 }
243             }
244         }
245 
246         if (brightnessCap  != mBrightnessCap) {
247             mBrightnessCap = brightnessCap;
248             mChangeListener.onChanged();
249         }
250     }
251 
thermalStatusChanged(@emperature.ThrottlingStatus int status)252     private void thermalStatusChanged(@Temperature.ThrottlingStatus int status) {
253         if (mThrottlingStatus != status) {
254             mThrottlingStatus = status;
255             recalculateBrightnessCap();
256         }
257     }
258 
259     private final class ThermalStatusObserver extends IThermalEventListener.Stub {
260         private final Injector mInjector;
261         private final Handler mHandler;
262         private IThermalService mThermalService;
263         private boolean mStarted;
264         private SensorData mObserverTempSensor;
265 
ThermalStatusObserver(Injector injector, Handler handler)266         ThermalStatusObserver(Injector injector, Handler handler) {
267             mInjector = injector;
268             mHandler = handler;
269             mStarted = false;
270         }
271 
registerSensor(SensorData tempSensor)272         void registerSensor(SensorData tempSensor) {
273             if (!mStarted || mObserverTempSensor == null) {
274                 mObserverTempSensor = tempSensor;
275                 registerThermalListener();
276                 return;
277             }
278 
279             String curType = mObserverTempSensor.type;
280             mObserverTempSensor = tempSensor;
281             if (Objects.equals(curType, tempSensor.type)) {
282                 Slog.d(TAG, "Thermal status observer already started");
283                 return;
284             }
285             stopObserving();
286             registerThermalListener();
287         }
288 
registerThermalListener()289         void registerThermalListener() {
290             mThermalService = mInjector.getThermalService();
291             if (mThermalService == null) {
292                 Slog.e(TAG, "Could not observe thermal status. Service not available");
293                 return;
294             }
295             int temperatureType = SensorUtils.getSensorTemperatureType(mObserverTempSensor);
296             try {
297                 // We get a callback immediately upon registering so there's no need to query
298                 // for the current value.
299                 mThermalService.registerThermalEventListenerWithType(this, temperatureType);
300                 mStarted = true;
301             } catch (RemoteException e) {
302                 Slog.e(TAG, "Failed to register thermal status listener", e);
303             }
304         }
305 
306         @Override
notifyThrottling(Temperature temp)307         public void notifyThrottling(Temperature temp) {
308             Slog.d(TAG, "New thermal throttling status = " + temp.getStatus());
309             if (mObserverTempSensor.name != null
310                     && !mObserverTempSensor.name.equals(temp.getName())) {
311                 Slog.i(TAG, "Skipping thermal throttling notification as monitored sensor: "
312                             + mObserverTempSensor.name
313                             + " != notified sensor: "
314                             + temp.getName());
315                 return;
316             }
317             @Temperature.ThrottlingStatus int status = temp.getStatus();
318             mHandler.post(() -> thermalStatusChanged(status));
319         }
320 
stopObserving()321         void stopObserving() {
322             if (!mStarted) {
323                 return;
324             }
325             try {
326                 mThermalService.unregisterThermalEventListener(this);
327                 mStarted = false;
328             } catch (RemoteException e) {
329                 Slog.e(TAG, "Failed to unregister thermal status listener", e);
330             }
331             mThermalService = null;
332         }
333 
dump(PrintWriter writer)334         void dump(PrintWriter writer) {
335             writer.println("  ThermalStatusObserver:");
336             writer.println("    mStarted: " + mStarted);
337             writer.println("    mObserverTempSensor: " + mObserverTempSensor);
338             if (mThermalService != null) {
339                 writer.println("    ThermalService available");
340             } else {
341                 writer.println("    ThermalService not available");
342             }
343         }
344     }
345 
346     interface ThermalData {
347         @NonNull
getUniqueDisplayId()348         String getUniqueDisplayId();
349 
350         @Nullable
getThermalThrottlingDataId()351         String getThermalThrottlingDataId();
352 
353         @Nullable
getThermalBrightnessThrottlingData()354         ThermalBrightnessThrottlingData getThermalBrightnessThrottlingData();
355 
356         @NonNull
getTempSensor()357         SensorData getTempSensor();
358     }
359 
360     @VisibleForTesting
361     static class Injector {
getThermalService()362         IThermalService getThermalService() {
363             return IThermalService.Stub.asInterface(
364                     ServiceManager.getService(Context.THERMAL_SERVICE));
365         }
366 
getDeviceConfigParameterProvider()367         DeviceConfigParameterProvider getDeviceConfigParameterProvider() {
368             return new DeviceConfigParameterProvider(DeviceConfigInterface.REAL);
369         }
370     }
371 }
372