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