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.os.Handler;
25 import android.os.PowerManager;
26 import android.os.Temperature;
27 import android.provider.DeviceConfigInterface;
28 import android.util.Slog;
29 
30 import com.android.internal.annotations.VisibleForTesting;
31 import com.android.server.display.DisplayDeviceConfig.PowerThrottlingConfigData;
32 import com.android.server.display.DisplayDeviceConfig.PowerThrottlingData;
33 import com.android.server.display.DisplayDeviceConfig.PowerThrottlingData.ThrottlingLevel;
34 import com.android.server.display.feature.DeviceConfigParameterProvider;
35 import com.android.server.display.utils.DeviceConfigParsingUtils;
36 
37 import java.io.PrintWriter;
38 import java.util.List;
39 import java.util.Map;
40 import java.util.function.BiFunction;
41 import java.util.function.Function;
42 
43 
44 class BrightnessPowerClamper extends
45         BrightnessClamper<BrightnessPowerClamper.PowerData> {
46 
47     private static final String TAG = "BrightnessPowerClamper";
48     @NonNull
49     private final Injector mInjector;
50     @NonNull
51     private final DeviceConfigParameterProvider mConfigParameterProvider;
52     @Nullable
53     private PmicMonitor mPmicMonitor;
54     // data from DeviceConfig, for all displays, for all dataSets
55     // mapOf(uniqueDisplayId to mapOf(dataSetId to PowerThrottlingData))
56     @NonNull
57     private Map<String, Map<String, PowerThrottlingData>>
58             mPowerThrottlingDataOverride = Map.of();
59     // data from DisplayDeviceConfig, for particular display+dataSet
60     @Nullable
61     private PowerThrottlingData mPowerThrottlingDataFromDDC = null;
62     // Active data, if mPowerThrottlingDataOverride contains data for mUniqueDisplayId,
63     // mDataId, then use it, otherwise mPowerThrottlingDataFromDDC.
64     @Nullable
65     private PowerThrottlingData mPowerThrottlingDataActive = null;
66     @Nullable
67     private PowerThrottlingConfigData mPowerThrottlingConfigData = null;
68 
69     private @Temperature.ThrottlingStatus int mCurrentThermalLevel = Temperature.THROTTLING_NONE;
70     private float mCurrentAvgPowerConsumed = 0;
71     @Nullable
72     private String mUniqueDisplayId = null;
73     @Nullable
74     private String mDataId = null;
75 
76     private final BiFunction<String, String, ThrottlingLevel> mDataPointMapper = (key, value) -> {
77         try {
78             int status = DeviceConfigParsingUtils.parseThermalStatus(key);
79             float powerQuota = Float.parseFloat(value);
80             return new ThrottlingLevel(status, powerQuota);
81         } catch (IllegalArgumentException iae) {
82             return null;
83         }
84     };
85 
86     private final Function<List<ThrottlingLevel>, PowerThrottlingData>
87             mDataSetMapper = PowerThrottlingData::create;
88 
89 
BrightnessPowerClamper(Handler handler, ClamperChangeListener listener, PowerData powerData)90     BrightnessPowerClamper(Handler handler, ClamperChangeListener listener,
91             PowerData powerData) {
92         this(new Injector(), handler, listener, powerData);
93     }
94 
95     @VisibleForTesting
BrightnessPowerClamper(Injector injector, Handler handler, ClamperChangeListener listener, PowerData powerData)96     BrightnessPowerClamper(Injector injector, Handler handler, ClamperChangeListener listener,
97             PowerData powerData) {
98         super(handler, listener);
99         mInjector = injector;
100         mConfigParameterProvider = injector.getDeviceConfigParameterProvider();
101 
102         mHandler.post(() -> {
103             setDisplayData(powerData);
104             loadOverrideData();
105             start();
106         });
107 
108     }
109 
110     @Override
111     @NonNull
getType()112     BrightnessClamper.Type getType() {
113         return Type.POWER;
114     }
115 
116     @Override
onDeviceConfigChanged()117     void onDeviceConfigChanged() {
118         mHandler.post(() -> {
119             loadOverrideData();
120             recalculateActiveData();
121         });
122     }
123 
124     @Override
onDisplayChanged(PowerData data)125     void onDisplayChanged(PowerData data) {
126         mHandler.post(() -> {
127             setDisplayData(data);
128             recalculateActiveData();
129         });
130     }
131 
132     @Override
stop()133     void stop() {
134         if (mPmicMonitor != null) {
135             mPmicMonitor.shutdown();
136         }
137     }
138 
139     /**
140      * Dumps the state of BrightnessPowerClamper.
141      */
dump(PrintWriter pw)142     public void dump(PrintWriter pw) {
143         pw.println("BrightnessPowerClamper:");
144         pw.println("  mCurrentAvgPowerConsumed=" + mCurrentAvgPowerConsumed);
145         pw.println("  mUniqueDisplayId=" + mUniqueDisplayId);
146         pw.println("  mCurrentThermalLevel=" + mCurrentThermalLevel);
147         pw.println("  mPowerThrottlingDataFromDDC=" + (mPowerThrottlingDataFromDDC == null ? "null"
148                 : mPowerThrottlingDataFromDDC.toString()));
149         super.dump(pw);
150     }
151 
recalculateActiveData()152     private void recalculateActiveData() {
153         if (mUniqueDisplayId == null || mDataId == null) {
154             return;
155         }
156         mPowerThrottlingDataActive = mPowerThrottlingDataOverride
157                 .getOrDefault(mUniqueDisplayId, Map.of()).getOrDefault(mDataId,
158                         mPowerThrottlingDataFromDDC);
159         if (mPowerThrottlingDataActive != null) {
160             if (mPmicMonitor != null) {
161                 mPmicMonitor.stop();
162                 mPmicMonitor.start();
163             }
164         } else {
165             if (mPmicMonitor != null) {
166                 mPmicMonitor.stop();
167             }
168         }
169         recalculateBrightnessCap();
170     }
171 
loadOverrideData()172     private void loadOverrideData() {
173         String throttlingDataOverride = mConfigParameterProvider.getPowerThrottlingData();
174         mPowerThrottlingDataOverride = DeviceConfigParsingUtils.parseDeviceConfigMap(
175                 throttlingDataOverride, mDataPointMapper, mDataSetMapper);
176     }
177 
setDisplayData(@onNull PowerData data)178     private void setDisplayData(@NonNull PowerData data) {
179         mUniqueDisplayId = data.getUniqueDisplayId();
180         mDataId = data.getPowerThrottlingDataId();
181         mPowerThrottlingDataFromDDC = data.getPowerThrottlingData();
182         if (mPowerThrottlingDataFromDDC == null && !DEFAULT_ID.equals(mDataId)) {
183             Slog.wtf(TAG,
184                     "Power throttling data is missing for powerThrottlingDataId=" + mDataId);
185         }
186 
187         mPowerThrottlingConfigData = data.getPowerThrottlingConfigData();
188         if (mPowerThrottlingConfigData == null) {
189             Slog.d(TAG,
190                     "Power throttling data is missing for configuration data.");
191         }
192     }
193 
recalculateBrightnessCap()194     private void recalculateBrightnessCap() {
195         boolean isActive = false;
196         float targetBrightnessCap = PowerManager.BRIGHTNESS_MAX;
197         float powerQuota = getPowerQuotaForThermalStatus(mCurrentThermalLevel);
198         if (mPowerThrottlingDataActive == null) {
199             return;
200         }
201         if (powerQuota > 0 && mCurrentAvgPowerConsumed > powerQuota) {
202             isActive = true;
203             // calculate new brightness Cap.
204             // Brightness has a linear relation to power-consumed.
205             targetBrightnessCap =
206                     (powerQuota / mCurrentAvgPowerConsumed) * PowerManager.BRIGHTNESS_MAX;
207             // Cap to lowest allowed brightness on device.
208             targetBrightnessCap = Math.max(targetBrightnessCap,
209                     mPowerThrottlingConfigData.brightnessLowestCapAllowed);
210         }
211 
212         if (mBrightnessCap != targetBrightnessCap || mIsActive != isActive) {
213             mIsActive = isActive;
214             mBrightnessCap = targetBrightnessCap;
215             mChangeListener.onChanged();
216         }
217     }
218 
getPowerQuotaForThermalStatus(@emperature.ThrottlingStatus int thermalStatus)219     private float getPowerQuotaForThermalStatus(@Temperature.ThrottlingStatus int thermalStatus) {
220         float powerQuota = 0f;
221         if (mPowerThrottlingDataActive != null) {
222             // Throttling levels are sorted by increasing severity
223             for (ThrottlingLevel level : mPowerThrottlingDataActive.throttlingLevels) {
224                 if (level.thermalStatus <= thermalStatus) {
225                     powerQuota = level.powerQuotaMilliWatts;
226                 } else {
227                     // Throttling levels that are greater than the current status are irrelevant
228                     break;
229                 }
230             }
231         }
232         return powerQuota;
233     }
234 
recalculatePowerQuotaChange(float avgPowerConsumed, int thermalStatus)235     private void recalculatePowerQuotaChange(float avgPowerConsumed, int thermalStatus) {
236         mHandler.post(() -> {
237             mCurrentThermalLevel = thermalStatus;
238             mCurrentAvgPowerConsumed = avgPowerConsumed;
239             recalculateBrightnessCap();
240         });
241     }
242 
start()243     private void start() {
244         if (mPowerThrottlingConfigData == null) {
245             return;
246         }
247         PowerChangeListener listener = (powerConsumed, thermalStatus) -> {
248             recalculatePowerQuotaChange(powerConsumed, thermalStatus);
249         };
250         mPmicMonitor =
251             mInjector.getPmicMonitor(listener, mPowerThrottlingConfigData.pollingWindowMillis);
252         mPmicMonitor.start();
253     }
254 
255     public interface PowerData {
256         @NonNull
getUniqueDisplayId()257         String getUniqueDisplayId();
258 
259         @NonNull
getPowerThrottlingDataId()260         String getPowerThrottlingDataId();
261 
262         @Nullable
getPowerThrottlingData()263         PowerThrottlingData getPowerThrottlingData();
264 
265         @Nullable
getPowerThrottlingConfigData()266         PowerThrottlingConfigData getPowerThrottlingConfigData();
267     }
268 
269     /**
270      * Power change listener
271      */
272     @FunctionalInterface
273     public interface PowerChangeListener {
274         /**
275          * Notifies that power state changed from power controller.
276          */
onChanged(float avgPowerConsumed, @Temperature.ThrottlingStatus int thermalStatus)277         void onChanged(float avgPowerConsumed, @Temperature.ThrottlingStatus int thermalStatus);
278     }
279 
280     @VisibleForTesting
281     static class Injector {
getPmicMonitor(PowerChangeListener listener, int pollingTime)282         PmicMonitor getPmicMonitor(PowerChangeListener listener, int pollingTime) {
283             return new PmicMonitor(listener, pollingTime);
284         }
285 
getDeviceConfigParameterProvider()286         DeviceConfigParameterProvider getDeviceConfigParameterProvider() {
287             return new DeviceConfigParameterProvider(DeviceConfigInterface.REAL);
288         }
289     }
290 }
291