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 android.content.BroadcastReceiver;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.content.IntentFilter;
23 import android.hardware.Sensor;
24 import android.hardware.SensorEvent;
25 import android.hardware.SensorEventListener;
26 import android.hardware.SensorManager;
27 import android.os.Handler;
28 import android.os.SystemProperties;
29 import android.os.Trace;
30 import android.os.UserHandle;
31 import android.provider.Settings;
32 
33 import com.android.internal.annotations.VisibleForTesting;
34 
35 /**
36  * Controls the screen brightness when dozing.
37  */
38 public class DozeScreenBrightness extends BroadcastReceiver implements DozeMachine.Part,
39         SensorEventListener {
40     private static final boolean DEBUG_AOD_BRIGHTNESS = SystemProperties
41             .getBoolean("debug.aod_brightness", false);
42     protected static final String ACTION_AOD_BRIGHTNESS =
43             "com.android.systemui.doze.AOD_BRIGHTNESS";
44     protected static final String BRIGHTNESS_BUCKET = "brightness_bucket";
45 
46     private final Context mContext;
47     private final DozeMachine.Service mDozeService;
48     private final DozeHost mDozeHost;
49     private final Handler mHandler;
50     private final SensorManager mSensorManager;
51     private final Sensor mLightSensor;
52     private final int[] mSensorToBrightness;
53     private final int[] mSensorToScrimOpacity;
54     private final boolean mDebuggable;
55 
56     private boolean mRegistered;
57     private int mDefaultDozeBrightness;
58     private boolean mPaused = false;
59     private boolean mScreenOff = false;
60     private int mLastSensorValue = -1;
61 
62     /**
63      * Debug value used for emulating various display brightness buckets:
64      *
65      * {@code am broadcast -p com.android.systemui -a com.android.systemui.doze.AOD_BRIGHTNESS
66      * --ei brightness_bucket 1}
67      */
68     private int mDebugBrightnessBucket = -1;
69 
70     @VisibleForTesting
DozeScreenBrightness(Context context, DozeMachine.Service service, SensorManager sensorManager, Sensor lightSensor, DozeHost host, Handler handler, int defaultDozeBrightness, int[] sensorToBrightness, int[] sensorToScrimOpacity, boolean debuggable)71     public DozeScreenBrightness(Context context, DozeMachine.Service service,
72             SensorManager sensorManager, Sensor lightSensor, DozeHost host,
73             Handler handler, int defaultDozeBrightness, int[] sensorToBrightness,
74             int[] sensorToScrimOpacity, boolean debuggable) {
75         mContext = context;
76         mDozeService = service;
77         mSensorManager = sensorManager;
78         mLightSensor = lightSensor;
79         mDozeHost = host;
80         mHandler = handler;
81         mDebuggable = debuggable;
82 
83         mDefaultDozeBrightness = defaultDozeBrightness;
84         mSensorToBrightness = sensorToBrightness;
85         mSensorToScrimOpacity = sensorToScrimOpacity;
86 
87         if (mDebuggable) {
88             IntentFilter filter = new IntentFilter();
89             filter.addAction(ACTION_AOD_BRIGHTNESS);
90             mContext.registerReceiverAsUser(this, UserHandle.ALL, filter, null, handler);
91         }
92     }
93 
DozeScreenBrightness(Context context, DozeMachine.Service service, SensorManager sensorManager, Sensor lightSensor, DozeHost host, Handler handler, AlwaysOnDisplayPolicy policy)94     public DozeScreenBrightness(Context context, DozeMachine.Service service,
95             SensorManager sensorManager, Sensor lightSensor, DozeHost host,
96             Handler handler, AlwaysOnDisplayPolicy policy) {
97         this(context, service, sensorManager, lightSensor, host, handler,
98                 context.getResources().getInteger(
99                         com.android.internal.R.integer.config_screenBrightnessDoze),
100                 policy.screenBrightnessArray, policy.dimmingScrimArray, DEBUG_AOD_BRIGHTNESS);
101     }
102 
103     @Override
transitionTo(DozeMachine.State oldState, DozeMachine.State newState)104     public void transitionTo(DozeMachine.State oldState, DozeMachine.State newState) {
105         switch (newState) {
106             case INITIALIZED:
107                 resetBrightnessToDefault();
108                 break;
109             case DOZE_AOD:
110             case DOZE_REQUEST_PULSE:
111                 setLightSensorEnabled(true);
112                 break;
113             case DOZE:
114                 setLightSensorEnabled(false);
115                 resetBrightnessToDefault();
116                 break;
117             case FINISH:
118                 onDestroy();
119                 break;
120         }
121         if (newState != DozeMachine.State.FINISH) {
122             setScreenOff(newState == DozeMachine.State.DOZE);
123             setPaused(newState == DozeMachine.State.DOZE_AOD_PAUSED);
124         }
125     }
126 
onDestroy()127     private void onDestroy() {
128         setLightSensorEnabled(false);
129         if (mDebuggable) {
130             mContext.unregisterReceiver(this);
131         }
132     }
133 
134     @Override
onSensorChanged(SensorEvent event)135     public void onSensorChanged(SensorEvent event) {
136         Trace.beginSection("DozeScreenBrightness.onSensorChanged" + event.values[0]);
137         try {
138             if (mRegistered) {
139                 mLastSensorValue = (int) event.values[0];
140                 updateBrightnessAndReady(false /* force */);
141             }
142         } finally {
143             Trace.endSection();
144         }
145     }
146 
updateBrightnessAndReady(boolean force)147     private void updateBrightnessAndReady(boolean force) {
148         if (force || mRegistered || mDebugBrightnessBucket != -1) {
149             int sensorValue = mDebugBrightnessBucket == -1
150                     ? mLastSensorValue : mDebugBrightnessBucket;
151             int brightness = computeBrightness(sensorValue);
152             boolean brightnessReady = brightness > 0;
153             if (brightnessReady) {
154                 mDozeService.setDozeScreenBrightness(clampToUserSetting(brightness));
155             }
156 
157             int scrimOpacity = -1;
158             if (mPaused || mScreenOff) {
159                 // If AOD is paused, force the screen black until the
160                 // sensor reports a new brightness. This ensures that when the screen comes on
161                 // again, it will only show after the brightness sensor has stabilized,
162                 // avoiding a potential flicker.
163                 scrimOpacity = 255;
164             } else if (brightnessReady) {
165                 // Only unblank scrim once brightness is ready.
166                 scrimOpacity = computeScrimOpacity(sensorValue);
167             }
168             if (scrimOpacity >= 0) {
169                 mDozeHost.setAodDimmingScrim(scrimOpacity / 255f);
170             }
171         }
172     }
173 
computeScrimOpacity(int sensorValue)174     private int computeScrimOpacity(int sensorValue) {
175         if (sensorValue < 0 || sensorValue >= mSensorToScrimOpacity.length) {
176             return -1;
177         }
178         return mSensorToScrimOpacity[sensorValue];
179     }
180 
computeBrightness(int sensorValue)181     private int computeBrightness(int sensorValue) {
182         if (sensorValue < 0 || sensorValue >= mSensorToBrightness.length) {
183             return -1;
184         }
185         return mSensorToBrightness[sensorValue];
186     }
187 
188     @Override
onAccuracyChanged(Sensor sensor, int accuracy)189     public void onAccuracyChanged(Sensor sensor, int accuracy) {
190     }
191 
resetBrightnessToDefault()192     private void resetBrightnessToDefault() {
193         mDozeService.setDozeScreenBrightness(clampToUserSetting(mDefaultDozeBrightness));
194         mDozeHost.setAodDimmingScrim(0f);
195     }
196 
clampToUserSetting(int brightness)197     private int clampToUserSetting(int brightness) {
198         int userSetting = Settings.System.getIntForUser(mContext.getContentResolver(),
199                 Settings.System.SCREEN_BRIGHTNESS, Integer.MAX_VALUE,
200                 UserHandle.USER_CURRENT);
201         return Math.min(brightness, userSetting);
202     }
203 
setLightSensorEnabled(boolean enabled)204     private void setLightSensorEnabled(boolean enabled) {
205         if (enabled && !mRegistered && mLightSensor != null) {
206             // Wait until we get an event from the sensor until indicating ready.
207             mRegistered = mSensorManager.registerListener(this, mLightSensor,
208                     SensorManager.SENSOR_DELAY_NORMAL, mHandler);
209             mLastSensorValue = -1;
210         } else if (!enabled && mRegistered) {
211             mSensorManager.unregisterListener(this);
212             mRegistered = false;
213             mLastSensorValue = -1;
214             // Sensor is not enabled, hence we use the default brightness and are always ready.
215         }
216     }
217 
setPaused(boolean paused)218     private void setPaused(boolean paused) {
219         if (mPaused != paused) {
220             mPaused = paused;
221             updateBrightnessAndReady(false /* force */);
222         }
223     }
224 
setScreenOff(boolean screenOff)225     private void setScreenOff(boolean screenOff) {
226         if (mScreenOff != screenOff) {
227             mScreenOff = screenOff;
228             updateBrightnessAndReady(true /* force */);
229         }
230     }
231 
232     @Override
onReceive(Context context, Intent intent)233     public void onReceive(Context context, Intent intent) {
234         mDebugBrightnessBucket = intent.getIntExtra(BRIGHTNESS_BUCKET, -1);
235         updateBrightnessAndReady(false /* force */);
236     }
237 }
238