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.os.Handler;
26 import android.os.IThermalEventListener;
27 import android.os.IThermalService;
28 import android.os.PowerManager;
29 import android.os.RemoteException;
30 import android.os.ServiceManager;
31 import android.os.Temperature;
32 import android.provider.DeviceConfigInterface;
33 import android.util.Slog;
34 
35 import com.android.internal.annotations.VisibleForTesting;
36 import com.android.server.display.DisplayDeviceConfig.ThermalBrightnessThrottlingData;
37 import com.android.server.display.DisplayDeviceConfig.ThermalBrightnessThrottlingData.ThrottlingLevel;
38 import com.android.server.display.config.SensorData;
39 import com.android.server.display.feature.DeviceConfigParameterProvider;
40 import com.android.server.display.utils.DeviceConfigParsingUtils;
41 import com.android.server.display.utils.SensorUtils;
42 
43 import java.io.PrintWriter;
44 import java.util.List;
45 import java.util.Map;
46 import java.util.function.BiFunction;
47 import java.util.function.Function;
48 
49 
50 class BrightnessThermalClamper extends
51         BrightnessClamper<BrightnessThermalClamper.ThermalData> {
52 
53     private static final String TAG = "BrightnessThermalClamper";
54     @NonNull
55     private final ThermalStatusObserver mThermalStatusObserver;
56     @NonNull
57     private final DeviceConfigParameterProvider mConfigParameterProvider;
58     // data from DeviceConfig, for all displays, for all dataSets
59     // mapOf(uniqueDisplayId to mapOf(dataSetId to ThermalBrightnessThrottlingData))
60     @NonNull
61     private Map<String, Map<String, ThermalBrightnessThrottlingData>>
62             mThermalThrottlingDataOverride = Map.of();
63     // data from DisplayDeviceConfig, for particular display+dataSet
64     @Nullable
65     private ThermalBrightnessThrottlingData mThermalThrottlingDataFromDeviceConfig = null;
66     // Active data, if mDataOverride contains data for mUniqueDisplayId, mDataId, then use it,
67     // otherwise mDataFromDeviceConfig
68     @Nullable
69     private ThermalBrightnessThrottlingData mThermalThrottlingDataActive = null;
70     @Nullable
71     private String mUniqueDisplayId = null;
72     @Nullable
73     private String mDataId = null;
74     @Temperature.ThrottlingStatus
75     private int mThrottlingStatus = Temperature.THROTTLING_NONE;
76 
77     private final BiFunction<String, String, ThrottlingLevel> mDataPointMapper = (key, value) -> {
78         try {
79             int status = DeviceConfigParsingUtils.parseThermalStatus(key);
80             float brightnessPoint = DeviceConfigParsingUtils.parseBrightness(value);
81             return new ThrottlingLevel(status, brightnessPoint);
82         } catch (IllegalArgumentException iae) {
83             return null;
84         }
85     };
86 
87     private final Function<List<ThrottlingLevel>, ThermalBrightnessThrottlingData>
88             mDataSetMapper = ThermalBrightnessThrottlingData::create;
89 
90 
BrightnessThermalClamper(Handler handler, ClamperChangeListener listener, ThermalData thermalData)91     BrightnessThermalClamper(Handler handler, ClamperChangeListener listener,
92             ThermalData thermalData) {
93         this(new Injector(), handler, listener, thermalData);
94     }
95 
96     @VisibleForTesting
BrightnessThermalClamper(Injector injector, Handler handler, ClamperChangeListener listener, ThermalData thermalData)97     BrightnessThermalClamper(Injector injector, Handler handler,
98             ClamperChangeListener listener, ThermalData thermalData) {
99         super(handler, listener);
100         mConfigParameterProvider = injector.getDeviceConfigParameterProvider();
101         mThermalStatusObserver = new ThermalStatusObserver(injector, handler);
102         mHandler.post(() -> {
103             setDisplayData(thermalData);
104             loadOverrideData();
105         });
106 
107     }
108 
109     @Override
110     @NonNull
getType()111     Type getType() {
112         return Type.THERMAL;
113     }
114 
115     @Override
onDeviceConfigChanged()116     void onDeviceConfigChanged() {
117         mHandler.post(() -> {
118             loadOverrideData();
119             recalculateActiveData();
120         });
121     }
122 
123     @Override
onDisplayChanged(ThermalData data)124     void onDisplayChanged(ThermalData data) {
125         mHandler.post(() -> {
126             setDisplayData(data);
127             recalculateActiveData();
128         });
129     }
130 
131     @Override
stop()132     void stop() {
133         mThermalStatusObserver.stopObserving();
134     }
135 
136     @Override
dump(PrintWriter writer)137     void dump(PrintWriter writer) {
138         writer.println("BrightnessThermalClamper:");
139         writer.println("  mThrottlingStatus: " + mThrottlingStatus);
140         writer.println("  mUniqueDisplayId: " + mUniqueDisplayId);
141         writer.println("  mDataId: " + mDataId);
142         writer.println("  mDataOverride: " + mThermalThrottlingDataOverride);
143         writer.println("  mDataFromDeviceConfig: " + mThermalThrottlingDataFromDeviceConfig);
144         writer.println("  mDataActive: " + mThermalThrottlingDataActive);
145         mThermalStatusObserver.dump(writer);
146         super.dump(writer);
147     }
148 
recalculateActiveData()149     private void recalculateActiveData() {
150         if (mUniqueDisplayId == null || mDataId == null) {
151             return;
152         }
153         mThermalThrottlingDataActive = mThermalThrottlingDataOverride
154                 .getOrDefault(mUniqueDisplayId, Map.of()).getOrDefault(mDataId,
155                         mThermalThrottlingDataFromDeviceConfig);
156 
157         recalculateBrightnessCap();
158     }
159 
loadOverrideData()160     private void loadOverrideData() {
161         String throttlingDataOverride = mConfigParameterProvider.getBrightnessThrottlingData();
162         mThermalThrottlingDataOverride = DeviceConfigParsingUtils.parseDeviceConfigMap(
163                 throttlingDataOverride, mDataPointMapper, mDataSetMapper);
164     }
165 
setDisplayData(@onNull ThermalData data)166     private void setDisplayData(@NonNull ThermalData data) {
167         mUniqueDisplayId = data.getUniqueDisplayId();
168         mDataId = data.getThermalThrottlingDataId();
169         mThermalThrottlingDataFromDeviceConfig = data.getThermalBrightnessThrottlingData();
170         if (mThermalThrottlingDataFromDeviceConfig == null && !DEFAULT_ID.equals(mDataId)) {
171             Slog.wtf(TAG,
172                     "Thermal throttling data is missing for thermalThrottlingDataId=" + mDataId);
173         }
174         mThermalStatusObserver.registerSensor(data.getTempSensor());
175     }
176 
recalculateBrightnessCap()177     private void recalculateBrightnessCap() {
178         float brightnessCap = PowerManager.BRIGHTNESS_MAX;
179         boolean isActive = false;
180 
181         if (mThermalThrottlingDataActive != null) {
182             // Throttling levels are sorted by increasing severity
183             for (ThrottlingLevel level : mThermalThrottlingDataActive.throttlingLevels) {
184                 if (level.thermalStatus <= mThrottlingStatus) {
185                     brightnessCap = level.brightness;
186                     isActive = true;
187                 } else {
188                     // Throttling levels that are greater than the current status are irrelevant
189                     break;
190                 }
191             }
192         }
193 
194         if (brightnessCap  != mBrightnessCap || mIsActive != isActive) {
195             mBrightnessCap = brightnessCap;
196             mIsActive = isActive;
197             mChangeListener.onChanged();
198         }
199     }
200 
thermalStatusChanged(@emperature.ThrottlingStatus int status)201     private void thermalStatusChanged(@Temperature.ThrottlingStatus int status) {
202         if (mThrottlingStatus != status) {
203             mThrottlingStatus = status;
204             recalculateBrightnessCap();
205         }
206     }
207 
208 
209     private final class ThermalStatusObserver extends IThermalEventListener.Stub {
210         private final Injector mInjector;
211         private final Handler mHandler;
212         private IThermalService mThermalService;
213         private boolean mStarted;
214         private SensorData mObserverTempSensor;
215 
ThermalStatusObserver(Injector injector, Handler handler)216         ThermalStatusObserver(Injector injector, Handler handler) {
217             mInjector = injector;
218             mHandler = handler;
219             mStarted = false;
220         }
221 
registerSensor(SensorData tempSensor)222         void registerSensor(SensorData tempSensor) {
223             if (!mStarted || mObserverTempSensor == null) {
224                 mObserverTempSensor = tempSensor;
225                 registerThermalListener();
226                 return;
227             }
228 
229             String curType = mObserverTempSensor.type;
230             mObserverTempSensor = tempSensor;
231             if (curType.equals(tempSensor.type)) {
232                 Slog.d(TAG, "Thermal status observer already started");
233                 return;
234             }
235             stopObserving();
236             registerThermalListener();
237         }
238 
registerThermalListener()239         void registerThermalListener() {
240             mThermalService = mInjector.getThermalService();
241             if (mThermalService == null) {
242                 Slog.e(TAG, "Could not observe thermal status. Service not available");
243                 return;
244             }
245             int temperatureType = SensorUtils.getSensorTemperatureType(mObserverTempSensor);
246             try {
247                 // We get a callback immediately upon registering so there's no need to query
248                 // for the current value.
249                 mThermalService.registerThermalEventListenerWithType(this, temperatureType);
250                 mStarted = true;
251             } catch (RemoteException e) {
252                 Slog.e(TAG, "Failed to register thermal status listener", e);
253             }
254         }
255 
256         @Override
notifyThrottling(Temperature temp)257         public void notifyThrottling(Temperature temp) {
258             Slog.d(TAG, "New thermal throttling status = " + temp.getStatus());
259             if (mObserverTempSensor.name != null
260                     && !mObserverTempSensor.name.equals(temp.getName())) {
261                 Slog.i(TAG, "Skipping thermal throttling notification as monitored sensor: "
262                             + mObserverTempSensor.name
263                             + " != notified sensor: "
264                             + temp.getName());
265                 return;
266             }
267             @Temperature.ThrottlingStatus int status = temp.getStatus();
268             mHandler.post(() -> thermalStatusChanged(status));
269         }
270 
stopObserving()271         void stopObserving() {
272             if (!mStarted) {
273                 return;
274             }
275             try {
276                 mThermalService.unregisterThermalEventListener(this);
277                 mStarted = false;
278             } catch (RemoteException e) {
279                 Slog.e(TAG, "Failed to unregister thermal status listener", e);
280             }
281             mThermalService = null;
282         }
283 
dump(PrintWriter writer)284         void dump(PrintWriter writer) {
285             writer.println("  ThermalStatusObserver:");
286             writer.println("    mStarted: " + mStarted);
287             writer.println("    mObserverTempSensor: " + mObserverTempSensor);
288             if (mThermalService != null) {
289                 writer.println("    ThermalService available");
290             } else {
291                 writer.println("    ThermalService not available");
292             }
293         }
294     }
295 
296     interface ThermalData {
297         @NonNull
getUniqueDisplayId()298         String getUniqueDisplayId();
299 
300         @NonNull
getThermalThrottlingDataId()301         String getThermalThrottlingDataId();
302 
303         @Nullable
getThermalBrightnessThrottlingData()304         ThermalBrightnessThrottlingData getThermalBrightnessThrottlingData();
305 
306         @NonNull
getTempSensor()307         SensorData getTempSensor();
308     }
309 
310     @VisibleForTesting
311     static class Injector {
getThermalService()312         IThermalService getThermalService() {
313             return IThermalService.Stub.asInterface(
314                     ServiceManager.getService(Context.THERMAL_SERVICE));
315         }
316 
getDeviceConfigParameterProvider()317         DeviceConfigParameterProvider getDeviceConfigParameterProvider() {
318             return new DeviceConfigParameterProvider(DeviceConfigInterface.REAL);
319         }
320     }
321 }
322