1 /*
2  * Copyright (C) 2024 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.strategy;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.hardware.Sensor;
22 import android.hardware.SensorManager;
23 import android.os.Handler;
24 import android.os.SystemClock;
25 import android.util.IndentingPrintWriter;
26 import android.view.Display;
27 
28 import com.android.internal.annotations.VisibleForTesting;
29 import com.android.server.display.BrightnessMappingStrategy;
30 import com.android.server.display.DisplayBrightnessState;
31 import com.android.server.display.DisplayDeviceConfig;
32 import com.android.server.display.ScreenOffBrightnessSensorController;
33 import com.android.server.display.brightness.BrightnessReason;
34 import com.android.server.display.brightness.BrightnessUtils;
35 import com.android.server.display.brightness.StrategyExecutionRequest;
36 import com.android.server.display.brightness.StrategySelectionNotifyRequest;
37 import com.android.server.display.layout.Layout;
38 import com.android.server.display.utils.SensorUtils;
39 
40 import java.io.PrintWriter;
41 
42 /**
43  * This strategy is used when the screen has just turned ON, with auto-brightness ON but there is
44  * no valid lux values available yet. In such a case, if configured, we set the brightness state
45  * from this
46  */
47 public final class AutoBrightnessFallbackStrategy implements DisplayBrightnessStrategy {
48 
49     @Nullable
50     private ScreenOffBrightnessSensorController mScreenOffBrightnessSensorController;
51     @VisibleForTesting
52     @Nullable
53     Sensor mScreenOffBrightnessSensor;
54 
55     // Indicates if the associated LogicalDisplay is enabled or not.
56     private boolean mIsEnabled;
57 
58     // Represents if the associated display is a lead display or not. If not, the variable
59     // represents the lead display ID
60     private int mLeadDisplayId;
61 
62     @NonNull
63     private final Injector mInjector;
64 
AutoBrightnessFallbackStrategy(Injector injector)65     public AutoBrightnessFallbackStrategy(Injector injector) {
66         mInjector = (injector == null) ? new RealInjector() : injector;
67     }
68 
69     @Override
updateBrightness( StrategyExecutionRequest strategyExecutionRequest)70     public DisplayBrightnessState updateBrightness(
71             StrategyExecutionRequest strategyExecutionRequest) {
72         assert mScreenOffBrightnessSensorController != null;
73         float brightness = mScreenOffBrightnessSensorController.getAutomaticScreenBrightness();
74         BrightnessReason brightnessReason = new BrightnessReason();
75         brightnessReason.setReason(BrightnessReason.REASON_SCREEN_OFF_BRIGHTNESS_SENSOR);
76         return new DisplayBrightnessState.Builder()
77                 .setBrightness(brightness)
78                 .setSdrBrightness(brightness)
79                 .setBrightnessReason(brightnessReason)
80                 .setDisplayBrightnessStrategyName(getName())
81                 .setShouldUpdateScreenBrightnessSetting(brightness
82                         != strategyExecutionRequest.getCurrentScreenBrightness())
83                 .build();
84     }
85 
86     @NonNull
87     @Override
getName()88     public String getName() {
89         return "AutoBrightnessFallbackStrategy";
90     }
91 
92     @Override
getReason()93     public int getReason() {
94         return BrightnessReason.REASON_SCREEN_OFF_BRIGHTNESS_SENSOR;
95     }
96 
97     @Override
dump(PrintWriter writer)98     public void dump(PrintWriter writer) {
99         writer.println("AutoBrightnessFallbackStrategy:");
100         writer.println("  mLeadDisplayId=" + mLeadDisplayId);
101         writer.println("  mIsEnabled=" + mIsEnabled);
102         if (mScreenOffBrightnessSensorController != null) {
103             IndentingPrintWriter ipw = new IndentingPrintWriter(writer, " ");
104             mScreenOffBrightnessSensorController.dump(ipw);
105         }
106     }
107 
108     @Override
strategySelectionPostProcessor( StrategySelectionNotifyRequest strategySelectionNotifyRequest)109     public void strategySelectionPostProcessor(
110             StrategySelectionNotifyRequest strategySelectionNotifyRequest) {
111         if (mScreenOffBrightnessSensorController != null) {
112             int targetDisplayState = strategySelectionNotifyRequest.getTargetDisplayState();
113             mScreenOffBrightnessSensorController.setLightSensorEnabled(
114                     strategySelectionNotifyRequest.isAutoBrightnessEnabled() && mIsEnabled
115                             && (targetDisplayState == Display.STATE_OFF
116                             || (targetDisplayState == Display.STATE_DOZE
117                             && !strategySelectionNotifyRequest
118                             .isAllowAutoBrightnessWhileDozingConfig()))
119                             && mLeadDisplayId == Layout.NO_LEAD_DISPLAY);
120         }
121     }
122 
123     /**
124      * Gets the associated ScreenOffBrightnessSensorController, controlling the brightness when
125      * auto-brightness is enabled, but the lux is not valid yet.
126      */
getScreenOffBrightnessSensorController()127     public ScreenOffBrightnessSensorController getScreenOffBrightnessSensorController() {
128         return mScreenOffBrightnessSensorController;
129     }
130 
131     /**
132      * Sets up the auto brightness fallback sensor
133      */
setupAutoBrightnessFallbackSensor(SensorManager sensorManager, DisplayDeviceConfig displayDeviceConfig, Handler handler, BrightnessMappingStrategy brightnessMappingStrategy, boolean isEnabled, int leadDisplayId)134     public void setupAutoBrightnessFallbackSensor(SensorManager sensorManager,
135             DisplayDeviceConfig displayDeviceConfig, Handler handler,
136             BrightnessMappingStrategy brightnessMappingStrategy, boolean isEnabled,
137             int leadDisplayId) {
138         mIsEnabled = isEnabled;
139         mLeadDisplayId = leadDisplayId;
140         if (mScreenOffBrightnessSensorController != null) {
141             mScreenOffBrightnessSensorController.stop();
142             mScreenOffBrightnessSensorController = null;
143         }
144         loadScreenOffBrightnessSensor(sensorManager, displayDeviceConfig);
145         int[] sensorValueToLux = displayDeviceConfig.getScreenOffBrightnessSensorValueToLux();
146         if (mScreenOffBrightnessSensor != null && sensorValueToLux != null) {
147             mScreenOffBrightnessSensorController =
148                     mInjector.getScreenOffBrightnessSensorController(
149                             sensorManager,
150                             mScreenOffBrightnessSensor,
151                             handler,
152                             SystemClock::uptimeMillis,
153                             sensorValueToLux,
154                             brightnessMappingStrategy);
155         }
156     }
157 
158     /**
159      * Stops the associated ScreenOffBrightnessSensorController responsible for managing the
160      * brightness when this strategy is selected
161      */
stop()162     public void stop() {
163         if (mScreenOffBrightnessSensorController != null) {
164             mScreenOffBrightnessSensorController.stop();
165         }
166     }
167 
168     /**
169      * Checks if the strategy is valid, based on its internal state. Note that there can still be
170      * external factors like auto-brightness not being enabled because of which this strategy is not
171      * selected
172      */
isValid()173     public boolean isValid() {
174         return mScreenOffBrightnessSensorController != null
175                 && BrightnessUtils.isValidBrightnessValue(
176                 mScreenOffBrightnessSensorController.getAutomaticScreenBrightness());
177     }
178 
loadScreenOffBrightnessSensor(SensorManager sensorManager, DisplayDeviceConfig displayDeviceConfig)179     private void loadScreenOffBrightnessSensor(SensorManager sensorManager,
180             DisplayDeviceConfig displayDeviceConfig) {
181         mScreenOffBrightnessSensor = mInjector.getScreenOffBrightnessSensor(sensorManager,
182                 displayDeviceConfig);
183     }
184 
185 
186     @VisibleForTesting
187     interface Injector {
getScreenOffBrightnessSensor(SensorManager sensorManager, DisplayDeviceConfig displayDeviceConfig)188         Sensor getScreenOffBrightnessSensor(SensorManager sensorManager,
189                 DisplayDeviceConfig displayDeviceConfig);
190 
getScreenOffBrightnessSensorController( SensorManager sensorManager, Sensor lightSensor, Handler handler, ScreenOffBrightnessSensorController.Clock clock, int[] sensorValueToLux, BrightnessMappingStrategy brightnessMapper)191         ScreenOffBrightnessSensorController getScreenOffBrightnessSensorController(
192                 SensorManager sensorManager,
193                 Sensor lightSensor,
194                 Handler handler,
195                 ScreenOffBrightnessSensorController.Clock clock,
196                 int[] sensorValueToLux,
197                 BrightnessMappingStrategy brightnessMapper);
198     }
199 
200     static class RealInjector implements Injector {
201         @Override
getScreenOffBrightnessSensor(SensorManager sensorManager, DisplayDeviceConfig displayDeviceConfig)202         public Sensor getScreenOffBrightnessSensor(SensorManager sensorManager,
203                 DisplayDeviceConfig displayDeviceConfig) {
204             return SensorUtils.findSensor(sensorManager,
205                     displayDeviceConfig.getScreenOffBrightnessSensor(), SensorUtils.NO_FALLBACK);
206         }
207 
208         @Override
getScreenOffBrightnessSensorController( SensorManager sensorManager, Sensor lightSensor, Handler handler, ScreenOffBrightnessSensorController.Clock clock, int[] sensorValueToLux, BrightnessMappingStrategy brightnessMapper)209         public ScreenOffBrightnessSensorController getScreenOffBrightnessSensorController(
210                 SensorManager sensorManager, Sensor lightSensor, Handler handler,
211                 ScreenOffBrightnessSensorController.Clock clock, int[] sensorValueToLux,
212                 BrightnessMappingStrategy brightnessMapper) {
213             return new ScreenOffBrightnessSensorController(
214                     sensorManager, lightSensor, handler, clock, sensorValueToLux, brightnessMapper);
215         }
216     }
217 }
218