1 /* 2 * Copyright (C) 2014 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.statusbar.phone; 18 19 import android.content.ContentResolver; 20 import android.content.Context; 21 import android.content.res.Configuration; 22 import android.content.res.Resources; 23 import android.database.ContentObserver; 24 import android.hardware.display.AmbientDisplayConfiguration; 25 import android.net.Uri; 26 import android.os.Handler; 27 import android.os.PowerManager; 28 import android.os.SystemProperties; 29 import android.os.UserHandle; 30 import android.provider.Settings; 31 import android.util.Log; 32 import android.util.MathUtils; 33 34 import androidx.annotation.NonNull; 35 import androidx.annotation.VisibleForTesting; 36 37 import com.android.keyguard.KeyguardUpdateMonitor; 38 import com.android.keyguard.KeyguardUpdateMonitorCallback; 39 import com.android.systemui.Dumpable; 40 import com.android.systemui.dagger.SysUISingleton; 41 import com.android.systemui.dagger.qualifiers.Background; 42 import com.android.systemui.dagger.qualifiers.Main; 43 import com.android.systemui.doze.AlwaysOnDisplayPolicy; 44 import com.android.systemui.doze.DozeScreenState; 45 import com.android.systemui.dump.DumpManager; 46 import com.android.systemui.keyguard.domain.interactor.DozeInteractor; 47 import com.android.systemui.plugins.statusbar.StatusBarStateController; 48 import com.android.systemui.res.R; 49 import com.android.systemui.settings.UserTracker; 50 import com.android.systemui.statusbar.policy.BatteryController; 51 import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback; 52 import com.android.systemui.statusbar.policy.ConfigurationController; 53 import com.android.systemui.statusbar.policy.DevicePostureController; 54 import com.android.systemui.tuner.TunerService; 55 import com.android.systemui.unfold.FoldAodAnimationController; 56 import com.android.systemui.unfold.SysUIUnfoldComponent; 57 58 import java.io.PrintWriter; 59 import java.util.Optional; 60 61 import javax.inject.Inject; 62 63 /** 64 * Retrieve doze information 65 */ 66 @SysUISingleton 67 public class DozeParameters implements 68 TunerService.Tunable, 69 com.android.systemui.plugins.statusbar.DozeParameters, 70 Dumpable, ConfigurationController.ConfigurationListener, 71 StatusBarStateController.StateListener, FoldAodAnimationController.FoldAodAnimationStatus { 72 private static final int MAX_DURATION = 60 * 1000; 73 public static final boolean FORCE_NO_BLANKING = 74 SystemProperties.getBoolean("debug.force_no_blanking", false); 75 public static final boolean FORCE_BLANKING = 76 SystemProperties.getBoolean("debug.force_blanking", false); 77 78 private final AmbientDisplayConfiguration mAmbientDisplayConfiguration; 79 private final PowerManager mPowerManager; 80 81 private final AlwaysOnDisplayPolicy mAlwaysOnPolicy; 82 private final Resources mResources; 83 private final BatteryController mBatteryController; 84 private final ScreenOffAnimationController mScreenOffAnimationController; 85 private final DozeInteractor mDozeInteractor; 86 private final FoldAodAnimationController mFoldAodAnimationController; 87 private final UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController; 88 private final UserTracker mUserTracker; 89 90 private boolean mDozeAlwaysOn; 91 private boolean mControlScreenOffAnimation; 92 private boolean mIsQuickPickupEnabled; 93 94 private boolean mKeyguardVisible; 95 @VisibleForTesting 96 final KeyguardUpdateMonitorCallback mKeyguardVisibilityCallback = 97 new KeyguardUpdateMonitorCallback() { 98 @Override 99 public void onKeyguardVisibilityChanged(boolean visible) { 100 mKeyguardVisible = visible; 101 updateControlScreenOff(); 102 } 103 104 @Override 105 public void onShadeExpandedChanged(boolean expanded) { 106 updateControlScreenOff(); 107 } 108 109 @Override 110 public void onUserSwitchComplete(int newUserId) { 111 updateQuickPickupEnabled(); 112 } 113 }; 114 115 @Inject DozeParameters( Context context, @Background Handler handler, @Main Resources resources, AmbientDisplayConfiguration ambientDisplayConfiguration, AlwaysOnDisplayPolicy alwaysOnDisplayPolicy, PowerManager powerManager, BatteryController batteryController, TunerService tunerService, DumpManager dumpManager, ScreenOffAnimationController screenOffAnimationController, Optional<SysUIUnfoldComponent> sysUiUnfoldComponent, UnlockedScreenOffAnimationController unlockedScreenOffAnimationController, KeyguardUpdateMonitor keyguardUpdateMonitor, ConfigurationController configurationController, StatusBarStateController statusBarStateController, UserTracker userTracker, DozeInteractor dozeInteractor)116 protected DozeParameters( 117 Context context, 118 @Background Handler handler, 119 @Main Resources resources, 120 AmbientDisplayConfiguration ambientDisplayConfiguration, 121 AlwaysOnDisplayPolicy alwaysOnDisplayPolicy, 122 PowerManager powerManager, 123 BatteryController batteryController, 124 TunerService tunerService, 125 DumpManager dumpManager, 126 ScreenOffAnimationController screenOffAnimationController, 127 Optional<SysUIUnfoldComponent> sysUiUnfoldComponent, 128 UnlockedScreenOffAnimationController unlockedScreenOffAnimationController, 129 KeyguardUpdateMonitor keyguardUpdateMonitor, 130 ConfigurationController configurationController, 131 StatusBarStateController statusBarStateController, 132 UserTracker userTracker, 133 DozeInteractor dozeInteractor) { 134 mResources = resources; 135 mAmbientDisplayConfiguration = ambientDisplayConfiguration; 136 mAlwaysOnPolicy = alwaysOnDisplayPolicy; 137 mBatteryController = batteryController; 138 dumpManager.registerDumpable("DozeParameters", this); 139 140 mControlScreenOffAnimation = !getDisplayNeedsBlanking(); 141 mPowerManager = powerManager; 142 mPowerManager.setDozeAfterScreenOff(!mControlScreenOffAnimation); 143 mScreenOffAnimationController = screenOffAnimationController; 144 mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController; 145 mUserTracker = userTracker; 146 mDozeInteractor = dozeInteractor; 147 148 keyguardUpdateMonitor.registerCallback(mKeyguardVisibilityCallback); 149 tunerService.addTunable( 150 this, 151 Settings.Secure.DOZE_ALWAYS_ON, 152 Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED); 153 configurationController.addCallback(this); 154 statusBarStateController.addCallback(this); 155 156 mFoldAodAnimationController = sysUiUnfoldComponent 157 .map(SysUIUnfoldComponent::getFoldAodAnimationController).orElse(null); 158 159 if (mFoldAodAnimationController != null) { 160 mFoldAodAnimationController.addCallback(this); 161 } 162 163 SettingsObserver quickPickupSettingsObserver = new SettingsObserver(context, handler); 164 quickPickupSettingsObserver.observe(); 165 166 batteryController.addCallback(new BatteryStateChangeCallback() { 167 @Override 168 public void onPowerSaveChanged(boolean isPowerSave) { 169 dispatchAlwaysOnEvent(); 170 } 171 }); 172 } 173 updateQuickPickupEnabled()174 private void updateQuickPickupEnabled() { 175 mIsQuickPickupEnabled = 176 mAmbientDisplayConfiguration.quickPickupSensorEnabled(mUserTracker.getUserId()); 177 } 178 getDisplayStateSupported()179 public boolean getDisplayStateSupported() { 180 return getBoolean("doze.display.supported", R.bool.doze_display_state_supported); 181 } 182 getDozeSuspendDisplayStateSupported()183 public boolean getDozeSuspendDisplayStateSupported() { 184 return mResources.getBoolean(R.bool.doze_suspend_display_state_supported); 185 } 186 getPulseDuration()187 public int getPulseDuration() { 188 return getPulseInDuration() + getPulseVisibleDuration() + getPulseOutDuration(); 189 } 190 getScreenBrightnessDoze()191 public float getScreenBrightnessDoze() { 192 return mResources.getInteger( 193 com.android.internal.R.integer.config_screenBrightnessDoze) / 255f; 194 } 195 getPulseInDuration()196 public int getPulseInDuration() { 197 return getInt("doze.pulse.duration.in", R.integer.doze_pulse_duration_in); 198 } 199 getPulseVisibleDuration()200 public int getPulseVisibleDuration() { 201 return getInt("doze.pulse.duration.visible", R.integer.doze_pulse_duration_visible); 202 } 203 getPulseOutDuration()204 public int getPulseOutDuration() { 205 return getInt("doze.pulse.duration.out", R.integer.doze_pulse_duration_out); 206 } 207 getPulseOnSigMotion()208 public boolean getPulseOnSigMotion() { 209 return getBoolean("doze.pulse.sigmotion", R.bool.doze_pulse_on_significant_motion); 210 } 211 getVibrateOnSigMotion()212 public boolean getVibrateOnSigMotion() { 213 return SystemProperties.getBoolean("doze.vibrate.sigmotion", false); 214 } 215 getVibrateOnPickup()216 public boolean getVibrateOnPickup() { 217 return SystemProperties.getBoolean("doze.vibrate.pickup", false); 218 } 219 getProxCheckBeforePulse()220 public boolean getProxCheckBeforePulse() { 221 return getBoolean("doze.pulse.proxcheck", R.bool.doze_proximity_check_before_pulse); 222 } 223 224 /** 225 * @return true if we should only register for sensors that use the proximity sensor when the 226 * display state is {@link android.view.Display.STATE_OFF}, 227 * {@link android.view.Display.STATE_DOZE} or {@link android.view.Display.STATE_DOZE_SUSPEND} 228 */ getSelectivelyRegisterSensorsUsingProx()229 public boolean getSelectivelyRegisterSensorsUsingProx() { 230 return getBoolean("doze.prox.selectively_register", 231 R.bool.doze_selectively_register_prox); 232 } 233 getPickupVibrationThreshold()234 public int getPickupVibrationThreshold() { 235 return getInt("doze.pickup.vibration.threshold", R.integer.doze_pickup_vibration_threshold); 236 } 237 getQuickPickupAodDuration()238 public int getQuickPickupAodDuration() { 239 return getInt("doze.gesture.quickpickup.duration", 240 R.integer.doze_quick_pickup_aod_duration); 241 } 242 243 /** 244 * For how long a wallpaper can be visible in AoD before it fades aways. 245 * @return duration in millis. 246 */ getWallpaperAodDuration()247 public long getWallpaperAodDuration() { 248 if (shouldControlScreenOff()) { 249 return DozeScreenState.ENTER_DOZE_HIDE_WALLPAPER_DELAY; 250 } 251 return mAlwaysOnPolicy.wallpaperVisibilityDuration; 252 } 253 254 /** 255 * How long it takes for the wallpaper fade away (Animation duration.) 256 * @return duration in millis. 257 */ getWallpaperFadeOutDuration()258 public long getWallpaperFadeOutDuration() { 259 return mAlwaysOnPolicy.wallpaperFadeOutDuration; 260 } 261 262 /** 263 * Checks if always on is available and enabled for the current user. 264 * @return {@code true} if enabled and available. 265 */ getAlwaysOn()266 public boolean getAlwaysOn() { 267 return mDozeAlwaysOn && !mBatteryController.isAodPowerSave(); 268 } 269 270 /** 271 * Whether the quick pickup gesture is supported and enabled for the device. 272 */ isQuickPickupEnabled()273 public boolean isQuickPickupEnabled() { 274 return mIsQuickPickupEnabled; 275 } 276 277 /** 278 * Some screens need to be completely black before changing the display power mode, 279 * unexpected behavior might happen if this parameter isn't respected. 280 * 281 * @return {@code true} if screen needs to be completely black before a power transition. 282 */ getDisplayNeedsBlanking()283 public boolean getDisplayNeedsBlanking() { 284 return FORCE_BLANKING || !FORCE_NO_BLANKING && mResources.getBoolean( 285 com.android.internal.R.bool.config_displayBlanksAfterDoze); 286 } 287 shouldControlScreenOff()288 public boolean shouldControlScreenOff() { 289 return mControlScreenOffAnimation; 290 } 291 setControlScreenOffAnimation(boolean controlScreenOffAnimation)292 public void setControlScreenOffAnimation(boolean controlScreenOffAnimation) { 293 if (mControlScreenOffAnimation == controlScreenOffAnimation) { 294 return; 295 } 296 mControlScreenOffAnimation = controlScreenOffAnimation; 297 mPowerManager.setDozeAfterScreenOff(!controlScreenOffAnimation); 298 } 299 updateControlScreenOff()300 public void updateControlScreenOff() { 301 if (!getDisplayNeedsBlanking()) { 302 final boolean controlScreenOff = 303 getAlwaysOn() && (mKeyguardVisible || shouldControlUnlockedScreenOff()); 304 setControlScreenOffAnimation(controlScreenOff); 305 } 306 } 307 308 /** 309 * Whether we're capable of controlling the screen off animation if we want to. This isn't 310 * possible if AOD isn't even enabled or if the display needs blanking. 311 */ canControlUnlockedScreenOff()312 public boolean canControlUnlockedScreenOff() { 313 return getAlwaysOn() && !getDisplayNeedsBlanking(); 314 } 315 316 /** 317 * Whether we want to control the screen off animation when the device is unlocked. If we do, 318 * we'll animate in AOD before turning off the screen, rather than simply fading to black and 319 * then abruptly showing AOD. 320 * 321 * There are currently several reasons we might not want to control the screen off even if we 322 * are able to, such as the shade being expanded, being in landscape, or having animations 323 * disabled for a11y. 324 */ shouldControlUnlockedScreenOff()325 public boolean shouldControlUnlockedScreenOff() { 326 return mUnlockedScreenOffAnimationController.shouldPlayUnlockedScreenOffAnimation(); 327 } 328 shouldDelayKeyguardShow()329 public boolean shouldDelayKeyguardShow() { 330 return mScreenOffAnimationController.shouldDelayKeyguardShow(); 331 } 332 shouldClampToDimBrightness()333 public boolean shouldClampToDimBrightness() { 334 return mScreenOffAnimationController.shouldClampDozeScreenBrightness(); 335 } 336 shouldShowLightRevealScrim()337 public boolean shouldShowLightRevealScrim() { 338 return mScreenOffAnimationController.shouldShowLightRevealScrim(); 339 } 340 shouldAnimateDozingChange()341 public boolean shouldAnimateDozingChange() { 342 return mScreenOffAnimationController.shouldAnimateDozingChange(); 343 } 344 345 /** 346 * When this method returns true then moving display state to power save mode will be 347 * delayed for a few seconds. This might be useful to play animations without reducing FPS. 348 */ shouldDelayDisplayDozeTransition()349 public boolean shouldDelayDisplayDozeTransition() { 350 return willAnimateFromLockScreenToAod() 351 || mScreenOffAnimationController.shouldDelayDisplayDozeTransition(); 352 } 353 willAnimateFromLockScreenToAod()354 private boolean willAnimateFromLockScreenToAod() { 355 return shouldControlScreenOff() && mKeyguardVisible; 356 } 357 getBoolean(String propName, int resId)358 private boolean getBoolean(String propName, int resId) { 359 return SystemProperties.getBoolean(propName, mResources.getBoolean(resId)); 360 } 361 getInt(String propName, int resId)362 private int getInt(String propName, int resId) { 363 int value = SystemProperties.getInt(propName, mResources.getInteger(resId)); 364 return MathUtils.constrain(value, 0, MAX_DURATION); 365 } 366 getPulseVisibleDurationExtended()367 public int getPulseVisibleDurationExtended() { 368 return 2 * getPulseVisibleDuration(); 369 } 370 doubleTapReportsTouchCoordinates()371 public boolean doubleTapReportsTouchCoordinates() { 372 return mResources.getBoolean(R.bool.doze_double_tap_reports_touch_coordinates); 373 } 374 375 /** 376 * Whether the single tap sensor uses the proximity sensor for this device posture. 377 */ singleTapUsesProx(@evicePostureController.DevicePostureInt int devicePosture)378 public boolean singleTapUsesProx(@DevicePostureController.DevicePostureInt int devicePosture) { 379 return getPostureSpecificBool( 380 mResources.getIntArray(R.array.doze_single_tap_uses_prox_posture_mapping), 381 singleTapUsesProx(), 382 devicePosture 383 ); 384 } 385 386 /** 387 * Whether the single tap sensor uses the proximity sensor. 388 */ singleTapUsesProx()389 private boolean singleTapUsesProx() { 390 return mResources.getBoolean(R.bool.doze_single_tap_uses_prox); 391 } 392 393 /** 394 * Whether the long press sensor uses the proximity sensor. 395 */ longPressUsesProx()396 public boolean longPressUsesProx() { 397 return mResources.getBoolean(R.bool.doze_long_press_uses_prox); 398 } 399 400 /** 401 * Gets the brightness string array per posture. Brightness names along with 402 * doze_brightness_sensor_type is used to determine the brightness sensor to use for 403 * the current posture. 404 */ brightnessNames()405 public String[] brightnessNames() { 406 return mResources.getStringArray(R.array.doze_brightness_sensor_name_posture_mapping); 407 } 408 409 @Override onTuningChanged(String key, String newValue)410 public void onTuningChanged(String key, String newValue) { 411 mDozeAlwaysOn = mAmbientDisplayConfiguration.alwaysOnEnabled(mUserTracker.getUserId()); 412 413 if (key.equals(Settings.Secure.DOZE_ALWAYS_ON)) { 414 updateControlScreenOff(); 415 } 416 417 dispatchAlwaysOnEvent(); 418 } 419 420 @Override onConfigChanged(Configuration newConfig)421 public void onConfigChanged(Configuration newConfig) { 422 updateControlScreenOff(); 423 } 424 425 @Override onStatePostChange()426 public void onStatePostChange() { 427 updateControlScreenOff(); 428 } 429 430 @Override onFoldToAodAnimationChanged()431 public void onFoldToAodAnimationChanged() { 432 updateControlScreenOff(); 433 } 434 435 @Override dump(@onNull PrintWriter pw, @NonNull String[] args)436 public void dump(@NonNull PrintWriter pw, @NonNull String[] args) { 437 pw.print("getAlwaysOn(): "); pw.println(getAlwaysOn()); 438 pw.print("getDisplayStateSupported(): "); pw.println(getDisplayStateSupported()); 439 pw.print("getPulseDuration(): "); pw.println(getPulseDuration()); 440 pw.print("getPulseInDuration(): "); pw.println(getPulseInDuration()); 441 pw.print("getPulseInVisibleDuration(): "); pw.println(getPulseVisibleDuration()); 442 pw.print("getPulseOutDuration(): "); pw.println(getPulseOutDuration()); 443 pw.print("getPulseOnSigMotion(): "); pw.println(getPulseOnSigMotion()); 444 pw.print("getVibrateOnSigMotion(): "); pw.println(getVibrateOnSigMotion()); 445 pw.print("getVibrateOnPickup(): "); pw.println(getVibrateOnPickup()); 446 pw.print("getProxCheckBeforePulse(): "); pw.println(getProxCheckBeforePulse()); 447 pw.print("getPickupVibrationThreshold(): "); pw.println(getPickupVibrationThreshold()); 448 pw.print("getSelectivelyRegisterSensorsUsingProx(): "); 449 pw.println(getSelectivelyRegisterSensorsUsingProx()); 450 pw.print("isQuickPickupEnabled(): "); pw.println(isQuickPickupEnabled()); 451 } 452 dispatchAlwaysOnEvent()453 private void dispatchAlwaysOnEvent() { 454 mScreenOffAnimationController.onAlwaysOnChanged(getAlwaysOn()); 455 mDozeInteractor.setAodAvailable(getAlwaysOn()); 456 457 } 458 getPostureSpecificBool( int[] postureMapping, boolean defaultSensorBool, int posture)459 private boolean getPostureSpecificBool( 460 int[] postureMapping, 461 boolean defaultSensorBool, 462 int posture) { 463 boolean bool = defaultSensorBool; 464 if (posture < postureMapping.length) { 465 bool = postureMapping[posture] != 0; 466 } else { 467 Log.e("DozeParameters", "Unsupported doze posture " + posture); 468 } 469 470 return bool; 471 } 472 473 private final class SettingsObserver extends ContentObserver { 474 private final Uri mQuickPickupGesture = 475 Settings.Secure.getUriFor(Settings.Secure.DOZE_QUICK_PICKUP_GESTURE); 476 private final Uri mPickupGesture = 477 Settings.Secure.getUriFor(Settings.Secure.DOZE_PICK_UP_GESTURE); 478 private final Uri mAlwaysOnEnabled = 479 Settings.Secure.getUriFor(Settings.Secure.DOZE_ALWAYS_ON); 480 private final Context mContext; 481 SettingsObserver(Context context, Handler handler)482 SettingsObserver(Context context, Handler handler) { 483 super(handler); 484 mContext = context; 485 } 486 observe()487 void observe() { 488 ContentResolver resolver = mContext.getContentResolver(); 489 resolver.registerContentObserver(mQuickPickupGesture, false, this, 490 UserHandle.USER_ALL); 491 resolver.registerContentObserver(mPickupGesture, false, this, UserHandle.USER_ALL); 492 resolver.registerContentObserver(mAlwaysOnEnabled, false, this, UserHandle.USER_ALL); 493 update(null); 494 } 495 496 @Override onChange(boolean selfChange, Uri uri)497 public void onChange(boolean selfChange, Uri uri) { 498 update(uri); 499 } 500 update(Uri uri)501 public void update(Uri uri) { 502 if (uri == null 503 || mQuickPickupGesture.equals(uri) 504 || mPickupGesture.equals(uri) 505 || mAlwaysOnEnabled.equals(uri)) { 506 // the quick pickup gesture is dependent on alwaysOn being disabled and 507 // the pickup gesture being enabled 508 updateQuickPickupEnabled(); 509 } 510 } 511 } 512 } 513