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