• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 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.systemui.doze;
18 
19 import static android.os.PowerManager.GO_TO_SLEEP_REASON_TIMEOUT;
20 
21 import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_GOING_TO_SLEEP;
22 
23 import android.annotation.Nullable;
24 import android.annotation.SuppressLint;
25 import android.content.BroadcastReceiver;
26 import android.content.Context;
27 import android.content.Intent;
28 import android.hardware.Sensor;
29 import android.hardware.SensorEvent;
30 import android.hardware.SensorEventListener;
31 import android.hardware.SensorManager;
32 import android.hardware.display.DisplayManager;
33 import android.os.Handler;
34 import android.os.PowerManager;
35 import android.os.SystemProperties;
36 import android.os.Trace;
37 import android.os.UserHandle;
38 import android.provider.Settings;
39 import android.util.IndentingPrintWriter;
40 import android.view.Display;
41 
42 import com.android.internal.R;
43 import com.android.systemui.dagger.qualifiers.Main;
44 import com.android.systemui.doze.dagger.BrightnessSensor;
45 import com.android.systemui.doze.dagger.DozeScope;
46 import com.android.systemui.doze.dagger.WrappedService;
47 import com.android.systemui.keyguard.WakefulnessLifecycle;
48 import com.android.systemui.statusbar.phone.DozeParameters;
49 import com.android.systemui.statusbar.policy.DevicePostureController;
50 import com.android.systemui.util.sensors.AsyncSensorManager;
51 import com.android.systemui.util.settings.SystemSettings;
52 
53 import java.io.PrintWriter;
54 import java.util.Arrays;
55 import java.util.Objects;
56 import java.util.Optional;
57 
58 import javax.inject.Inject;
59 
60 /**
61  * Controls the screen brightness when dozing.
62  */
63 @DozeScope
64 public class DozeScreenBrightness extends BroadcastReceiver implements DozeMachine.Part,
65         SensorEventListener {
66     private static final boolean DEBUG_AOD_BRIGHTNESS = SystemProperties
67             .getBoolean("debug.aod_brightness", false);
68     protected static final String ACTION_AOD_BRIGHTNESS =
69             "com.android.systemui.doze.AOD_BRIGHTNESS";
70     protected static final String BRIGHTNESS_BUCKET = "brightness_bucket";
71 
72     /**
73      * Just before the screen times out from user inactivity, DisplayPowerController dims the screen
74      * brightness to the lower of {@link #mScreenBrightnessDim}, or the current brightness minus
75      * this amount.
76      */
77     private final float mScreenBrightnessMinimumDimAmountFloat;
78     private final Context mContext;
79     private final DozeMachine.Service mDozeService;
80     private final DozeHost mDozeHost;
81     private final Handler mHandler;
82     private final SensorManager mSensorManager;
83     private final DisplayManager mDisplayManager;
84     private final Optional<Sensor>[] mLightSensorOptional; // light sensors to use per posture
85     private final WakefulnessLifecycle mWakefulnessLifecycle;
86     private final DozeParameters mDozeParameters;
87     private final DevicePostureController mDevicePostureController;
88     private final DozeLog mDozeLog;
89     private final SystemSettings mSystemSettings;
90     private final int[] mSensorToBrightness;
91     @Nullable
92     private final float[] mSensorToBrightnessFloat;
93     private final int[] mSensorToScrimOpacity;
94     private final int mScreenBrightnessDim;
95     private final float mScreenBrightnessDimFloat;
96 
97     @DevicePostureController.DevicePostureInt
98     private int mDevicePosture;
99     private boolean mRegistered;
100     private int mDefaultDozeBrightness;
101     private float mDefaultDozeBrightnessFloat;
102     private boolean mPaused = false;
103     private boolean mScreenOff = false;
104     private int mLastSensorValue = -1;
105     private DozeMachine.State mState = DozeMachine.State.UNINITIALIZED;
106 
107     /**
108      * Debug value used for emulating various display brightness buckets:
109      *
110      * {@code am broadcast -p com.android.systemui -a com.android.systemui.doze.AOD_BRIGHTNESS
111      * --ei brightness_bucket 1}
112      */
113     private int mDebugBrightnessBucket = -1;
114 
115     @Inject
116     @SuppressLint("AndroidFrameworkRequiresPermission")
DozeScreenBrightness( Context context, @WrappedService DozeMachine.Service service, AsyncSensorManager sensorManager, @BrightnessSensor Optional<Sensor>[] lightSensorOptional, DozeHost host, @Main Handler handler, AlwaysOnDisplayPolicy alwaysOnDisplayPolicy, WakefulnessLifecycle wakefulnessLifecycle, DozeParameters dozeParameters, DevicePostureController devicePostureController, DozeLog dozeLog, SystemSettings systemSettings, DisplayManager displayManager)117     public DozeScreenBrightness(
118             Context context,
119             @WrappedService DozeMachine.Service service,
120             AsyncSensorManager sensorManager,
121             @BrightnessSensor Optional<Sensor>[] lightSensorOptional,
122             DozeHost host, @Main Handler handler,
123             AlwaysOnDisplayPolicy alwaysOnDisplayPolicy,
124             WakefulnessLifecycle wakefulnessLifecycle,
125             DozeParameters dozeParameters,
126             DevicePostureController devicePostureController,
127             DozeLog dozeLog,
128             SystemSettings systemSettings,
129             DisplayManager displayManager) {
130         mContext = context;
131         mDozeService = service;
132         mSensorManager = sensorManager;
133         mDisplayManager = displayManager;
134         mLightSensorOptional = lightSensorOptional;
135         mDevicePostureController = devicePostureController;
136         mDevicePosture = mDevicePostureController.getDevicePosture();
137         mWakefulnessLifecycle = wakefulnessLifecycle;
138         mDozeParameters = dozeParameters;
139         mDozeHost = host;
140         mHandler = handler;
141         mDozeLog = dozeLog;
142         mSystemSettings = systemSettings;
143 
144         mScreenBrightnessMinimumDimAmountFloat = context.getResources().getFloat(
145                 R.dimen.config_screenBrightnessMinimumDimAmountFloat);
146 
147         mDefaultDozeBrightness = alwaysOnDisplayPolicy.defaultDozeBrightness;
148         mDefaultDozeBrightnessFloat =
149                 mDisplayManager.getDefaultDozeBrightness(mContext.getDisplayId());
150         mScreenBrightnessDim = alwaysOnDisplayPolicy.dimBrightness;
151         mScreenBrightnessDimFloat = alwaysOnDisplayPolicy.dimBrightnessFloat;
152         mSensorToBrightness = alwaysOnDisplayPolicy.screenBrightnessArray;
153         mSensorToBrightnessFloat =
154                 mDisplayManager.getDozeBrightnessSensorValueToBrightness(mContext.getDisplayId());
155         mSensorToScrimOpacity = alwaysOnDisplayPolicy.dimmingScrimArray;
156 
157         mDevicePostureController.addCallback(mDevicePostureCallback);
158     }
159 
160     @Override
transitionTo(DozeMachine.State oldState, DozeMachine.State newState)161     public void transitionTo(DozeMachine.State oldState, DozeMachine.State newState) {
162         mState = newState;
163         switch (newState) {
164             case INITIALIZED:
165                 resetBrightnessToDefault();
166                 break;
167             case DOZE_AOD:
168             case DOZE_REQUEST_PULSE:
169             case DOZE_AOD_DOCKED:
170                 setLightSensorEnabled(true);
171                 break;
172             case DOZE:
173             case DOZE_SUSPEND_TRIGGERS:
174                 setLightSensorEnabled(false);
175                 resetBrightnessToDefault();
176                 break;
177             case DOZE_AOD_PAUSED:
178                 setLightSensorEnabled(false);
179                 break;
180             case FINISH:
181                 onDestroy();
182                 break;
183         }
184         if (newState != DozeMachine.State.FINISH) {
185             setScreenOff(newState == DozeMachine.State.DOZE);
186             setPaused(newState == DozeMachine.State.DOZE_AOD_PAUSED);
187         }
188     }
189 
onDestroy()190     private void onDestroy() {
191         setLightSensorEnabled(false);
192         mDevicePostureController.removeCallback(mDevicePostureCallback);
193     }
194 
195     @Override
onSensorChanged(SensorEvent event)196     public void onSensorChanged(SensorEvent event) {
197         if (Trace.isEnabled()) {
198             Trace.traceBegin(
199                     Trace.TRACE_TAG_APP, "DozeScreenBrightness.onSensorChanged" + event.values[0]);
200         }
201         try {
202             if (mRegistered) {
203                 mLastSensorValue = (int) event.values[0];
204                 updateBrightnessAndReady(false /* force */);
205             }
206         } finally {
207             Trace.endSection();
208         }
209     }
210 
updateBrightnessAndReady(boolean force)211     public void updateBrightnessAndReady(boolean force) {
212         if (force || mRegistered || mDebugBrightnessBucket != -1) {
213             int sensorValue = mDebugBrightnessBucket == -1
214                     ? mLastSensorValue : mDebugBrightnessBucket;
215             boolean brightnessReady;
216             if (shouldUseFloatBrightness()) {
217                 float brightness = computeBrightnessFloat(sensorValue);
218                 brightnessReady = brightness >= 0;
219                 if (brightnessReady) {
220                     mDozeService.setDozeScreenBrightnessFloat(
221                             clampToDimBrightnessForScreenOffFloat(
222                                     clampToUserSettingFloat(brightness)));
223                 }
224             } else {
225                 int brightness = computeBrightness(sensorValue);
226                 brightnessReady = brightness > 0;
227                 if (brightnessReady) {
228                     mDozeService.setDozeScreenBrightness(
229                             clampToDimBrightnessForScreenOff(clampToUserSetting(brightness)));
230                 }
231             }
232 
233             int scrimOpacity = -1;
234             if (!isLightSensorPresent()) {
235                 // No light sensor, scrims are always transparent.
236                 scrimOpacity = 0;
237             } else if (brightnessReady) {
238                 // Only unblank scrim once brightness is ready.
239                 scrimOpacity = computeScrimOpacity(sensorValue);
240             }
241             if (scrimOpacity >= 0) {
242                 mDozeHost.setAodDimmingScrim(scrimOpacity / 255f);
243             }
244         }
245     }
246 
lightSensorSupportsCurrentPosture()247     private boolean lightSensorSupportsCurrentPosture() {
248         return mLightSensorOptional != null
249                 && mDevicePosture < mLightSensorOptional.length;
250     }
251 
isLightSensorPresent()252     private boolean isLightSensorPresent() {
253         if (!lightSensorSupportsCurrentPosture()) {
254             return mLightSensorOptional != null && mLightSensorOptional[0].isPresent();
255         }
256 
257         return mLightSensorOptional[mDevicePosture].isPresent();
258     }
259 
getLightSensor()260     private Sensor getLightSensor() {
261         if (!lightSensorSupportsCurrentPosture()) {
262             return null;
263         }
264 
265         return mLightSensorOptional[mDevicePosture].get();
266     }
267 
computeScrimOpacity(int sensorValue)268     private int computeScrimOpacity(int sensorValue) {
269         if (sensorValue < 0 || sensorValue >= mSensorToScrimOpacity.length) {
270             return -1;
271         }
272         return mSensorToScrimOpacity[sensorValue];
273     }
274 
computeBrightness(int sensorValue)275     private int computeBrightness(int sensorValue) {
276         if (sensorValue < 0 || sensorValue >= mSensorToBrightness.length) {
277             return -1;
278         }
279         return mSensorToBrightness[sensorValue];
280     }
281 
computeBrightnessFloat(int sensorValue)282     private float computeBrightnessFloat(int sensorValue) {
283         if (sensorValue < 0 || sensorValue >= mSensorToBrightnessFloat.length) {
284             return -1;
285         }
286         return mSensorToBrightnessFloat[sensorValue];
287     }
288 
289     @Override
onAccuracyChanged(Sensor sensor, int accuracy)290     public void onAccuracyChanged(Sensor sensor, int accuracy) {
291     }
292 
resetBrightnessToDefault()293     private void resetBrightnessToDefault() {
294         if (shouldUseFloatBrightness()) {
295             mDozeService.setDozeScreenBrightnessFloat(
296                     clampToDimBrightnessForScreenOffFloat(
297                             clampToUserSettingOrAutoBrightnessFloat(mDefaultDozeBrightnessFloat)));
298         } else {
299             mDozeService.setDozeScreenBrightness(
300                     clampToDimBrightnessForScreenOff(
301                             clampToUserSettingOrAutoBrightness(mDefaultDozeBrightness)));
302         }
303         mDozeHost.setAodDimmingScrim(0f);
304     }
305 
clampToUserSetting(int brightness)306     private int clampToUserSetting(int brightness) {
307         int screenBrightnessModeSetting = mSystemSettings.getIntForUser(
308                 Settings.System.SCREEN_BRIGHTNESS_MODE,
309                 Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL, UserHandle.USER_CURRENT);
310         if (screenBrightnessModeSetting == Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC) {
311             return brightness;
312         }
313 
314         return Math.min(brightness, getScreenBrightness());
315     }
316 
317     @SuppressLint("AndroidFrameworkRequiresPermission")
clampToUserSettingFloat(float brightness)318     private float clampToUserSettingFloat(float brightness) {
319         int screenBrightnessModeSetting = mSystemSettings.getIntForUser(
320                 Settings.System.SCREEN_BRIGHTNESS_MODE,
321                 Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL, UserHandle.USER_CURRENT);
322         if (screenBrightnessModeSetting == Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC) {
323             return brightness;
324         }
325 
326         return Math.min(brightness, getScreenBrightnessFloat());
327     }
328 
clampToUserSettingOrAutoBrightness(int brightness)329     private int clampToUserSettingOrAutoBrightness(int brightness) {
330         return Math.min(brightness, getScreenBrightness());
331     }
332 
clampToUserSettingOrAutoBrightnessFloat(float brightness)333     private float clampToUserSettingOrAutoBrightnessFloat(float brightness) {
334         return Math.min(brightness, getScreenBrightnessFloat());
335     }
336 
337     /**
338      * Gets the current screen brightness that may have been set by manually by the user
339      * or by autobrightness.
340      */
getScreenBrightness()341     private int getScreenBrightness() {
342         return mSystemSettings.getIntForUser(
343                 Settings.System.SCREEN_BRIGHTNESS, Integer.MAX_VALUE,
344                 UserHandle.USER_CURRENT);
345     }
346 
347     /**
348      * Gets the current screen brightness that may have been set by manually by the user
349      * or by autobrightness.
350      */
getScreenBrightnessFloat()351     private float getScreenBrightnessFloat() {
352         return mDisplayManager.getBrightness(Display.DEFAULT_DISPLAY);
353     }
354 
355     /**
356      * Clamp the brightness to the dim brightness value used by PowerManagerService just before the
357      * device times out and goes to sleep, if we are sleeping from a timeout. This ensures that we
358      * don't raise the brightness back to the user setting before or during the screen off
359      * animation.
360      */
clampToDimBrightnessForScreenOff(int brightness)361     private int clampToDimBrightnessForScreenOff(int brightness) {
362         final boolean screenTurningOff =
363                 (mDozeParameters.shouldClampToDimBrightness()
364                         || mWakefulnessLifecycle.getWakefulness() == WAKEFULNESS_GOING_TO_SLEEP)
365                 && mState == DozeMachine.State.INITIALIZED;
366         if (screenTurningOff
367                 && mWakefulnessLifecycle.getLastSleepReason() == GO_TO_SLEEP_REASON_TIMEOUT) {
368             return Math.max(
369                     PowerManager.BRIGHTNESS_OFF,
370                     // Use the lower of either the dim brightness, or the current brightness reduced
371                     // by the minimum dim amount. This is the same logic used in
372                     // DisplayPowerController#updatePowerState to apply a minimum dim amount.
373                     Math.min(
374                             brightness - (int) Math.floor(
375                                     mScreenBrightnessMinimumDimAmountFloat * 255),
376                             mScreenBrightnessDim));
377         } else {
378             return brightness;
379         }
380     }
381 
382     /**
383      * Clamp the brightness to the dim brightness value used by PowerManagerService just before the
384      * device times out and goes to sleep, if we are sleeping from a timeout. This ensures that we
385      * don't raise the brightness back to the user setting before or during the screen off
386      * animation.
387      */
clampToDimBrightnessForScreenOffFloat(float brightness)388     private float clampToDimBrightnessForScreenOffFloat(float brightness) {
389         final boolean screenTurningOff =
390                 (mDozeParameters.shouldClampToDimBrightness()
391                         || mWakefulnessLifecycle.getWakefulness() == WAKEFULNESS_GOING_TO_SLEEP)
392                 && mState == DozeMachine.State.INITIALIZED;
393         if (screenTurningOff
394                 && mWakefulnessLifecycle.getLastSleepReason() == GO_TO_SLEEP_REASON_TIMEOUT) {
395             return Math.max(
396                     PowerManager.BRIGHTNESS_MIN,
397                     // Use the lower of either the dim brightness, or the current brightness reduced
398                     // by the minimum dim amount. This is the same logic used in
399                     // DisplayPowerController#updatePowerState to apply a minimum dim amount.
400                     Math.min(brightness - mScreenBrightnessMinimumDimAmountFloat,
401                             mScreenBrightnessDimFloat));
402         } else {
403             return brightness;
404         }
405     }
406 
setLightSensorEnabled(boolean enabled)407     private void setLightSensorEnabled(boolean enabled) {
408         if (enabled && !mRegistered && isLightSensorPresent()) {
409             // Wait until we get an event from the sensor until indicating ready.
410             mRegistered = mSensorManager.registerListener(this, getLightSensor(),
411                     SensorManager.SENSOR_DELAY_NORMAL, mHandler);
412             mLastSensorValue = -1;
413         } else if (!enabled && mRegistered) {
414             mSensorManager.unregisterListener(this);
415             mRegistered = false;
416             mLastSensorValue = -1;
417             // Sensor is not enabled, hence we use the default brightness and are always ready.
418         }
419     }
420 
setPaused(boolean paused)421     private void setPaused(boolean paused) {
422         if (mPaused != paused) {
423             mPaused = paused;
424             updateBrightnessAndReady(false /* force */);
425         }
426     }
427 
setScreenOff(boolean screenOff)428     private void setScreenOff(boolean screenOff) {
429         if (mScreenOff != screenOff) {
430             mScreenOff = screenOff;
431             updateBrightnessAndReady(true /* force */);
432         }
433     }
434 
435     @Override
onReceive(Context context, Intent intent)436     public void onReceive(Context context, Intent intent) {
437         mDebugBrightnessBucket = intent.getIntExtra(BRIGHTNESS_BUCKET, -1);
438         updateBrightnessAndReady(false /* force */);
439     }
440 
441     /** Dump current state */
dump(PrintWriter pw)442     public void dump(PrintWriter pw) {
443         pw.println("DozeScreenBrightness:");
444         IndentingPrintWriter idpw = new IndentingPrintWriter(pw);
445         idpw.increaseIndent();
446         idpw.println("registered=" + mRegistered);
447         idpw.println("posture=" + DevicePostureController.devicePostureToString(mDevicePosture));
448         idpw.println("sensorToBrightness=" + Arrays.toString(mSensorToBrightness));
449         idpw.println("sensorToBrightnessFloat=" + Arrays.toString(mSensorToBrightnessFloat));
450         idpw.println("sensorToScrimOpacity=" + Arrays.toString(mSensorToScrimOpacity));
451         idpw.println("screenBrightnessDim=" + mScreenBrightnessDim);
452         idpw.println("screenBrightnessDimFloat=" + mScreenBrightnessDimFloat);
453         idpw.println("mDefaultDozeBrightness=" + mDefaultDozeBrightness);
454         idpw.println("mDefaultDozeBrightnessFloat=" + mDefaultDozeBrightnessFloat);
455         idpw.println("mLastSensorValue=" + mLastSensorValue);
456         idpw.println("shouldUseFloatBrightness()=" + shouldUseFloatBrightness());
457     }
458 
shouldUseFloatBrightness()459     private boolean shouldUseFloatBrightness() {
460         return com.android.server.display.feature.flags.Flags.dozeBrightnessFloat()
461                 && mSensorToBrightnessFloat != null;
462     }
463 
464     private final DevicePostureController.Callback mDevicePostureCallback =
465             new DevicePostureController.Callback() {
466         @Override
467         public void onPostureChanged(int posture) {
468             if (mDevicePosture == posture
469                     || mLightSensorOptional.length < 2
470                     || posture >= mLightSensorOptional.length) {
471                 return;
472             }
473             Sensor oldSensor = mLightSensorOptional[mDevicePosture].orElse(null);
474             Sensor newSensor = mLightSensorOptional[posture].orElse(null);
475             if (Objects.equals(oldSensor, newSensor)) {
476                 mDevicePosture = posture;
477                 // uses the same sensor for the new posture
478                 return;
479             }
480 
481             // cancel the previous sensor:
482             if (mRegistered) {
483                 setLightSensorEnabled(false);
484                 mDevicePosture = posture;
485                 setLightSensorEnabled(true);
486             } else {
487                 mDevicePosture = posture;
488             }
489             mDozeLog.tracePostureChanged(mDevicePosture, "DozeScreenBrightness swap "
490                     + "{" + oldSensor + "} => {" + newSensor + "}, mRegistered=" + mRegistered);
491         }
492     };
493 }
494