1 /*
2  * Copyright (C) 2022 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;
18 
19 import android.annotation.Nullable;
20 import android.content.Context;
21 import android.hardware.display.DisplayManagerInternal;
22 import android.os.HandlerExecutor;
23 import android.os.PowerManager;
24 import android.util.IndentingPrintWriter;
25 import android.view.Display;
26 
27 import com.android.internal.annotations.GuardedBy;
28 import com.android.internal.annotations.VisibleForTesting;
29 import com.android.server.display.AutomaticBrightnessController;
30 import com.android.server.display.BrightnessSetting;
31 import com.android.server.display.DisplayBrightnessState;
32 import com.android.server.display.brightness.strategy.AutomaticBrightnessStrategy;
33 import com.android.server.display.brightness.strategy.DisplayBrightnessStrategy;
34 
35 import java.io.PrintWriter;
36 
37 /**
38  * Deploys different DozeBrightnessStrategy to choose the current brightness for a specified
39  * display. Applies the chosen brightness.
40  */
41 public final class DisplayBrightnessController {
42     private static final int DEFAULT_USER_SERIAL = -1;
43 
44     // The ID of the display tied to this DisplayBrightnessController
45     private final int mDisplayId;
46 
47     // The lock which is to be used to synchronize the resources being used in this class
48     private final Object mLock = new Object();
49 
50     // The default screen brightness to be used when no value is available in BrightnessSetting.
51     private final float mScreenBrightnessDefault;
52 
53     // This is used to persist the changes happening to the brightness.
54     private final BrightnessSetting mBrightnessSetting;
55 
56     // A runnable to update the clients registered via DisplayManagerGlobal
57     // .EVENT_DISPLAY_BRIGHTNESS_CHANGED about the brightness change. Called when
58     // mCurrentScreenBrightness is updated.
59     private Runnable mOnBrightnessChangeRunnable;
60 
61     // The screen brightness that has changed but not taken effect yet. If this is different
62     // from the current screen brightness then this is coming from something other than us
63     // and should be considered a user interaction.
64     @GuardedBy("mLock")
65     private float mPendingScreenBrightness;
66 
67     // The last observed screen brightness, either set by us or by the settings app on
68     // behalf of the user.
69     @GuardedBy("mLock")
70     private float mCurrentScreenBrightness;
71 
72     // The last brightness that was set by the user and not temporary. Set to
73     // PowerManager.BRIGHTNESS_INVALID_FLOAT when a brightness has yet to be recorded.
74     @GuardedBy("mLock")
75     private float mLastUserSetScreenBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
76 
77     // The listener which is to be notified everytime there is a change in the brightness in the
78     // BrightnessSetting.
79     private BrightnessSetting.BrightnessSettingListener mBrightnessSettingListener;
80 
81     // Selects an appropriate strategy based on the request provided by the clients.
82     @GuardedBy("mLock")
83     private DisplayBrightnessStrategySelector mDisplayBrightnessStrategySelector;
84 
85     // Currently selected DisplayBrightnessStrategy.
86     @GuardedBy("mLock")
87     private DisplayBrightnessStrategy mDisplayBrightnessStrategy;
88 
89     // The executor on which the mOnBrightnessChangeRunnable is executed. This ensures that the
90     // callback is not executed in sync and is not blocking the thread from which it is called.
91     private final HandlerExecutor mBrightnessChangeExecutor;
92 
93     // True if we want to persist the brightness value in nits even if the underlying display
94     // device changes.
95     private final boolean mPersistBrightnessNitsForDefaultDisplay;
96 
97     // The controller for the automatic brightness level.
98     // TODO(b/265415257): Move to the automatic brightness strategy
99     @Nullable
100     private AutomaticBrightnessController mAutomaticBrightnessController;
101 
102     /**
103      * The constructor of DisplayBrightnessController.
104      */
DisplayBrightnessController(Context context, Injector injector, int displayId, float defaultScreenBrightness, BrightnessSetting brightnessSetting, Runnable onBrightnessChangeRunnable, HandlerExecutor brightnessChangeExecutor)105     public DisplayBrightnessController(Context context, Injector injector, int displayId,
106             float defaultScreenBrightness, BrightnessSetting brightnessSetting,
107             Runnable onBrightnessChangeRunnable, HandlerExecutor brightnessChangeExecutor) {
108         if (injector == null) {
109             injector = new Injector();
110         }
111         mDisplayId = displayId;
112         // TODO: b/186428377 update brightness setting when display changes
113         mBrightnessSetting = brightnessSetting;
114         mPendingScreenBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
115         mScreenBrightnessDefault = BrightnessUtils.clampAbsoluteBrightness(defaultScreenBrightness);
116         mCurrentScreenBrightness = getScreenBrightnessSetting();
117         mOnBrightnessChangeRunnable = onBrightnessChangeRunnable;
118         mDisplayBrightnessStrategySelector = injector.getDisplayBrightnessStrategySelector(context,
119                 displayId);
120         mBrightnessChangeExecutor = brightnessChangeExecutor;
121         mPersistBrightnessNitsForDefaultDisplay = context.getResources().getBoolean(
122                 com.android.internal.R.bool.config_persistBrightnessNitsForDefaultDisplay);
123     }
124 
125     /**
126      * Updates the display brightness. This delegates the responsibility of selecting an appropriate
127      * strategy to DisplayBrightnessStrategySelector, which is then applied to evaluate the
128      * DisplayBrightnessState. In the future,
129      * 1. This will account for clamping the brightness if needed.
130      * 2. This will notify the system about the updated brightness
131      *
132      * @param displayPowerRequest The request to update the brightness
133      * @param targetDisplayState  The target display state of the system
134      */
updateBrightness( DisplayManagerInternal.DisplayPowerRequest displayPowerRequest, int targetDisplayState)135     public DisplayBrightnessState updateBrightness(
136             DisplayManagerInternal.DisplayPowerRequest displayPowerRequest,
137             int targetDisplayState) {
138 
139         DisplayBrightnessState state;
140         synchronized (mLock) {
141             mDisplayBrightnessStrategy = mDisplayBrightnessStrategySelector.selectStrategy(
142                     displayPowerRequest, targetDisplayState);
143             state = mDisplayBrightnessStrategy.updateBrightness(displayPowerRequest);
144         }
145 
146         // This is a temporary measure until AutomaticBrightnessStrategy works as a traditional
147         // strategy.
148         // TODO: Remove when AutomaticBrightnessStrategy is populating the values directly.
149         if (state != null) {
150             state = addAutomaticBrightnessState(state);
151         }
152         return state;
153     }
154 
155     /**
156      * Sets the temporary brightness
157      */
setTemporaryBrightness(Float temporaryBrightness)158     public void setTemporaryBrightness(Float temporaryBrightness) {
159         synchronized (mLock) {
160             setTemporaryBrightnessLocked(temporaryBrightness);
161         }
162     }
163 
164     /**
165      * Sets the brightness to follow
166      */
setBrightnessToFollow(float brightnessToFollow, boolean slowChange)167     public void setBrightnessToFollow(float brightnessToFollow, boolean slowChange) {
168         synchronized (mLock) {
169             mDisplayBrightnessStrategySelector.getFollowerDisplayBrightnessStrategy()
170                     .setBrightnessToFollow(brightnessToFollow, slowChange);
171         }
172     }
173 
174     /**
175      * Returns a boolean flag indicating if the light sensor is to be used to decide the screen
176      * brightness when dozing
177      */
isAllowAutoBrightnessWhileDozingConfig()178     public boolean isAllowAutoBrightnessWhileDozingConfig() {
179         synchronized (mLock) {
180             return mDisplayBrightnessStrategySelector.isAllowAutoBrightnessWhileDozingConfig();
181         }
182     }
183 
184     /**
185      * Sets the current screen brightness to the supplied value, and notifies all the listeners
186      * requesting for change events on brightness change.
187      */
setAndNotifyCurrentScreenBrightness(float brightnessValue)188     public void setAndNotifyCurrentScreenBrightness(float brightnessValue) {
189         final boolean hasBrightnessChanged;
190         synchronized (mLock) {
191             hasBrightnessChanged = (brightnessValue != mCurrentScreenBrightness);
192             setCurrentScreenBrightnessLocked(brightnessValue);
193         }
194         if (hasBrightnessChanged) {
195             notifyCurrentScreenBrightness();
196         }
197     }
198 
199     /**
200      * Returns the last observed screen brightness.
201      */
getCurrentBrightness()202     public float getCurrentBrightness() {
203         synchronized (mLock) {
204             return mCurrentScreenBrightness;
205         }
206     }
207 
208     /**
209      * Returns the screen brightness which has changed but has not taken any effect so far.
210      */
getPendingScreenBrightness()211     public float getPendingScreenBrightness() {
212         synchronized (mLock) {
213             return mPendingScreenBrightness;
214         }
215     }
216 
217     /**
218      * Sets the pending screen brightness setting, representing a value which is requested, but not
219      * yet processed.
220      * @param brightnessValue The value to which the pending screen brightness is to be set.
221      */
setPendingScreenBrightness(float brightnessValue)222     public void setPendingScreenBrightness(float brightnessValue) {
223         synchronized (mLock) {
224             mPendingScreenBrightness = brightnessValue;
225         }
226     }
227 
228     /**
229      * We want to return true if the user has set the screen brightness.
230      * RBC on, off, and intensity changes will return false.
231      * Slider interactions whilst in RBC will return true, just as when in non-rbc.
232      */
updateUserSetScreenBrightness()233     public boolean updateUserSetScreenBrightness() {
234         synchronized (mLock) {
235             if (!BrightnessUtils.isValidBrightnessValue(mPendingScreenBrightness)) {
236                 return false;
237             }
238             if (mCurrentScreenBrightness == mPendingScreenBrightness) {
239                 mPendingScreenBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
240                 setTemporaryBrightnessLocked(PowerManager.BRIGHTNESS_INVALID_FLOAT);
241                 return false;
242             }
243             setCurrentScreenBrightnessLocked(mPendingScreenBrightness);
244             mLastUserSetScreenBrightness = mPendingScreenBrightness;
245             mPendingScreenBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
246             setTemporaryBrightnessLocked(PowerManager.BRIGHTNESS_INVALID_FLOAT);
247         }
248         notifyCurrentScreenBrightness();
249         return true;
250 
251     }
252 
253     /**
254      * Registers the BrightnessSettingListener with the BrightnessSetting, which will be notified
255      * everytime there is a change in the brightness.
256      */
registerBrightnessSettingChangeListener( BrightnessSetting.BrightnessSettingListener brightnessSettingListener)257     public void registerBrightnessSettingChangeListener(
258             BrightnessSetting.BrightnessSettingListener brightnessSettingListener) {
259         mBrightnessSettingListener = brightnessSettingListener;
260         mBrightnessSetting.registerListener(mBrightnessSettingListener);
261     }
262 
263     /**
264      * Returns the last user set brightness which is not temporary.
265      */
getLastUserSetScreenBrightness()266     public float getLastUserSetScreenBrightness() {
267         synchronized (mLock) {
268             return mLastUserSetScreenBrightness;
269         }
270     }
271 
272     /**
273      * Returns the current screen brightnessSetting which is responsible for saving the brightness
274      * in the persistent store
275      */
getScreenBrightnessSetting()276     public float getScreenBrightnessSetting() {
277         float brightness = mBrightnessSetting.getBrightness();
278         synchronized (mLock) {
279             if (Float.isNaN(brightness)) {
280                 brightness = mScreenBrightnessDefault;
281             }
282             return BrightnessUtils.clampAbsoluteBrightness(brightness);
283         }
284     }
285 
286     /**
287      * Notifies the brightnessSetting to persist the supplied brightness value.
288      */
setBrightness(float brightnessValue)289     public void setBrightness(float brightnessValue) {
290         setBrightness(brightnessValue, DEFAULT_USER_SERIAL);
291     }
292 
293     /**
294      * Notifies the brightnessSetting to persist the supplied brightness value for a user.
295      */
setBrightness(float brightnessValue, int userSerial)296     public void setBrightness(float brightnessValue, int userSerial) {
297         // Update the setting, which will eventually call back into DPC to have us actually
298         // update the display with the new value.
299         mBrightnessSetting.setUserSerial(userSerial);
300         mBrightnessSetting.setBrightness(brightnessValue);
301         if (mDisplayId == Display.DEFAULT_DISPLAY && mPersistBrightnessNitsForDefaultDisplay) {
302             float nits = convertToNits(brightnessValue);
303             if (nits >= 0) {
304                 mBrightnessSetting.setBrightnessNitsForDefaultDisplay(nits);
305             }
306         }
307     }
308 
309     /**
310      * Sets the current screen brightness, and notifies the BrightnessSetting about the change.
311      */
updateScreenBrightnessSetting(float brightnessValue)312     public void updateScreenBrightnessSetting(float brightnessValue) {
313         synchronized (mLock) {
314             if (!BrightnessUtils.isValidBrightnessValue(brightnessValue)
315                     || brightnessValue == mCurrentScreenBrightness) {
316                 return;
317             }
318             setCurrentScreenBrightnessLocked(brightnessValue);
319         }
320         notifyCurrentScreenBrightness();
321         setBrightness(brightnessValue);
322     }
323 
324     /**
325      * Set the {@link AutomaticBrightnessController} which is needed to perform nit-to-float-scale
326      * conversion.
327      * @param automaticBrightnessController The ABC
328      */
setAutomaticBrightnessController( AutomaticBrightnessController automaticBrightnessController)329     public void setAutomaticBrightnessController(
330             AutomaticBrightnessController automaticBrightnessController) {
331         mAutomaticBrightnessController = automaticBrightnessController;
332         loadNitBasedBrightnessSetting();
333     }
334 
335     /**
336      * TODO(b/253226419): Remove once auto-brightness is a fully-functioning strategy.
337      */
getAutomaticBrightnessStrategy()338     public AutomaticBrightnessStrategy getAutomaticBrightnessStrategy() {
339         return mDisplayBrightnessStrategySelector.getAutomaticBrightnessStrategy();
340     }
341 
342     /**
343      * Convert a brightness float scale value to a nit value. Adjustments, such as RBC, are not
344      * applied. This is used when storing the brightness in nits for the default display and when
345      * passing the brightness value to follower displays.
346      *
347      * @param brightness The float scale value
348      * @return The nit value or -1f if no conversion is possible.
349      */
convertToNits(float brightness)350     public float convertToNits(float brightness) {
351         if (mAutomaticBrightnessController == null) {
352             return -1f;
353         }
354         return mAutomaticBrightnessController.convertToNits(brightness);
355     }
356 
357     /**
358      * Convert a brightness float scale value to a nit value. Adjustments, such as RBC are applied.
359      * This is used when sending the brightness value to
360      * {@link com.android.server.display.BrightnessTracker}.
361      *
362      * @param brightness The float scale value
363      * @return The nit value or -1f if no conversion is possible.
364      */
convertToAdjustedNits(float brightness)365     public float convertToAdjustedNits(float brightness) {
366         if (mAutomaticBrightnessController == null) {
367             return -1f;
368         }
369         return mAutomaticBrightnessController.convertToAdjustedNits(brightness);
370     }
371 
372     /**
373      * Convert a brightness nit value to a float scale value. It is assumed that the nit value
374      * provided does not have adjustments, such as RBC, applied.
375      *
376      * @param nits The nit value
377      * @return The float scale value or {@link PowerManager.BRIGHTNESS_INVALID_FLOAT} if no
378      * conversion is possible.
379      */
convertToFloatScale(float nits)380     public float convertToFloatScale(float nits) {
381         if (mAutomaticBrightnessController == null) {
382             return PowerManager.BRIGHTNESS_INVALID_FLOAT;
383         }
384         return mAutomaticBrightnessController.convertToFloatScale(nits);
385     }
386 
387     /**
388      * Stops the associated listeners when the display is stopped. Invoked when the {@link
389      * #mDisplayId} is being removed.
390      */
stop()391     public void stop() {
392         if (mBrightnessSetting != null) {
393             mBrightnessSetting.unregisterListener(mBrightnessSettingListener);
394         }
395     }
396 
397     /**
398      * Used to dump the state.
399      *
400      * @param writer The PrintWriter used to dump the state.
401      */
dump(PrintWriter writer)402     public void dump(PrintWriter writer) {
403         writer.println();
404         writer.println("DisplayBrightnessController:");
405         writer.println("  mDisplayId=: " + mDisplayId);
406         writer.println("  mScreenBrightnessDefault=" + mScreenBrightnessDefault);
407         writer.println("  mPersistBrightnessNitsForDefaultDisplay="
408                 + mPersistBrightnessNitsForDefaultDisplay);
409         synchronized (mLock) {
410             writer.println("  mPendingScreenBrightness=" + mPendingScreenBrightness);
411             writer.println("  mCurrentScreenBrightness=" + mCurrentScreenBrightness);
412             writer.println("  mLastUserSetScreenBrightness="
413                     + mLastUserSetScreenBrightness);
414             if (mDisplayBrightnessStrategy != null) {
415                 writer.println("  Last selected DisplayBrightnessStrategy= "
416                         + mDisplayBrightnessStrategy.getName());
417             }
418             IndentingPrintWriter ipw = new IndentingPrintWriter(writer, " ");
419             mDisplayBrightnessStrategySelector.dump(ipw);
420         }
421     }
422 
423     @VisibleForTesting
424     static class Injector {
getDisplayBrightnessStrategySelector(Context context, int displayId)425         DisplayBrightnessStrategySelector getDisplayBrightnessStrategySelector(Context context,
426                 int displayId) {
427             return new DisplayBrightnessStrategySelector(context, /* injector= */ null, displayId);
428         }
429     }
430 
431     @VisibleForTesting
getBrightnessSettingListener()432     BrightnessSetting.BrightnessSettingListener getBrightnessSettingListener() {
433         return mBrightnessSettingListener;
434     }
435 
436     /**
437      * Returns the current selected DisplayBrightnessStrategy
438      */
439     @VisibleForTesting
getCurrentDisplayBrightnessStrategy()440     DisplayBrightnessStrategy getCurrentDisplayBrightnessStrategy() {
441         synchronized (mLock) {
442             return mDisplayBrightnessStrategy;
443         }
444     }
445 
446     /**
447      * TODO(b/253226419): Remove once auto-brightness is a fully-functioning strategy.
448      */
addAutomaticBrightnessState(DisplayBrightnessState state)449     private DisplayBrightnessState addAutomaticBrightnessState(DisplayBrightnessState state) {
450         AutomaticBrightnessStrategy autoStrat = getAutomaticBrightnessStrategy();
451 
452         DisplayBrightnessState.Builder builder = DisplayBrightnessState.Builder.from(state);
453         builder.setShouldUseAutoBrightness(
454                 autoStrat != null && autoStrat.shouldUseAutoBrightness());
455         return builder.build();
456     }
457 
458     @GuardedBy("mLock")
setTemporaryBrightnessLocked(float temporaryBrightness)459     private void setTemporaryBrightnessLocked(float temporaryBrightness) {
460         mDisplayBrightnessStrategySelector.getTemporaryDisplayBrightnessStrategy()
461                 .setTemporaryScreenBrightness(temporaryBrightness);
462     }
463 
464     @GuardedBy("mLock")
setCurrentScreenBrightnessLocked(float brightnessValue)465     private void setCurrentScreenBrightnessLocked(float brightnessValue) {
466         if (brightnessValue != mCurrentScreenBrightness) {
467             mCurrentScreenBrightness = brightnessValue;
468         }
469     }
470 
notifyCurrentScreenBrightness()471     private void notifyCurrentScreenBrightness() {
472         mBrightnessChangeExecutor.execute(mOnBrightnessChangeRunnable);
473     }
474 
475     /**
476      * Loads the brightness value. If this is the default display and the config says that we should
477      * persist the nit value, the nit value for the default display will be loaded.
478      */
loadNitBasedBrightnessSetting()479     private void loadNitBasedBrightnessSetting() {
480         float currentBrightnessSetting = Float.NaN;
481         if (mDisplayId == Display.DEFAULT_DISPLAY && mPersistBrightnessNitsForDefaultDisplay) {
482             float brightnessNitsForDefaultDisplay =
483                     mBrightnessSetting.getBrightnessNitsForDefaultDisplay();
484             if (brightnessNitsForDefaultDisplay >= 0) {
485                 float brightnessForDefaultDisplay = convertToFloatScale(
486                         brightnessNitsForDefaultDisplay);
487                 if (BrightnessUtils.isValidBrightnessValue(brightnessForDefaultDisplay)) {
488                     mBrightnessSetting.setBrightness(brightnessForDefaultDisplay);
489                     currentBrightnessSetting = brightnessForDefaultDisplay;
490                 }
491             }
492         }
493 
494         if (Float.isNaN(currentBrightnessSetting)) {
495             currentBrightnessSetting = getScreenBrightnessSetting();
496         }
497 
498         synchronized (mLock) {
499             mCurrentScreenBrightness = currentBrightnessSetting;
500         }
501     }
502 }
503