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