• 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 android.view.Display.STATE_ON;
20 
21 import android.annotation.NonNull;
22 import android.annotation.Nullable;
23 import android.content.Context;
24 import android.hardware.SensorManager;
25 import android.hardware.display.BrightnessInfo;
26 import android.hardware.display.DisplayManagerInternal;
27 import android.os.Handler;
28 import android.os.HandlerExecutor;
29 import android.os.IBinder;
30 import android.os.PowerManager;
31 import android.provider.DeviceConfig;
32 import android.provider.DeviceConfigInterface;
33 import android.util.IndentingPrintWriter;
34 import android.util.Spline;
35 import android.view.Display;
36 
37 import com.android.internal.annotations.VisibleForTesting;
38 import com.android.internal.display.BrightnessSynchronizer;
39 import com.android.server.display.DisplayBrightnessState;
40 import com.android.server.display.DisplayDeviceConfig;
41 import com.android.server.display.DisplayDeviceConfig.PowerThrottlingConfigData;
42 import com.android.server.display.DisplayDeviceConfig.PowerThrottlingData;
43 import com.android.server.display.DisplayDeviceConfig.ThermalBrightnessThrottlingData;
44 import com.android.server.display.config.SensorData;
45 import com.android.server.display.feature.DeviceConfigParameterProvider;
46 import com.android.server.display.feature.DisplayManagerFlags;
47 
48 import java.io.PrintWriter;
49 import java.util.ArrayList;
50 import java.util.List;
51 import java.util.concurrent.Executor;
52 
53 /**
54  * Clampers controller, all in DisplayControllerHandler
55  */
56 public class BrightnessClamperController {
57     private static final String TAG = "BrightnessClamperController";
58 
59     private final DeviceConfigParameterProvider mDeviceConfigParameterProvider;
60     private final Handler mHandler;
61     private final LightSensorController mLightSensorController;
62     private int mDisplayState = Display.STATE_OFF;
63 
64     private final ClamperChangeListener mClamperChangeListenerExternal;
65     private final Executor mExecutor;
66 
67     private final List<BrightnessStateModifier> mModifiers;
68 
69     private final List<DisplayDeviceDataListener> mDisplayDeviceDataListeners = new ArrayList<>();
70     private final List<StatefulModifier> mStatefulModifiers = new ArrayList<>();
71     private final List<UserSwitchListener> mUserSwitchListeners = new ArrayList<>();
72     private final List<DeviceConfigListener> mDeviceConfigListeners = new ArrayList<>();
73 
74     private ModifiersAggregatedState mModifiersAggregatedState = new ModifiersAggregatedState();
75 
76     private final DeviceConfig.OnPropertiesChangedListener mOnPropertiesChangedListener;
77 
78     private final LightSensorController.LightSensorListener mLightSensorListener =
79             new LightSensorController.LightSensorListener() {
80                 @Override
81                 public void onAmbientLuxChange(float lux) {
82                     mModifiers.forEach(mModifier -> mModifier.setAmbientLux(lux));
83                 }
84             };
85 
86     private volatile boolean mStarted = false;
87 
BrightnessClamperController(Handler handler, ClamperChangeListener clamperChangeListener, DisplayDeviceData data, Context context, DisplayManagerFlags flags, SensorManager sensorManager, float currentBrightness)88     public BrightnessClamperController(Handler handler,
89             ClamperChangeListener clamperChangeListener, DisplayDeviceData data, Context context,
90             DisplayManagerFlags flags, SensorManager sensorManager, float currentBrightness) {
91         this(new Injector(), handler, clamperChangeListener, data, context, flags, sensorManager,
92                 currentBrightness);
93     }
94 
95     @VisibleForTesting
BrightnessClamperController(Injector injector, Handler handler, ClamperChangeListener clamperChangeListener, DisplayDeviceData data, Context context, DisplayManagerFlags flags, SensorManager sensorManager, float currentBrightness)96     BrightnessClamperController(Injector injector, Handler handler,
97             ClamperChangeListener clamperChangeListener, DisplayDeviceData data, Context context,
98             DisplayManagerFlags flags, SensorManager sensorManager, float currentBrightness) {
99         mDeviceConfigParameterProvider = injector.getDeviceConfigParameterProvider();
100         mHandler = handler;
101         mLightSensorController = injector.getLightSensorController(sensorManager, context,
102                 mLightSensorListener, mHandler);
103 
104         mClamperChangeListenerExternal = clamperChangeListener;
105         mExecutor = new HandlerExecutor(handler);
106 
107         Runnable modifiersChangeRunnableInternal = this::recalculateModifiersState;
108         ClamperChangeListener clamperChangeListenerInternal = () -> {
109             if (mStarted && !mHandler.hasCallbacks(modifiersChangeRunnableInternal)) {
110                 mHandler.post(modifiersChangeRunnableInternal);
111             }
112         };
113 
114         mModifiers = injector.getModifiers(flags, context, handler, clamperChangeListenerInternal,
115                 data, currentBrightness);
116 
117         mModifiers.forEach(m -> {
118             if (m instanceof  DisplayDeviceDataListener l) {
119                 mDisplayDeviceDataListeners.add(l);
120             }
121             if (m instanceof StatefulModifier s) {
122                 mStatefulModifiers.add(s);
123             }
124             if (m instanceof UserSwitchListener l) {
125                 mUserSwitchListeners.add(l);
126             }
127             if (m instanceof DeviceConfigListener l) {
128                 mDeviceConfigListeners.add(l);
129             }
130         });
131         mOnPropertiesChangedListener = properties -> {
132             mDeviceConfigListeners.forEach(DeviceConfigListener::onDeviceConfigChanged);
133         };
134         mLightSensorController.configure(data.getAmbientLightSensor(), data.getDisplayId());
135         start();
136     }
137 
138     /**
139      * Should be called when display changed. Forwards the call to individual clampers
140      */
onDisplayChanged(DisplayDeviceData data)141     public void onDisplayChanged(DisplayDeviceData data) {
142         mLightSensorController.configure(data.getAmbientLightSensor(), data.getDisplayId());
143         mDisplayDeviceDataListeners.forEach(l -> l.onDisplayChanged(data));
144         adjustLightSensorSubscription();
145     }
146 
147     /**
148      * Applies clamping
149      * Called in DisplayControllerHandler
150      */
clamp(DisplayBrightnessState displayBrightnessState, DisplayManagerInternal.DisplayPowerRequest request, float brightnessValue, boolean slowChange, int displayState)151     public DisplayBrightnessState clamp(DisplayBrightnessState displayBrightnessState,
152             DisplayManagerInternal.DisplayPowerRequest request,
153             float brightnessValue, boolean slowChange, int displayState) {
154         mDisplayState = displayState;
155         DisplayBrightnessState.Builder builder = DisplayBrightnessState.Builder.from(
156                 displayBrightnessState);
157         builder.setIsSlowChange(slowChange);
158         builder.setBrightness(brightnessValue);
159 
160         adjustLightSensorSubscription();
161 
162         for (int i = 0; i < mModifiers.size(); i++) {
163             mModifiers.get(i).apply(request, builder);
164         }
165 
166         return builder.build();
167     }
168 
169     /**
170      * Called when the user switches.
171      */
onUserSwitch()172     public void onUserSwitch() {
173         mUserSwitchListeners.forEach(UserSwitchListener::onSwitchUser);
174     }
175 
176     /**
177      * Used to dump ClampersController state.
178      */
dump(PrintWriter writer)179     public void dump(PrintWriter writer) {
180         writer.println("BrightnessClamperController:");
181         IndentingPrintWriter ipw = new IndentingPrintWriter(writer, "    ");
182         mLightSensorController.dump(ipw);
183         mModifiers.forEach(modifier -> modifier.dump(ipw));
184     }
185 
186     /**
187      * This method should be called when the ClamperController is no longer in use.
188      * Called in DisplayControllerHandler
189      */
stop()190     public void stop() {
191         mStarted = false;
192         mDeviceConfigParameterProvider.removeOnPropertiesChangedListener(
193                 mOnPropertiesChangedListener);
194         mLightSensorController.stop();
195         mModifiers.forEach(BrightnessStateModifier::stop);
196     }
197 
198     /**
199      * returns max allowed brightness.
200      * TODO(b/387452517): introduce constrainBrightness method
201      */
getMaxBrightness()202     public float getMaxBrightness() {
203         return mModifiersAggregatedState.mMaxBrightness;
204     }
205 
isThrottled()206     public boolean isThrottled() {
207         return mModifiersAggregatedState.mMaxBrightnessReason
208                 != BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE;
209     }
210 
211 
212     // Called in DisplayControllerHandler
recalculateModifiersState()213     private void recalculateModifiersState() {
214         ModifiersAggregatedState newAggregatedState = new ModifiersAggregatedState();
215         mStatefulModifiers.forEach((modifier) -> modifier.applyStateChange(newAggregatedState));
216 
217         if (needToNotifyExternalListener(mModifiersAggregatedState, newAggregatedState)) {
218             mClamperChangeListenerExternal.onChanged();
219         }
220         mModifiersAggregatedState = newAggregatedState;
221     }
222 
needToNotifyExternalListener(ModifiersAggregatedState state1, ModifiersAggregatedState state2)223     private boolean needToNotifyExternalListener(ModifiersAggregatedState state1,
224             ModifiersAggregatedState state2) {
225         return !BrightnessSynchronizer.floatEquals(state1.mMaxDesiredHdrRatio,
226                 state2.mMaxDesiredHdrRatio)
227                 || !BrightnessSynchronizer.floatEquals(state1.mMaxHdrBrightness,
228                 state2.mMaxHdrBrightness)
229                 || state1.mSdrHdrRatioSpline != state2.mSdrHdrRatioSpline
230                 || state1.mMaxBrightnessReason != state2.mMaxBrightnessReason
231                 || !BrightnessSynchronizer.floatEquals(state1.mMaxBrightness,
232                 state2.mMaxBrightness);
233     }
234 
start()235     private void start() {
236         if (!mDeviceConfigListeners.isEmpty()) {
237             mDeviceConfigParameterProvider.addOnPropertiesChangedListener(
238                     mExecutor, mOnPropertiesChangedListener);
239         }
240         adjustLightSensorSubscription();
241         mStarted = true;
242     }
243 
adjustLightSensorSubscription()244     private void adjustLightSensorSubscription() {
245         if (mDisplayState == STATE_ON && mModifiers.stream()
246                 .anyMatch(BrightnessStateModifier::shouldListenToLightSensor)) {
247             mLightSensorController.restart();
248         } else {
249             mLightSensorController.stop();
250         }
251     }
252 
253     /**
254      * Clampers change listener
255      */
256     public interface ClamperChangeListener {
257         /**
258          * Notifies that clamper state changed
259          */
onChanged()260         void onChanged();
261     }
262 
263     @VisibleForTesting
264     static class Injector {
getDeviceConfigParameterProvider()265         DeviceConfigParameterProvider getDeviceConfigParameterProvider() {
266             return new DeviceConfigParameterProvider(DeviceConfigInterface.REAL);
267         }
268 
getModifiers(DisplayManagerFlags flags, Context context, Handler handler, ClamperChangeListener listener, DisplayDeviceData data, float currentBrightness)269         List<BrightnessStateModifier> getModifiers(DisplayManagerFlags flags, Context context,
270                 Handler handler, ClamperChangeListener listener,
271                 DisplayDeviceData data, float currentBrightness) {
272             List<BrightnessStateModifier> modifiers = new ArrayList<>();
273             modifiers.add(new BrightnessThermalModifier(handler, listener, data));
274             if (flags.isBrightnessWearBedtimeModeClamperEnabled()) {
275                 modifiers.add(new BrightnessWearBedtimeModeModifier(handler, context,
276                         listener, data));
277             }
278             if (flags.isPowerThrottlingClamperEnabled()) {
279                 // Check if power-throttling config is present.
280                 PowerThrottlingConfigData configData = data.getPowerThrottlingConfigData();
281                 if (configData != null) {
282                     modifiers.add(new BrightnessPowerModifier(handler, listener,
283                             data, currentBrightness));
284                 }
285             }
286 
287             modifiers.add(new DisplayDimModifier(data.mDisplayId, context));
288             modifiers.add(new BrightnessLowPowerModeModifier());
289             if (flags.isEvenDimmerEnabled() && data.mDisplayDeviceConfig.isEvenDimmerAvailable()) {
290                 modifiers.add(new BrightnessLowLuxModifier(handler, listener, context,
291                         data.mDisplayDeviceConfig));
292             }
293             if (flags.useNewHdrBrightnessModifier()) {
294                 modifiers.add(new HdrBrightnessModifier(handler, context, listener, data));
295             }
296             return modifiers;
297         }
298 
getLightSensorController(SensorManager sensorManager, Context context, LightSensorController.LightSensorListener listener, Handler handler)299         LightSensorController getLightSensorController(SensorManager sensorManager,
300                 Context context, LightSensorController.LightSensorListener listener,
301                 Handler handler) {
302             return new LightSensorController(sensorManager, context.getResources(),
303                     listener, handler);
304         }
305     }
306 
307     /**
308      * Modifier should implement this interface in order to receive display change updates
309      */
310     interface DisplayDeviceDataListener {
onDisplayChanged(DisplayDeviceData displayData)311         void onDisplayChanged(DisplayDeviceData displayData);
312     }
313 
314     /**
315      * Config Data for clampers/modifiers
316      */
317     public static class DisplayDeviceData implements BrightnessThermalModifier.ThermalData,
318             BrightnessPowerModifier.PowerData,
319             BrightnessWearBedtimeModeModifier.WearBedtimeModeData {
320         @NonNull
321         private final String mUniqueDisplayId;
322         @Nullable
323         private final String mThermalThrottlingDataId;
324         @NonNull
325         private final String mPowerThrottlingDataId;
326         @NonNull
327         final DisplayDeviceConfig mDisplayDeviceConfig;
328 
329         final int mWidth;
330 
331         final int mHeight;
332 
333         final IBinder mDisplayToken;
334 
335         final int mDisplayId;
336 
DisplayDeviceData(@onNull String uniqueDisplayId, @Nullable String thermalThrottlingDataId, @NonNull String powerThrottlingDataId, @NonNull DisplayDeviceConfig displayDeviceConfig, int width, int height, IBinder displayToken, int displayId)337         public DisplayDeviceData(@NonNull String uniqueDisplayId,
338                 @Nullable String thermalThrottlingDataId,
339                 @NonNull String powerThrottlingDataId,
340                 @NonNull DisplayDeviceConfig displayDeviceConfig,
341                 int width,
342                 int height,
343                 IBinder displayToken,
344                 int displayId) {
345             mUniqueDisplayId = uniqueDisplayId;
346             mThermalThrottlingDataId = thermalThrottlingDataId;
347             mPowerThrottlingDataId = powerThrottlingDataId;
348             mDisplayDeviceConfig = displayDeviceConfig;
349             mWidth = width;
350             mHeight = height;
351             mDisplayToken = displayToken;
352             mDisplayId = displayId;
353         }
354 
355         @NonNull
356         @Override
getUniqueDisplayId()357         public String getUniqueDisplayId() {
358             return mUniqueDisplayId;
359         }
360 
361         @Nullable
362         @Override
getThermalThrottlingDataId()363         public String getThermalThrottlingDataId() {
364             return mThermalThrottlingDataId;
365         }
366 
367         @Nullable
368         @Override
getThermalBrightnessThrottlingData()369         public ThermalBrightnessThrottlingData getThermalBrightnessThrottlingData() {
370             if (mThermalThrottlingDataId == null) {
371                 return null;
372             }
373             return mDisplayDeviceConfig.getThermalBrightnessThrottlingDataMapByThrottlingId().get(
374                     mThermalThrottlingDataId);
375         }
376 
377         @NonNull
378         @Override
getPowerThrottlingDataId()379         public String getPowerThrottlingDataId() {
380             return mPowerThrottlingDataId;
381         }
382 
383         @Nullable
384         @Override
getPowerThrottlingData()385         public PowerThrottlingData getPowerThrottlingData() {
386             return mDisplayDeviceConfig.getPowerThrottlingDataMapByThrottlingId().get(
387                     mPowerThrottlingDataId);
388         }
389 
390         @Nullable
391         @Override
getPowerThrottlingConfigData()392         public PowerThrottlingConfigData getPowerThrottlingConfigData() {
393             return mDisplayDeviceConfig.getPowerThrottlingConfigData();
394         }
395 
396         @Override
getBrightnessWearBedtimeModeCap()397         public float getBrightnessWearBedtimeModeCap() {
398             return mDisplayDeviceConfig.getBrightnessCapForWearBedtimeMode();
399         }
400 
401         @NonNull
402         @Override
getTempSensor()403         public SensorData getTempSensor() {
404             return mDisplayDeviceConfig.getTempSensor();
405         }
406 
407         @NonNull
getAmbientLightSensor()408         SensorData getAmbientLightSensor() {
409             return mDisplayDeviceConfig.getAmbientLightSensor();
410         }
411 
getDisplayId()412         int getDisplayId() {
413             return mDisplayId;
414         }
415     }
416 
417     /**
418      * Stateful modifier should implement this interface and modify aggregatedState.
419      * AggregatedState is used by Controller to determine if updatePowerState call is needed
420      * to correctly adjust brightness
421      */
422     interface StatefulModifier {
applyStateChange(ModifiersAggregatedState aggregatedState)423         void applyStateChange(ModifiersAggregatedState aggregatedState);
424     }
425 
426     /**
427      * A clamper/modifier should implement this interface if it reads user-specific settings
428      */
429     interface UserSwitchListener {
onSwitchUser()430         void onSwitchUser();
431     }
432 
433     /**
434      * Modifier should implement this interface in order to receive device config updates
435      */
436     interface DeviceConfigListener {
onDeviceConfigChanged()437         void onDeviceConfigChanged();
438     }
439 
440     /**
441      * StatefulModifiers contribute to AggregatedState, that is used to decide if brightness
442      * adjustment is needed
443      */
444     public static class ModifiersAggregatedState {
445         float mMaxDesiredHdrRatio = HdrBrightnessModifier.DEFAULT_MAX_HDR_SDR_RATIO;
446         float mMaxHdrBrightness = PowerManager.BRIGHTNESS_MAX;
447         @Nullable
448         Spline mSdrHdrRatioSpline = null;
449         @BrightnessInfo.BrightnessMaxReason
450         int mMaxBrightnessReason = BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE;
451         float mMaxBrightness = PowerManager.BRIGHTNESS_MAX;
452     }
453 }
454