• 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.PowerThrottlingConfigData;
40 import com.android.server.display.DisplayDeviceConfig.PowerThrottlingData;
41 import com.android.server.display.DisplayDeviceConfig.PowerThrottlingData.ThrottlingLevel;
42 import com.android.server.display.brightness.BrightnessReason;
43 import com.android.server.display.brightness.BrightnessUtils;
44 import com.android.server.display.feature.DeviceConfigParameterProvider;
45 import com.android.server.display.utils.DeviceConfigParsingUtils;
46 
47 import java.io.PrintWriter;
48 import java.util.List;
49 import java.util.Map;
50 import java.util.function.BiFunction;
51 import java.util.function.Function;
52 
53 
54 class BrightnessPowerModifier implements BrightnessStateModifier,
55         BrightnessClamperController.DisplayDeviceDataListener,
56         BrightnessClamperController.StatefulModifier,
57         BrightnessClamperController.DeviceConfigListener {
58 
59     private static final String TAG = "BrightnessPowerClamper";
60     @NonNull
61     private final DeviceConfigParameterProvider mConfigParameterProvider;
62     @NonNull
63     private final PmicMonitor mPmicMonitor;
64     // data from DeviceConfig, for all displays, for all dataSets
65     // mapOf(uniqueDisplayId to mapOf(dataSetId to PowerThrottlingData))
66     @NonNull
67     private Map<String, Map<String, PowerThrottlingData>>
68             mPowerThrottlingDataOverride = Map.of();
69     // data from DisplayDeviceConfig, for particular display+dataSet
70     @Nullable
71     private PowerThrottlingData mPowerThrottlingDataFromDDC = null;
72     // Active data, if mPowerThrottlingDataOverride contains data for mUniqueDisplayId,
73     // mDataId, then use it, otherwise mPowerThrottlingDataFromDDC.
74     @Nullable
75     private PowerThrottlingData mPowerThrottlingDataActive = null;
76     @Nullable
77     private PowerThrottlingConfigData mPowerThrottlingConfigData;
78     @NonNull
79     private final ThermalLevelListener mThermalLevelListener;
80     private @Temperature.ThrottlingStatus int mCurrentThermalLevel = Temperature.THROTTLING_NONE;
81     private boolean mCurrentThermalLevelChanged = false;
82     private float mCurrentAvgPowerConsumed = 0;
83     @Nullable
84     private String mUniqueDisplayId = null;
85     @Nullable
86     private String mDataId = null;
87     private float mCurrentBrightness = PowerManager.BRIGHTNESS_INVALID;
88     private float mCustomAnimationRateSec = DisplayBrightnessState.CUSTOM_ANIMATION_RATE_NOT_SET;
89     private float mCustomAnimationRateDeviceConfig =
90                         DisplayBrightnessState.CUSTOM_ANIMATION_RATE_NOT_SET;
91     private final BiFunction<String, String, ThrottlingLevel> mDataPointMapper = (key, value) -> {
92         try {
93             int status = DeviceConfigParsingUtils.parseThermalStatus(key);
94             float powerQuota = Float.parseFloat(value);
95             return new ThrottlingLevel(status, powerQuota);
96         } catch (IllegalArgumentException iae) {
97             return null;
98         }
99     };
100 
101     private final Function<List<ThrottlingLevel>, PowerThrottlingData>
102             mDataSetMapper = PowerThrottlingData::create;
103 
104     protected final Handler mHandler;
105     protected final BrightnessClamperController.ClamperChangeListener mChangeListener;
106 
107     private float mBrightnessCap = PowerManager.BRIGHTNESS_MAX;;
108     private boolean mApplied = false;
109 
110 
BrightnessPowerModifier(Handler handler, ClamperChangeListener listener, PowerData powerData, float currentBrightness)111     BrightnessPowerModifier(Handler handler, ClamperChangeListener listener,
112             PowerData powerData, float currentBrightness) {
113         this(new Injector(), handler, listener, powerData, currentBrightness);
114     }
115 
116     @VisibleForTesting
BrightnessPowerModifier(@onNull Injector injector, Handler handler, ClamperChangeListener listener, PowerData powerData, float currentBrightness)117     BrightnessPowerModifier(@NonNull Injector injector, Handler handler,
118             ClamperChangeListener listener, PowerData powerData, float currentBrightness) {
119         mHandler = handler;
120         mChangeListener = listener;
121         mCurrentBrightness = currentBrightness;
122 
123         mPowerThrottlingConfigData = powerData.getPowerThrottlingConfigData();
124         if (mPowerThrottlingConfigData != null) {
125             mCustomAnimationRateDeviceConfig = mPowerThrottlingConfigData.customAnimationRate;
126         }
127         mThermalLevelListener = new ThermalLevelListener(handler);
128         mPmicMonitor =
129             injector.getPmicMonitor(this::recalculatePowerQuotaChange,
130                     mThermalLevelListener.getThermalService(),
131                     mPowerThrottlingConfigData.pollingWindowMaxMillis,
132                     mPowerThrottlingConfigData.pollingWindowMinMillis);
133 
134         mConfigParameterProvider = injector.getDeviceConfigParameterProvider();
135         mHandler.post(() -> {
136             setDisplayData(powerData);
137             loadOverrideData();
138             start();
139         });
140     }
141 
142     //region BrightnessStateModifier
143     @Override
apply(DisplayManagerInternal.DisplayPowerRequest request, DisplayBrightnessState.Builder stateBuilder)144     public void apply(DisplayManagerInternal.DisplayPowerRequest request,
145             DisplayBrightnessState.Builder stateBuilder) {
146         if (stateBuilder.getMaxBrightness() > mBrightnessCap) {
147             stateBuilder.setMaxBrightness(mBrightnessCap);
148             stateBuilder.setBrightness(Math.min(stateBuilder.getBrightness(), mBrightnessCap));
149             stateBuilder.setBrightnessMaxReason(BrightnessInfo.BRIGHTNESS_MAX_REASON_POWER_IC);
150             stateBuilder.getBrightnessReason().addModifier(BrightnessReason.MODIFIER_THROTTLED);
151             // set custom animation rate only when modifier is activated.
152             // this will allow auto brightness to apply slow change even when modifier is active
153             if (!mApplied) {
154                 stateBuilder.setCustomAnimationRate(mCustomAnimationRateSec);
155                 mCustomAnimationRateSec = DisplayBrightnessState.CUSTOM_ANIMATION_RATE_NOT_SET;
156             }
157             mApplied = true;
158         } else {
159             mApplied = false;
160         }
161         mCurrentBrightness = stateBuilder.getBrightness();
162     }
163 
164     @Override
stop()165     public void stop() {
166         mPmicMonitor.shutdown();
167         mThermalLevelListener.stop();
168     }
169 
170     /**
171      * Dumps the state of BrightnessPowerClamper.
172      */
dump(PrintWriter pw)173     public void dump(PrintWriter pw) {
174         pw.println("BrightnessPowerClamper:");
175         pw.println("  mCurrentAvgPowerConsumed=" + mCurrentAvgPowerConsumed);
176         pw.println("  mUniqueDisplayId=" + mUniqueDisplayId);
177         pw.println("  mCurrentThermalLevel=" + mCurrentThermalLevel);
178         pw.println("  mCurrentThermalLevelChanged=" + mCurrentThermalLevelChanged);
179         pw.println("  mPowerThrottlingDataFromDDC=" + (mPowerThrottlingDataFromDDC == null ? "null"
180                 : mPowerThrottlingDataFromDDC.toString()));
181         pw.println("  mBrightnessCap: " + mBrightnessCap);
182         pw.println("  mApplied: " + mApplied);
183         mThermalLevelListener.dump(pw);
184     }
185 
186     @Override
shouldListenToLightSensor()187     public boolean shouldListenToLightSensor() {
188         return false;
189     }
190 
191     @Override
setAmbientLux(float lux)192     public void setAmbientLux(float lux) {
193         // noop
194     }
195     //endregion
196 
197     //region DisplayDeviceDataListener
198     @Override
onDisplayChanged(BrightnessClamperController.DisplayDeviceData data)199     public void onDisplayChanged(BrightnessClamperController.DisplayDeviceData data) {
200         mHandler.post(() -> {
201             setDisplayData(data);
202             recalculateActiveData();
203         });
204     }
205     //endregion
206 
207     //region StatefulModifier
208     @Override
applyStateChange( BrightnessClamperController.ModifiersAggregatedState aggregatedState)209     public void applyStateChange(
210             BrightnessClamperController.ModifiersAggregatedState aggregatedState) {
211         if (aggregatedState.mMaxBrightness > mBrightnessCap) {
212             aggregatedState.mMaxBrightness = mBrightnessCap;
213             aggregatedState.mMaxBrightnessReason = BrightnessInfo.BRIGHTNESS_MAX_REASON_POWER_IC;
214         }
215     }
216     //endregion
217 
218     //region DeviceConfigListener
219     @Override
onDeviceConfigChanged()220     public void onDeviceConfigChanged() {
221         mHandler.post(() -> {
222             loadOverrideData();
223             recalculateActiveData();
224         });
225     }
226     //endregion
227 
recalculateActiveData()228     private void recalculateActiveData() {
229         if (mUniqueDisplayId == null || mDataId == null) {
230             return;
231         }
232         mPowerThrottlingDataActive = mPowerThrottlingDataOverride
233                 .getOrDefault(mUniqueDisplayId, Map.of()).getOrDefault(mDataId,
234                         mPowerThrottlingDataFromDDC);
235         if (mPowerThrottlingDataActive == null) {
236             mPmicMonitor.stop();
237         }
238     }
239 
loadOverrideData()240     private void loadOverrideData() {
241         String throttlingDataOverride = mConfigParameterProvider.getPowerThrottlingData();
242         mPowerThrottlingDataOverride = DeviceConfigParsingUtils.parseDeviceConfigMap(
243                 throttlingDataOverride, mDataPointMapper, mDataSetMapper);
244     }
245 
setDisplayData(@onNull PowerData data)246     private void setDisplayData(@NonNull PowerData data) {
247         mUniqueDisplayId = data.getUniqueDisplayId();
248         mDataId = data.getPowerThrottlingDataId();
249         mPowerThrottlingDataFromDDC = data.getPowerThrottlingData();
250         if (mPowerThrottlingDataFromDDC == null && !DEFAULT_ID.equals(mDataId)) {
251             Slog.wtf(TAG,
252                     "Power throttling data is missing for powerThrottlingDataId=" + mDataId);
253         }
254 
255         mPowerThrottlingConfigData = data.getPowerThrottlingConfigData();
256     }
257 
recalculateBrightnessCap()258     private void recalculateBrightnessCap() {
259         float targetBrightnessCap = PowerManager.BRIGHTNESS_MAX;
260         float powerQuota = getPowerQuotaForThermalStatus(mCurrentThermalLevel);
261         if (mPowerThrottlingDataActive == null) {
262             return;
263         }
264         if (powerQuota > 0) {
265             if (BrightnessUtils.isValidBrightnessValue(mCurrentBrightness)
266                     && (mCurrentAvgPowerConsumed > powerQuota)) {
267                 // calculate new brightness Cap.
268                 // Brightness has a linear relation to power-consumed.
269                 targetBrightnessCap =
270                     (powerQuota / mCurrentAvgPowerConsumed) * mCurrentBrightness;
271             } else if (mCurrentThermalLevelChanged) {
272                 if (mCurrentThermalLevel == Temperature.THROTTLING_NONE) {
273                     // reset pmic and remove the power-throttling cap.
274                     targetBrightnessCap = PowerManager.BRIGHTNESS_MAX;
275                     mPmicMonitor.stop();
276                 } else {
277                     // Since the thermal status has changed, we need to remove power-throttling cap.
278                     // Instead of recalculating and changing brightness again, adding flicker,
279                     // we will wait for the next pmic cycle to re-evaluate this value
280                     // make act on it, if needed.
281                     targetBrightnessCap = PowerManager.BRIGHTNESS_MAX;
282                     if (mPmicMonitor.isStopped()) {
283                         mPmicMonitor.start();
284                     }
285                 }
286             } else { // Current power consumed is under the quota.
287                 targetBrightnessCap = PowerManager.BRIGHTNESS_MAX;
288             }
289         }
290 
291         // Cap to lowest allowed brightness on device.
292         if (mPowerThrottlingConfigData != null) {
293             targetBrightnessCap = Math.max(targetBrightnessCap,
294                                 mPowerThrottlingConfigData.brightnessLowestCapAllowed);
295         }
296 
297         if (mBrightnessCap != targetBrightnessCap) {
298             Slog.i(TAG, "Power clamper changing current brightness cap mBrightnessCap: "
299                     + mBrightnessCap + " to target brightness cap:" + targetBrightnessCap
300                     + " for current screen brightness: " + mCurrentBrightness + "\n"
301                     + "Power clamper changed state: thermalStatus:" + mCurrentThermalLevel
302                     + " mCurrentThermalLevelChanged:" + mCurrentThermalLevelChanged
303                     + " mCurrentAvgPowerConsumed:" + mCurrentAvgPowerConsumed
304                     + " mCustomAnimationRateSec:" + mCustomAnimationRateDeviceConfig);
305             mBrightnessCap = targetBrightnessCap;
306             mCustomAnimationRateSec = mCustomAnimationRateDeviceConfig;
307             mChangeListener.onChanged();
308         } else {
309             mCustomAnimationRateSec = DisplayBrightnessState.CUSTOM_ANIMATION_RATE_NOT_SET;
310         }
311     }
312 
getPowerQuotaForThermalStatus(@emperature.ThrottlingStatus int thermalStatus)313     private float getPowerQuotaForThermalStatus(@Temperature.ThrottlingStatus int thermalStatus) {
314         float powerQuota = 0f;
315         if (mPowerThrottlingDataActive != null) {
316             // Throttling levels are sorted by increasing severity
317             for (ThrottlingLevel level : mPowerThrottlingDataActive.throttlingLevels) {
318                 if (level.thermalStatus <= thermalStatus) {
319                     powerQuota = level.powerQuotaMilliWatts;
320                 } else {
321                     // Throttling levels that are greater than the current status are irrelevant
322                     break;
323                 }
324             }
325         }
326         return powerQuota;
327     }
328 
recalculatePowerQuotaChange(float avgPowerConsumed, int thermalStatus)329     private void recalculatePowerQuotaChange(float avgPowerConsumed, int thermalStatus) {
330         mHandler.post(() -> {
331             mCurrentThermalLevelChanged = mCurrentThermalLevel != thermalStatus;
332             mCurrentThermalLevel = thermalStatus;
333             mCurrentAvgPowerConsumed = avgPowerConsumed;
334             recalculateBrightnessCap();
335         });
336     }
337 
start()338     private void start() {
339         if (mPowerThrottlingConfigData == null) {
340             return;
341         }
342         if (mPowerThrottlingConfigData.pollingWindowMaxMillis
343                 <= mPowerThrottlingConfigData.pollingWindowMinMillis) {
344             Slog.e(TAG, "Brightness power max polling window:"
345                     + mPowerThrottlingConfigData.pollingWindowMaxMillis
346                     + " msec, should be greater than brightness min polling window:"
347                     + mPowerThrottlingConfigData.pollingWindowMinMillis + " msec.");
348             return;
349         }
350         if ((mPowerThrottlingConfigData.pollingWindowMaxMillis
351                 % mPowerThrottlingConfigData.pollingWindowMinMillis) != 0) {
352             Slog.e(TAG, "Brightness power max polling window:"
353                     + mPowerThrottlingConfigData.pollingWindowMaxMillis
354                     + " msec, is not divisible by brightness min polling window:"
355                     + mPowerThrottlingConfigData.pollingWindowMinMillis + " msec.");
356             return;
357         }
358         mCustomAnimationRateDeviceConfig = mPowerThrottlingConfigData.customAnimationRate;
359         mThermalLevelListener.start();
360     }
361 
activatePmicMonitor()362     private void activatePmicMonitor() {
363         if (!mPmicMonitor.isStopped()) {
364             return;
365         }
366         mPmicMonitor.start();
367     }
368 
deactivatePmicMonitor(@emperature.ThrottlingStatus int status)369     private void deactivatePmicMonitor(@Temperature.ThrottlingStatus int status) {
370         if (status != Temperature.THROTTLING_NONE) {
371             return;
372         }
373         if (mPmicMonitor.isStopped()) {
374             return;
375         }
376         mPmicMonitor.stop();
377     }
378 
379     private final class ThermalLevelListener extends IThermalEventListener.Stub {
380         private final Handler mHandler;
381         private IThermalService mThermalService;
382         private boolean mStarted;
383 
ThermalLevelListener(Handler handler)384         ThermalLevelListener(Handler handler) {
385             mHandler = handler;
386             mStarted = false;
387             mThermalService = IThermalService.Stub.asInterface(
388                     ServiceManager.getService(Context.THERMAL_SERVICE));
389         }
390 
getThermalService()391         IThermalService getThermalService() {
392             return mThermalService;
393         }
394 
start()395         void start() {
396             if (mStarted) {
397                 return;
398             }
399             if (mThermalService == null) {
400                 return;
401             }
402             try {
403                 // TODO b/279114539 Try DISPLAY first and then fallback to SKIN.
404                 mThermalService.registerThermalEventListenerWithType(this, Temperature.TYPE_SKIN);
405                 mStarted = true;
406             } catch (RemoteException e) {
407                 Slog.e(TAG, "Failed to register thermal status listener", e);
408             }
409         }
410 
411         @Override
notifyThrottling(Temperature temp)412         public void notifyThrottling(Temperature temp) {
413             @Temperature.ThrottlingStatus int status = temp.getStatus();
414             if (status >= Temperature.THROTTLING_LIGHT) {
415                 Slog.d(TAG, "Activating pmic monitor due to thermal state:" + status);
416                 mHandler.post(BrightnessPowerModifier.this::activatePmicMonitor);
417             } else {
418                 if (!mPmicMonitor.isStopped()) {
419                     mHandler.post(() -> deactivatePmicMonitor(status));
420                 }
421             }
422         }
423 
stop()424         void stop() {
425             if (!mStarted) {
426                 return;
427             }
428             try {
429                 mThermalService.unregisterThermalEventListener(this);
430                 mStarted = false;
431             } catch (RemoteException e) {
432                 Slog.e(TAG, "Failed to unregister thermal status listener", e);
433             }
434             mThermalService = null;
435         }
436 
dump(PrintWriter writer)437         void dump(PrintWriter writer) {
438             writer.println("  ThermalLevelObserver:");
439             writer.println("    mStarted: " + mStarted);
440         }
441     }
442 
443     public interface PowerData {
444         @NonNull
getUniqueDisplayId()445         String getUniqueDisplayId();
446 
447         @NonNull
getPowerThrottlingDataId()448         String getPowerThrottlingDataId();
449 
450         @Nullable
getPowerThrottlingData()451         PowerThrottlingData getPowerThrottlingData();
452 
453         @Nullable
getPowerThrottlingConfigData()454         PowerThrottlingConfigData getPowerThrottlingConfigData();
455     }
456 
457     /**
458      * Power change listener
459      */
460     @FunctionalInterface
461     public interface PowerChangeListener {
462         /**
463          * Notifies that power state changed from power controller.
464          */
onChanged(float avgPowerConsumed, @Temperature.ThrottlingStatus int thermalStatus)465         void onChanged(float avgPowerConsumed, @Temperature.ThrottlingStatus int thermalStatus);
466     }
467 
468     @VisibleForTesting
469     static class Injector {
470         @NonNull
getPmicMonitor(PowerChangeListener powerChangeListener, IThermalService thermalService, int pollingMaxTimeMillis, int pollingMinTimeMillis)471         PmicMonitor getPmicMonitor(PowerChangeListener powerChangeListener,
472                                    IThermalService thermalService,
473                                    int pollingMaxTimeMillis,
474                                    int pollingMinTimeMillis) {
475             return new PmicMonitor(powerChangeListener, thermalService, pollingMaxTimeMillis,
476                                         pollingMinTimeMillis);
477         }
478 
getDeviceConfigParameterProvider()479         DeviceConfigParameterProvider getDeviceConfigParameterProvider() {
480             return new DeviceConfigParameterProvider(DeviceConfigInterface.REAL);
481         }
482     }
483 }
484