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 android.view.Display.STATE_ON;
20 
21 import static com.android.server.display.brightness.clamper.BrightnessClamper.Type;
22 
23 import android.annotation.NonNull;
24 import android.annotation.Nullable;
25 import android.content.Context;
26 import android.content.res.Resources;
27 import android.hardware.Sensor;
28 import android.hardware.SensorEvent;
29 import android.hardware.SensorEventListener;
30 import android.hardware.SensorManager;
31 import android.hardware.display.BrightnessInfo;
32 import android.hardware.display.DisplayManagerInternal;
33 import android.os.Handler;
34 import android.os.HandlerExecutor;
35 import android.os.PowerManager;
36 import android.os.SystemClock;
37 import android.provider.DeviceConfig;
38 import android.provider.DeviceConfigInterface;
39 import android.util.IndentingPrintWriter;
40 import android.util.Slog;
41 
42 import com.android.internal.R;
43 import com.android.internal.annotations.VisibleForTesting;
44 import com.android.server.display.DisplayBrightnessState;
45 import com.android.server.display.DisplayDeviceConfig;
46 import com.android.server.display.DisplayDeviceConfig.PowerThrottlingConfigData;
47 import com.android.server.display.DisplayDeviceConfig.PowerThrottlingData;
48 import com.android.server.display.DisplayDeviceConfig.ThermalBrightnessThrottlingData;
49 import com.android.server.display.brightness.BrightnessReason;
50 import com.android.server.display.config.SensorData;
51 import com.android.server.display.feature.DeviceConfigParameterProvider;
52 import com.android.server.display.feature.DisplayManagerFlags;
53 import com.android.server.display.utils.AmbientFilter;
54 import com.android.server.display.utils.AmbientFilterFactory;
55 import com.android.server.display.utils.DebugUtils;
56 import com.android.server.display.utils.SensorUtils;
57 
58 import java.io.PrintWriter;
59 import java.util.ArrayList;
60 import java.util.List;
61 import java.util.concurrent.Executor;
62 import java.util.concurrent.TimeUnit;
63 
64 /**
65  * Clampers controller, all in DisplayControllerHandler
66  */
67 public class BrightnessClamperController {
68     private static final String TAG = "BrightnessClamperController";
69     // To enable these logs, run:
70     // 'adb shell setprop persist.log.tag.BrightnessClamperController DEBUG && adb reboot'
71     private static final boolean DEBUG = DebugUtils.isDebuggable(TAG);
72     public static final float INVALID_LUX = -1f;
73 
74     private final DeviceConfigParameterProvider mDeviceConfigParameterProvider;
75     private final Handler mHandler;
76     private final SensorManager mSensorManager;
77     private final ClamperChangeListener mClamperChangeListenerExternal;
78     private final Executor mExecutor;
79     private final List<BrightnessClamper<? super DisplayDeviceData>> mClampers;
80 
81     private final List<BrightnessStateModifier> mModifiers;
82     private final DeviceConfig.OnPropertiesChangedListener mOnPropertiesChangedListener;
83     private float mBrightnessCap = PowerManager.BRIGHTNESS_MAX;
84 
85     private float mCustomAnimationRate = DisplayBrightnessState.CUSTOM_ANIMATION_RATE_NOT_SET;
86     @Nullable
87     private Type mClamperType = null;
88     private final SensorEventListener mLightSensorListener;
89     private Sensor mRegisteredLightSensor = null;
90     private Sensor mLightSensor;
91     private String mLightSensorType;
92     private String mLightSensorName;
93     private AmbientFilter mAmbientFilter;
94     private final DisplayDeviceConfig mDisplayDeviceConfig;
95     private final Resources mResources;
96     private final int mLightSensorRate;
97 
98     private final Injector mInjector;
99     private boolean mClamperApplied = false;
100 
BrightnessClamperController(Handler handler, ClamperChangeListener clamperChangeListener, DisplayDeviceData data, Context context, DisplayManagerFlags flags, SensorManager sensorManager)101     public BrightnessClamperController(Handler handler,
102             ClamperChangeListener clamperChangeListener, DisplayDeviceData data, Context context,
103             DisplayManagerFlags flags, SensorManager sensorManager) {
104         this(null, handler, clamperChangeListener, data, context, flags, sensorManager);
105     }
106 
107     @VisibleForTesting
BrightnessClamperController(Injector injector, Handler handler, ClamperChangeListener clamperChangeListener, DisplayDeviceData data, Context context, DisplayManagerFlags flags, SensorManager sensorManager)108     BrightnessClamperController(Injector injector, Handler handler,
109             ClamperChangeListener clamperChangeListener, DisplayDeviceData data, Context context,
110             DisplayManagerFlags flags, SensorManager sensorManager) {
111         mInjector = injector == null ? new Injector() : injector;
112         mDeviceConfigParameterProvider = mInjector.getDeviceConfigParameterProvider();
113         mHandler = handler;
114         mSensorManager = sensorManager;
115         mDisplayDeviceConfig = data.mDisplayDeviceConfig;
116         mLightSensorListener = new SensorEventListener() {
117             @Override
118             public void onSensorChanged(SensorEvent event) {
119                 long now = SystemClock.elapsedRealtime();
120                 mAmbientFilter.addValue(TimeUnit.NANOSECONDS.toMillis(event.timestamp),
121                         event.values[0]);
122                 final float lux = mAmbientFilter.getEstimate(now);
123                 mModifiers.forEach(mModifier -> mModifier.setAmbientLux(lux));
124             }
125 
126             @Override
127             public void onAccuracyChanged(Sensor sensor, int accuracy) {
128                 // unused
129             }
130         };
131 
132         mClamperChangeListenerExternal = clamperChangeListener;
133         mExecutor = new HandlerExecutor(handler);
134         mResources = context.getResources();
135         mLightSensorRate = context.getResources().getInteger(
136                 R.integer.config_autoBrightnessLightSensorRate);
137 
138         Runnable clamperChangeRunnableInternal = this::recalculateBrightnessCap;
139 
140         ClamperChangeListener clamperChangeListenerInternal = () -> {
141             if (!mHandler.hasCallbacks(clamperChangeRunnableInternal)) {
142                 mHandler.post(clamperChangeRunnableInternal);
143             }
144         };
145 
146         mClampers = mInjector.getClampers(handler, clamperChangeListenerInternal, data, flags,
147                 context);
148         mModifiers = mInjector.getModifiers(flags, context, handler, clamperChangeListener,
149                 data.mDisplayDeviceConfig, mSensorManager);
150         mOnPropertiesChangedListener =
151                 properties -> mClampers.forEach(BrightnessClamper::onDeviceConfigChanged);
152         start();
153     }
154 
155     /**
156      * Should be called when display changed. Forwards the call to individual clampers
157      */
onDisplayChanged(DisplayDeviceData data)158     public void onDisplayChanged(DisplayDeviceData data) {
159         mClampers.forEach(clamper -> clamper.onDisplayChanged(data));
160     }
161 
162     /**
163      * Applies clamping
164      * Called in DisplayControllerHandler
165      */
clamp(DisplayManagerInternal.DisplayPowerRequest request, float brightnessValue, boolean slowChange, int displayState)166     public DisplayBrightnessState clamp(DisplayManagerInternal.DisplayPowerRequest request,
167             float brightnessValue, boolean slowChange, int displayState) {
168         float cappedBrightness = Math.min(brightnessValue, mBrightnessCap);
169 
170         DisplayBrightnessState.Builder builder = DisplayBrightnessState.builder();
171         builder.setIsSlowChange(slowChange);
172         builder.setBrightness(cappedBrightness);
173         builder.setMaxBrightness(mBrightnessCap);
174         builder.setCustomAnimationRate(mCustomAnimationRate);
175 
176         if (mClamperType != null) {
177             builder.getBrightnessReason().addModifier(BrightnessReason.MODIFIER_THROTTLED);
178             if (!mClamperApplied) {
179                 builder.setIsSlowChange(false);
180             }
181             mClamperApplied = true;
182         } else {
183             mClamperApplied = false;
184         }
185 
186         if (displayState != STATE_ON) {
187             unregisterSensorListener();
188         } else {
189             maybeRegisterLightSensor();
190         }
191 
192         for (int i = 0; i < mModifiers.size(); i++) {
193             mModifiers.get(i).apply(request, builder);
194         }
195 
196         return builder.build();
197     }
198 
199     /**
200      * See BrightnessThrottler.getBrightnessMaxReason:
201      * used in:
202      * 1) DPC2.CachedBrightnessInfo to determine changes
203      * 2) DPC2.logBrightnessEvent
204      * 3) HBMController - for logging
205      * Method is called in mHandler thread (DisplayControllerHandler), in the same thread
206      * recalculateBrightnessCap and DPC2.updatePowerStateInternal are called.
207      * Should be moved to DisplayBrightnessState OR derived from DisplayBrightnessState
208      * TODO: b/263362199
209      */
210     @BrightnessInfo.BrightnessMaxReason
getBrightnessMaxReason()211     public int getBrightnessMaxReason() {
212         if (mClamperType == null) {
213             return BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE;
214         } else if (mClamperType == Type.THERMAL) {
215             return BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL;
216         } else if (mClamperType == Type.POWER) {
217             return BrightnessInfo.BRIGHTNESS_MAX_REASON_POWER_IC;
218         } else if (mClamperType == Type.WEAR_BEDTIME_MODE) {
219             return BrightnessInfo.BRIGHTNESS_MAX_REASON_WEAR_BEDTIME_MODE;
220         } else {
221             Slog.wtf(TAG, "BrightnessMaxReason not mapped for type=" + mClamperType);
222             return BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE;
223         }
224     }
225 
226     /**
227      * Used to dump ClampersController state.
228      */
dump(PrintWriter writer)229     public void dump(PrintWriter writer) {
230         writer.println("BrightnessClamperController:");
231         writer.println("  mBrightnessCap: " + mBrightnessCap);
232         writer.println("  mClamperType: " + mClamperType);
233         writer.println("  mClamperApplied: " + mClamperApplied);
234         writer.println("  mLightSensor=" + mLightSensor);
235         writer.println("  mRegisteredLightSensor=" + mRegisteredLightSensor);
236         IndentingPrintWriter ipw = new IndentingPrintWriter(writer, "    ");
237         mClampers.forEach(clamper -> clamper.dump(ipw));
238         mModifiers.forEach(modifier -> modifier.dump(ipw));
239     }
240 
241     /**
242      * This method should be called when the ClamperController is no longer in use.
243      * Called in DisplayControllerHandler
244      */
stop()245     public void stop() {
246         mDeviceConfigParameterProvider.removeOnPropertiesChangedListener(
247                 mOnPropertiesChangedListener);
248         mClampers.forEach(BrightnessClamper::stop);
249         mModifiers.forEach(BrightnessStateModifier::stop);
250     }
251 
252 
253     // Called in DisplayControllerHandler
recalculateBrightnessCap()254     private void recalculateBrightnessCap() {
255         float brightnessCap = PowerManager.BRIGHTNESS_MAX;
256         Type clamperType = null;
257         float customAnimationRate = DisplayBrightnessState.CUSTOM_ANIMATION_RATE_NOT_SET;
258 
259         BrightnessClamper<?> minClamper = mClampers.stream()
260                 .filter(BrightnessClamper::isActive)
261                 .min((clamper1, clamper2) -> Float.compare(clamper1.getBrightnessCap(),
262                         clamper2.getBrightnessCap())).orElse(null);
263 
264         if (minClamper != null) {
265             brightnessCap = minClamper.getBrightnessCap();
266             clamperType = minClamper.getType();
267             customAnimationRate = minClamper.getCustomAnimationRate();
268         }
269 
270         if (mBrightnessCap != brightnessCap
271                 || mClamperType != clamperType
272                 || mCustomAnimationRate != customAnimationRate) {
273             mBrightnessCap = brightnessCap;
274             mClamperType = clamperType;
275             mCustomAnimationRate = customAnimationRate;
276             mClamperChangeListenerExternal.onChanged();
277         }
278     }
279 
start()280     private void start() {
281         if (!mClampers.isEmpty()) {
282             mDeviceConfigParameterProvider.addOnPropertiesChangedListener(
283                     mExecutor, mOnPropertiesChangedListener);
284             reloadLightSensorData(mDisplayDeviceConfig);
285             mLightSensor = mInjector.getLightSensor(
286                     mSensorManager, mLightSensorType, mLightSensorName);
287             maybeRegisterLightSensor();
288         }
289     }
290 
291     /**
292      * Clampers change listener
293      */
294     public interface ClamperChangeListener {
295         /**
296          * Notifies that clamper state changed
297          */
onChanged()298         void onChanged();
299     }
300 
301     @VisibleForTesting
302     static class Injector {
getDeviceConfigParameterProvider()303         DeviceConfigParameterProvider getDeviceConfigParameterProvider() {
304             return new DeviceConfigParameterProvider(DeviceConfigInterface.REAL);
305         }
306 
getClampers(Handler handler, ClamperChangeListener clamperChangeListener, DisplayDeviceData data, DisplayManagerFlags flags, Context context)307         List<BrightnessClamper<? super DisplayDeviceData>> getClampers(Handler handler,
308                 ClamperChangeListener clamperChangeListener, DisplayDeviceData data,
309                 DisplayManagerFlags flags, Context context) {
310             List<BrightnessClamper<? super DisplayDeviceData>> clampers = new ArrayList<>();
311             clampers.add(
312                     new BrightnessThermalClamper(handler, clamperChangeListener, data));
313             if (flags.isPowerThrottlingClamperEnabled()) {
314                 clampers.add(new BrightnessPowerClamper(handler, clamperChangeListener,
315                         data));
316             }
317             if (flags.isBrightnessWearBedtimeModeClamperEnabled()) {
318                 clampers.add(new BrightnessWearBedtimeModeClamper(handler, context,
319                         clamperChangeListener, data));
320             }
321             return clampers;
322         }
323 
getModifiers(DisplayManagerFlags flags, Context context, Handler handler, ClamperChangeListener listener, DisplayDeviceConfig displayDeviceConfig, SensorManager sensorManager)324         List<BrightnessStateModifier> getModifiers(DisplayManagerFlags flags, Context context,
325                 Handler handler, ClamperChangeListener listener,
326                 DisplayDeviceConfig displayDeviceConfig, SensorManager sensorManager) {
327             List<BrightnessStateModifier> modifiers = new ArrayList<>();
328             modifiers.add(new DisplayDimModifier(context));
329             modifiers.add(new BrightnessLowPowerModeModifier());
330             if (flags.isEvenDimmerEnabled() && displayDeviceConfig != null
331                     && displayDeviceConfig.isEvenDimmerAvailable()) {
332                 modifiers.add(new BrightnessLowLuxModifier(handler, listener, context,
333                         displayDeviceConfig));
334             }
335             return modifiers;
336         }
337 
getLightSensor(SensorManager sensorManager, String type, String name)338         Sensor getLightSensor(SensorManager sensorManager, String type, String name) {
339             return SensorUtils.findSensor(sensorManager, type,
340                     name, Sensor.TYPE_LIGHT);
341         }
342 
343     }
344 
345     /**
346      * Config Data for clampers
347      */
348     public static class DisplayDeviceData implements BrightnessThermalClamper.ThermalData,
349             BrightnessPowerClamper.PowerData,
350             BrightnessWearBedtimeModeClamper.WearBedtimeModeData {
351         @NonNull
352         private final String mUniqueDisplayId;
353         @NonNull
354         private final String mThermalThrottlingDataId;
355         @NonNull
356         private final String mPowerThrottlingDataId;
357 
358         private final DisplayDeviceConfig mDisplayDeviceConfig;
359 
DisplayDeviceData(@onNull String uniqueDisplayId, @NonNull String thermalThrottlingDataId, @NonNull String powerThrottlingDataId, @NonNull DisplayDeviceConfig displayDeviceConfig)360         public DisplayDeviceData(@NonNull String uniqueDisplayId,
361                 @NonNull String thermalThrottlingDataId,
362                 @NonNull String powerThrottlingDataId,
363                 @NonNull DisplayDeviceConfig displayDeviceConfig) {
364             mUniqueDisplayId = uniqueDisplayId;
365             mThermalThrottlingDataId = thermalThrottlingDataId;
366             mPowerThrottlingDataId = powerThrottlingDataId;
367             mDisplayDeviceConfig = displayDeviceConfig;
368         }
369 
370 
371         @NonNull
372         @Override
getUniqueDisplayId()373         public String getUniqueDisplayId() {
374             return mUniqueDisplayId;
375         }
376 
377         @NonNull
378         @Override
getThermalThrottlingDataId()379         public String getThermalThrottlingDataId() {
380             return mThermalThrottlingDataId;
381         }
382 
383         @Nullable
384         @Override
getThermalBrightnessThrottlingData()385         public ThermalBrightnessThrottlingData getThermalBrightnessThrottlingData() {
386             return mDisplayDeviceConfig.getThermalBrightnessThrottlingDataMapByThrottlingId().get(
387                     mThermalThrottlingDataId);
388         }
389 
390         @NonNull
391         @Override
getPowerThrottlingDataId()392         public String getPowerThrottlingDataId() {
393             return mPowerThrottlingDataId;
394         }
395 
396         @Nullable
397         @Override
getPowerThrottlingData()398         public PowerThrottlingData getPowerThrottlingData() {
399             return mDisplayDeviceConfig.getPowerThrottlingDataMapByThrottlingId().get(
400                     mPowerThrottlingDataId);
401         }
402 
403         @Nullable
404         @Override
getPowerThrottlingConfigData()405         public PowerThrottlingConfigData getPowerThrottlingConfigData() {
406             return mDisplayDeviceConfig.getPowerThrottlingConfigData();
407         }
408 
409         @Override
getBrightnessWearBedtimeModeCap()410         public float getBrightnessWearBedtimeModeCap() {
411             return mDisplayDeviceConfig.getBrightnessCapForWearBedtimeMode();
412         }
413 
414         @NonNull
getTempSensor()415         public SensorData getTempSensor() {
416             return mDisplayDeviceConfig.getTempSensor();
417         }
418     }
419 
maybeRegisterLightSensor()420     private void maybeRegisterLightSensor() {
421         if (mModifiers.stream().noneMatch(BrightnessStateModifier::shouldListenToLightSensor)) {
422             return;
423         }
424 
425         if (mRegisteredLightSensor == mLightSensor) {
426             return;
427         }
428 
429         if (mRegisteredLightSensor != null) {
430             unregisterSensorListener();
431         }
432 
433         mAmbientFilter = AmbientFilterFactory.createBrightnessFilter(TAG, mResources);
434         mSensorManager.registerListener(mLightSensorListener,
435                 mLightSensor, mLightSensorRate * 1000, mHandler);
436         mRegisteredLightSensor = mLightSensor;
437 
438         if (DEBUG) {
439             Slog.d(TAG, "maybeRegisterLightSensor");
440         }
441     }
442 
unregisterSensorListener()443     private void unregisterSensorListener() {
444         mSensorManager.unregisterListener(mLightSensorListener);
445         mRegisteredLightSensor = null;
446         mModifiers.forEach(mModifier -> mModifier.setAmbientLux(INVALID_LUX)); // set lux to invalid
447         if (DEBUG) {
448             Slog.d(TAG, "unregisterSensorListener");
449         }
450     }
451 
reloadLightSensorData(DisplayDeviceConfig displayDeviceConfig)452     private void reloadLightSensorData(DisplayDeviceConfig displayDeviceConfig) {
453         // The displayDeviceConfig (ddc) contains display specific preferences. When loaded,
454         // it naturally falls back to the global config.xml.
455         if (displayDeviceConfig != null
456                 && displayDeviceConfig.getAmbientLightSensor() != null) {
457             // This covers both the ddc and the config.xml fallback
458             mLightSensorType = displayDeviceConfig.getAmbientLightSensor().type;
459             mLightSensorName = displayDeviceConfig.getAmbientLightSensor().name;
460         } else if (mLightSensorName == null && mLightSensorType == null) {
461             mLightSensorType = mResources.getString(
462                     com.android.internal.R.string.config_displayLightSensorType);
463             mLightSensorName = "";
464         }
465     }
466 }
467