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.R; 41 import com.android.systemui.dagger.SysUISingleton; 42 import com.android.systemui.dagger.qualifiers.Background; 43 import com.android.systemui.dagger.qualifiers.Main; 44 import com.android.systemui.doze.AlwaysOnDisplayPolicy; 45 import com.android.systemui.doze.DozeScreenState; 46 import com.android.systemui.dump.DumpManager; 47 import com.android.systemui.plugins.statusbar.StatusBarStateController; 48 import com.android.systemui.settings.UserTracker; 49 import com.android.systemui.statusbar.policy.BatteryController; 50 import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback; 51 import com.android.systemui.statusbar.policy.ConfigurationController; 52 import com.android.systemui.statusbar.policy.DevicePostureController; 53 import com.android.systemui.tuner.TunerService; 54 import com.android.systemui.unfold.FoldAodAnimationController; 55 import com.android.systemui.unfold.SysUIUnfoldComponent; 56 57 import java.io.PrintWriter; 58 import java.util.HashSet; 59 import java.util.Optional; 60 import java.util.Set; 61 62 import javax.inject.Inject; 63 64 /** 65 * Retrieve doze information 66 */ 67 @SysUISingleton 68 public class DozeParameters implements 69 TunerService.Tunable, 70 com.android.systemui.plugins.statusbar.DozeParameters, 71 Dumpable, ConfigurationController.ConfigurationListener, 72 StatusBarStateController.StateListener, FoldAodAnimationController.FoldAodAnimationStatus { 73 private static final int MAX_DURATION = 60 * 1000; 74 public static final boolean FORCE_NO_BLANKING = 75 SystemProperties.getBoolean("debug.force_no_blanking", false); 76 public static final boolean FORCE_BLANKING = 77 SystemProperties.getBoolean("debug.force_blanking", false); 78 79 private final AmbientDisplayConfiguration mAmbientDisplayConfiguration; 80 private final PowerManager mPowerManager; 81 82 private final AlwaysOnDisplayPolicy mAlwaysOnPolicy; 83 private final Resources mResources; 84 private final BatteryController mBatteryController; 85 private final ScreenOffAnimationController mScreenOffAnimationController; 86 private final FoldAodAnimationController mFoldAodAnimationController; 87 private final UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController; 88 private final UserTracker mUserTracker; 89 90 private final Set<Callback> mCallbacks = new HashSet<>(); 91 92 private boolean mDozeAlwaysOn; 93 private boolean mControlScreenOffAnimation; 94 private boolean mIsQuickPickupEnabled; 95 96 private boolean mKeyguardVisible; 97 @VisibleForTesting 98 final KeyguardUpdateMonitorCallback mKeyguardVisibilityCallback = 99 new KeyguardUpdateMonitorCallback() { 100 @Override 101 public void onKeyguardVisibilityChanged(boolean visible) { 102 mKeyguardVisible = visible; 103 updateControlScreenOff(); 104 } 105 106 @Override 107 public void onShadeExpandedChanged(boolean expanded) { 108 updateControlScreenOff(); 109 } 110 111 @Override 112 public void onUserSwitchComplete(int newUserId) { 113 updateQuickPickupEnabled(); 114 } 115 }; 116 117 @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)118 protected DozeParameters( 119 Context context, 120 @Background Handler handler, 121 @Main Resources resources, 122 AmbientDisplayConfiguration ambientDisplayConfiguration, 123 AlwaysOnDisplayPolicy alwaysOnDisplayPolicy, 124 PowerManager powerManager, 125 BatteryController batteryController, 126 TunerService tunerService, 127 DumpManager dumpManager, 128 ScreenOffAnimationController screenOffAnimationController, 129 Optional<SysUIUnfoldComponent> sysUiUnfoldComponent, 130 UnlockedScreenOffAnimationController unlockedScreenOffAnimationController, 131 KeyguardUpdateMonitor keyguardUpdateMonitor, 132 ConfigurationController configurationController, 133 StatusBarStateController statusBarStateController, 134 UserTracker userTracker) { 135 mResources = resources; 136 mAmbientDisplayConfiguration = ambientDisplayConfiguration; 137 mAlwaysOnPolicy = alwaysOnDisplayPolicy; 138 mBatteryController = batteryController; 139 dumpManager.registerDumpable("DozeParameters", this); 140 141 mControlScreenOffAnimation = !getDisplayNeedsBlanking(); 142 mPowerManager = powerManager; 143 mPowerManager.setDozeAfterScreenOff(!mControlScreenOffAnimation); 144 mScreenOffAnimationController = screenOffAnimationController; 145 mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController; 146 mUserTracker = userTracker; 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 /** 410 * Callback to listen for DozeParameter changes. 411 */ addCallback(Callback callback)412 public void addCallback(Callback callback) { 413 mCallbacks.add(callback); 414 } 415 416 /** 417 * Remove callback that listens for DozeParameter changes. 418 */ removeCallback(Callback callback)419 public void removeCallback(Callback callback) { 420 mCallbacks.remove(callback); 421 } 422 423 @Override onTuningChanged(String key, String newValue)424 public void onTuningChanged(String key, String newValue) { 425 mDozeAlwaysOn = mAmbientDisplayConfiguration.alwaysOnEnabled(mUserTracker.getUserId()); 426 427 if (key.equals(Settings.Secure.DOZE_ALWAYS_ON)) { 428 updateControlScreenOff(); 429 } 430 431 dispatchAlwaysOnEvent(); 432 } 433 434 @Override onConfigChanged(Configuration newConfig)435 public void onConfigChanged(Configuration newConfig) { 436 updateControlScreenOff(); 437 } 438 439 @Override onStatePostChange()440 public void onStatePostChange() { 441 updateControlScreenOff(); 442 } 443 444 @Override onFoldToAodAnimationChanged()445 public void onFoldToAodAnimationChanged() { 446 updateControlScreenOff(); 447 } 448 449 @Override dump(@onNull PrintWriter pw, @NonNull String[] args)450 public void dump(@NonNull PrintWriter pw, @NonNull String[] args) { 451 pw.print("getAlwaysOn(): "); pw.println(getAlwaysOn()); 452 pw.print("getDisplayStateSupported(): "); pw.println(getDisplayStateSupported()); 453 pw.print("getPulseDuration(): "); pw.println(getPulseDuration()); 454 pw.print("getPulseInDuration(): "); pw.println(getPulseInDuration()); 455 pw.print("getPulseInVisibleDuration(): "); pw.println(getPulseVisibleDuration()); 456 pw.print("getPulseOutDuration(): "); pw.println(getPulseOutDuration()); 457 pw.print("getPulseOnSigMotion(): "); pw.println(getPulseOnSigMotion()); 458 pw.print("getVibrateOnSigMotion(): "); pw.println(getVibrateOnSigMotion()); 459 pw.print("getVibrateOnPickup(): "); pw.println(getVibrateOnPickup()); 460 pw.print("getProxCheckBeforePulse(): "); pw.println(getProxCheckBeforePulse()); 461 pw.print("getPickupVibrationThreshold(): "); pw.println(getPickupVibrationThreshold()); 462 pw.print("getSelectivelyRegisterSensorsUsingProx(): "); 463 pw.println(getSelectivelyRegisterSensorsUsingProx()); 464 pw.print("isQuickPickupEnabled(): "); pw.println(isQuickPickupEnabled()); 465 } 466 dispatchAlwaysOnEvent()467 private void dispatchAlwaysOnEvent() { 468 for (Callback callback : mCallbacks) { 469 callback.onAlwaysOnChange(); 470 } 471 mScreenOffAnimationController.onAlwaysOnChanged(getAlwaysOn()); 472 } 473 getPostureSpecificBool( int[] postureMapping, boolean defaultSensorBool, int posture)474 private boolean getPostureSpecificBool( 475 int[] postureMapping, 476 boolean defaultSensorBool, 477 int posture) { 478 boolean bool = defaultSensorBool; 479 if (posture < postureMapping.length) { 480 bool = postureMapping[posture] != 0; 481 } else { 482 Log.e("DozeParameters", "Unsupported doze posture " + posture); 483 } 484 485 return bool; 486 } 487 488 /** Callbacks for doze parameter related information */ 489 public interface Callback { 490 /** 491 * Invoked when the value of getAlwaysOn may have changed. 492 */ onAlwaysOnChange()493 void onAlwaysOnChange(); 494 } 495 496 private final class SettingsObserver extends ContentObserver { 497 private final Uri mQuickPickupGesture = 498 Settings.Secure.getUriFor(Settings.Secure.DOZE_QUICK_PICKUP_GESTURE); 499 private final Uri mPickupGesture = 500 Settings.Secure.getUriFor(Settings.Secure.DOZE_PICK_UP_GESTURE); 501 private final Uri mAlwaysOnEnabled = 502 Settings.Secure.getUriFor(Settings.Secure.DOZE_ALWAYS_ON); 503 private final Context mContext; 504 SettingsObserver(Context context, Handler handler)505 SettingsObserver(Context context, Handler handler) { 506 super(handler); 507 mContext = context; 508 } 509 observe()510 void observe() { 511 ContentResolver resolver = mContext.getContentResolver(); 512 resolver.registerContentObserver(mQuickPickupGesture, false, this, 513 UserHandle.USER_ALL); 514 resolver.registerContentObserver(mPickupGesture, false, this, UserHandle.USER_ALL); 515 resolver.registerContentObserver(mAlwaysOnEnabled, false, this, UserHandle.USER_ALL); 516 update(null); 517 } 518 519 @Override onChange(boolean selfChange, Uri uri)520 public void onChange(boolean selfChange, Uri uri) { 521 update(uri); 522 } 523 update(Uri uri)524 public void update(Uri uri) { 525 if (uri == null 526 || mQuickPickupGesture.equals(uri) 527 || mPickupGesture.equals(uri) 528 || mAlwaysOnEnabled.equals(uri)) { 529 // the quick pickup gesture is dependent on alwaysOn being disabled and 530 // the pickup gesture being enabled 531 updateQuickPickupEnabled(); 532 } 533 } 534 } 535 } 536