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