1 /* 2 * Copyright (C) 2019 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 static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_AWAKE; 20 import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_WAKING; 21 22 import android.annotation.NonNull; 23 import android.os.Bundle; 24 import android.os.PowerManager; 25 import android.os.SystemClock; 26 import android.os.SystemProperties; 27 import android.util.Log; 28 import android.view.MotionEvent; 29 import android.view.View; 30 31 import com.android.internal.annotations.VisibleForTesting; 32 import com.android.keyguard.KeyguardUpdateMonitor; 33 import com.android.systemui.assist.AssistManager; 34 import com.android.systemui.doze.DozeHost; 35 import com.android.systemui.doze.DozeLog; 36 import com.android.systemui.doze.DozeReceiver; 37 import com.android.systemui.keyguard.KeyguardViewMediator; 38 import com.android.systemui.keyguard.WakefulnessLifecycle; 39 import com.android.systemui.statusbar.PulseExpansionHandler; 40 import com.android.systemui.statusbar.StatusBarState; 41 import com.android.systemui.statusbar.SysuiStatusBarStateController; 42 import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; 43 import com.android.systemui.statusbar.notification.VisualStabilityManager; 44 import com.android.systemui.statusbar.notification.collection.NotificationEntry; 45 import com.android.systemui.statusbar.policy.BatteryController; 46 import com.android.systemui.statusbar.policy.DeviceProvisionedController; 47 48 import java.util.ArrayList; 49 50 import javax.inject.Inject; 51 import javax.inject.Singleton; 52 53 import dagger.Lazy; 54 55 /** 56 * Implementation of DozeHost for SystemUI. 57 */ 58 @Singleton 59 public final class DozeServiceHost implements DozeHost { 60 private static final String TAG = "DozeServiceHost"; 61 private final ArrayList<Callback> mCallbacks = new ArrayList<>(); 62 private final DozeLog mDozeLog; 63 private final PowerManager mPowerManager; 64 private boolean mAnimateWakeup; 65 private boolean mAnimateScreenOff; 66 private boolean mIgnoreTouchWhilePulsing; 67 private Runnable mPendingScreenOffCallback; 68 @VisibleForTesting 69 boolean mWakeLockScreenPerformsAuth = SystemProperties.getBoolean( 70 "persist.sysui.wake_performs_auth", true); 71 private boolean mDozingRequested; 72 private boolean mPulsing; 73 private WakefulnessLifecycle mWakefulnessLifecycle; 74 private final SysuiStatusBarStateController mStatusBarStateController; 75 private final DeviceProvisionedController mDeviceProvisionedController; 76 private final HeadsUpManagerPhone mHeadsUpManagerPhone; 77 private final BatteryController mBatteryController; 78 private final ScrimController mScrimController; 79 private final Lazy<BiometricUnlockController> mBiometricUnlockControllerLazy; 80 private final KeyguardViewMediator mKeyguardViewMediator; 81 private final Lazy<AssistManager> mAssistManagerLazy; 82 private final DozeScrimController mDozeScrimController; 83 private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; 84 private final VisualStabilityManager mVisualStabilityManager; 85 private final PulseExpansionHandler mPulseExpansionHandler; 86 private final NotificationShadeWindowController mNotificationShadeWindowController; 87 private final NotificationWakeUpCoordinator mNotificationWakeUpCoordinator; 88 private NotificationShadeWindowViewController mNotificationShadeWindowViewController; 89 private final LockscreenLockIconController mLockscreenLockIconController; 90 private NotificationIconAreaController mNotificationIconAreaController; 91 private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; 92 private NotificationPanelViewController mNotificationPanel; 93 private View mAmbientIndicationContainer; 94 private StatusBar mStatusBar; 95 private boolean mSuppressed; 96 97 @Inject DozeServiceHost(DozeLog dozeLog, PowerManager powerManager, WakefulnessLifecycle wakefulnessLifecycle, SysuiStatusBarStateController statusBarStateController, DeviceProvisionedController deviceProvisionedController, HeadsUpManagerPhone headsUpManagerPhone, BatteryController batteryController, ScrimController scrimController, Lazy<BiometricUnlockController> biometricUnlockControllerLazy, KeyguardViewMediator keyguardViewMediator, Lazy<AssistManager> assistManagerLazy, DozeScrimController dozeScrimController, KeyguardUpdateMonitor keyguardUpdateMonitor, VisualStabilityManager visualStabilityManager, PulseExpansionHandler pulseExpansionHandler, NotificationShadeWindowController notificationShadeWindowController, NotificationWakeUpCoordinator notificationWakeUpCoordinator, LockscreenLockIconController lockscreenLockIconController)98 public DozeServiceHost(DozeLog dozeLog, PowerManager powerManager, 99 WakefulnessLifecycle wakefulnessLifecycle, 100 SysuiStatusBarStateController statusBarStateController, 101 DeviceProvisionedController deviceProvisionedController, 102 HeadsUpManagerPhone headsUpManagerPhone, BatteryController batteryController, 103 ScrimController scrimController, 104 Lazy<BiometricUnlockController> biometricUnlockControllerLazy, 105 KeyguardViewMediator keyguardViewMediator, 106 Lazy<AssistManager> assistManagerLazy, 107 DozeScrimController dozeScrimController, KeyguardUpdateMonitor keyguardUpdateMonitor, 108 VisualStabilityManager visualStabilityManager, 109 PulseExpansionHandler pulseExpansionHandler, 110 NotificationShadeWindowController notificationShadeWindowController, 111 NotificationWakeUpCoordinator notificationWakeUpCoordinator, 112 LockscreenLockIconController lockscreenLockIconController) { 113 super(); 114 mDozeLog = dozeLog; 115 mPowerManager = powerManager; 116 mWakefulnessLifecycle = wakefulnessLifecycle; 117 mStatusBarStateController = statusBarStateController; 118 mDeviceProvisionedController = deviceProvisionedController; 119 mHeadsUpManagerPhone = headsUpManagerPhone; 120 mBatteryController = batteryController; 121 mScrimController = scrimController; 122 mBiometricUnlockControllerLazy = biometricUnlockControllerLazy; 123 mKeyguardViewMediator = keyguardViewMediator; 124 mAssistManagerLazy = assistManagerLazy; 125 mDozeScrimController = dozeScrimController; 126 mKeyguardUpdateMonitor = keyguardUpdateMonitor; 127 mVisualStabilityManager = visualStabilityManager; 128 mPulseExpansionHandler = pulseExpansionHandler; 129 mNotificationShadeWindowController = notificationShadeWindowController; 130 mNotificationWakeUpCoordinator = notificationWakeUpCoordinator; 131 mLockscreenLockIconController = lockscreenLockIconController; 132 } 133 134 // TODO: we should try to not pass status bar in here if we can avoid it. 135 136 /** 137 * Initialize instance with objects only available later during execution. 138 */ initialize(StatusBar statusBar, NotificationIconAreaController notificationIconAreaController, StatusBarKeyguardViewManager statusBarKeyguardViewManager, NotificationShadeWindowViewController notificationShadeWindowViewController, NotificationPanelViewController notificationPanel, View ambientIndicationContainer)139 public void initialize(StatusBar statusBar, 140 NotificationIconAreaController notificationIconAreaController, 141 StatusBarKeyguardViewManager statusBarKeyguardViewManager, 142 NotificationShadeWindowViewController notificationShadeWindowViewController, 143 NotificationPanelViewController notificationPanel, View ambientIndicationContainer) { 144 mStatusBar = statusBar; 145 mNotificationIconAreaController = notificationIconAreaController; 146 mStatusBarKeyguardViewManager = statusBarKeyguardViewManager; 147 mNotificationPanel = notificationPanel; 148 mNotificationShadeWindowViewController = notificationShadeWindowViewController; 149 mAmbientIndicationContainer = ambientIndicationContainer; 150 } 151 152 153 @Override toString()154 public String toString() { 155 return "PSB.DozeServiceHost[mCallbacks=" + mCallbacks.size() + "]"; 156 } 157 firePowerSaveChanged(boolean active)158 void firePowerSaveChanged(boolean active) { 159 for (Callback callback : mCallbacks) { 160 callback.onPowerSaveChanged(active); 161 } 162 } 163 fireNotificationPulse(NotificationEntry entry)164 void fireNotificationPulse(NotificationEntry entry) { 165 Runnable pulseSuppressedListener = () -> { 166 entry.setPulseSuppressed(true); 167 mNotificationIconAreaController.updateAodNotificationIcons(); 168 }; 169 for (Callback callback : mCallbacks) { 170 callback.onNotificationAlerted(pulseSuppressedListener); 171 } 172 } 173 getDozingRequested()174 boolean getDozingRequested() { 175 return mDozingRequested; 176 } 177 isPulsing()178 boolean isPulsing() { 179 return mPulsing; 180 } 181 182 183 @Override addCallback(@onNull Callback callback)184 public void addCallback(@NonNull Callback callback) { 185 mCallbacks.add(callback); 186 } 187 188 @Override removeCallback(@onNull Callback callback)189 public void removeCallback(@NonNull Callback callback) { 190 mCallbacks.remove(callback); 191 } 192 193 @Override startDozing()194 public void startDozing() { 195 if (!mDozingRequested) { 196 mDozingRequested = true; 197 updateDozing(); 198 mDozeLog.traceDozing(mStatusBarStateController.isDozing()); 199 mStatusBar.updateIsKeyguard(); 200 } 201 } 202 updateDozing()203 void updateDozing() { 204 // When in wake-and-unlock while pulsing, keep dozing state until fully unlocked. 205 boolean 206 dozing = 207 mDozingRequested && mStatusBarStateController.getState() == StatusBarState.KEYGUARD 208 || mBiometricUnlockControllerLazy.get().getMode() 209 == BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING; 210 // When in wake-and-unlock we may not have received a change to StatusBarState 211 // but we still should not be dozing, manually set to false. 212 if (mBiometricUnlockControllerLazy.get().getMode() 213 == BiometricUnlockController.MODE_WAKE_AND_UNLOCK) { 214 dozing = false; 215 } 216 217 mStatusBarStateController.setIsDozing(dozing); 218 } 219 220 @Override pulseWhileDozing(@onNull PulseCallback callback, int reason)221 public void pulseWhileDozing(@NonNull PulseCallback callback, int reason) { 222 if (reason == DozeLog.PULSE_REASON_SENSOR_LONG_PRESS) { 223 mPowerManager.wakeUp(SystemClock.uptimeMillis(), PowerManager.WAKE_REASON_GESTURE, 224 "com.android.systemui:LONG_PRESS"); 225 mAssistManagerLazy.get().startAssist(new Bundle()); 226 return; 227 } 228 229 if (reason == DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN) { 230 mScrimController.setWakeLockScreenSensorActive(true); 231 } 232 233 boolean passiveAuthInterrupt = reason == DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN 234 && mWakeLockScreenPerformsAuth; 235 // Set the state to pulsing, so ScrimController will know what to do once we ask it to 236 // execute the transition. The pulse callback will then be invoked when the scrims 237 // are black, indicating that StatusBar is ready to present the rest of the UI. 238 mPulsing = true; 239 mDozeScrimController.pulse(new PulseCallback() { 240 @Override 241 public void onPulseStarted() { 242 callback.onPulseStarted(); 243 mStatusBar.updateNotificationPanelTouchState(); 244 setPulsing(true); 245 } 246 247 @Override 248 public void onPulseFinished() { 249 mPulsing = false; 250 callback.onPulseFinished(); 251 mStatusBar.updateNotificationPanelTouchState(); 252 mScrimController.setWakeLockScreenSensorActive(false); 253 setPulsing(false); 254 } 255 256 private void setPulsing(boolean pulsing) { 257 mStatusBarStateController.setPulsing(pulsing); 258 mStatusBarKeyguardViewManager.setPulsing(pulsing); 259 mKeyguardViewMediator.setPulsing(pulsing); 260 mNotificationPanel.setPulsing(pulsing); 261 mVisualStabilityManager.setPulsing(pulsing); 262 mIgnoreTouchWhilePulsing = false; 263 if (mKeyguardUpdateMonitor != null && passiveAuthInterrupt) { 264 mKeyguardUpdateMonitor.onAuthInterruptDetected(pulsing /* active */); 265 } 266 mStatusBar.updateScrimController(); 267 mPulseExpansionHandler.setPulsing(pulsing); 268 mNotificationWakeUpCoordinator.setPulsing(pulsing); 269 } 270 }, reason); 271 // DozeScrimController is in pulse state, now let's ask ScrimController to start 272 // pulsing and draw the black frame, if necessary. 273 mStatusBar.updateScrimController(); 274 } 275 276 @Override stopDozing()277 public void stopDozing() { 278 if (mDozingRequested) { 279 mDozingRequested = false; 280 updateDozing(); 281 mDozeLog.traceDozing(mStatusBarStateController.isDozing()); 282 } 283 } 284 285 @Override onIgnoreTouchWhilePulsing(boolean ignore)286 public void onIgnoreTouchWhilePulsing(boolean ignore) { 287 if (ignore != mIgnoreTouchWhilePulsing) { 288 mDozeLog.tracePulseTouchDisabledByProx(ignore); 289 } 290 mIgnoreTouchWhilePulsing = ignore; 291 if (mStatusBarStateController.isDozing() && ignore) { 292 mNotificationShadeWindowViewController.cancelCurrentTouch(); 293 } 294 } 295 296 @Override dozeTimeTick()297 public void dozeTimeTick() { 298 mNotificationPanel.dozeTimeTick(); 299 if (mAmbientIndicationContainer instanceof DozeReceiver) { 300 ((DozeReceiver) mAmbientIndicationContainer).dozeTimeTick(); 301 } 302 } 303 304 @Override isPowerSaveActive()305 public boolean isPowerSaveActive() { 306 return mBatteryController.isAodPowerSave(); 307 } 308 309 @Override isPulsingBlocked()310 public boolean isPulsingBlocked() { 311 return mBiometricUnlockControllerLazy.get().getMode() 312 == BiometricUnlockController.MODE_WAKE_AND_UNLOCK; 313 } 314 315 @Override isProvisioned()316 public boolean isProvisioned() { 317 return mDeviceProvisionedController.isDeviceProvisioned() 318 && mDeviceProvisionedController.isCurrentUserSetup(); 319 } 320 321 @Override isBlockingDoze()322 public boolean isBlockingDoze() { 323 if (mBiometricUnlockControllerLazy.get().hasPendingAuthentication()) { 324 Log.i(StatusBar.TAG, "Blocking AOD because fingerprint has authenticated"); 325 return true; 326 } 327 return false; 328 } 329 330 @Override extendPulse(int reason)331 public void extendPulse(int reason) { 332 if (reason == DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN) { 333 mScrimController.setWakeLockScreenSensorActive(true); 334 } 335 if (mDozeScrimController.isPulsing() && mHeadsUpManagerPhone.hasNotifications()) { 336 mHeadsUpManagerPhone.extendHeadsUp(); 337 } else { 338 mDozeScrimController.extendPulse(); 339 } 340 } 341 342 @Override stopPulsing()343 public void stopPulsing() { 344 if (mDozeScrimController.isPulsing()) { 345 mDozeScrimController.pulseOutNow(); 346 } 347 } 348 349 @Override setAnimateWakeup(boolean animateWakeup)350 public void setAnimateWakeup(boolean animateWakeup) { 351 if (mWakefulnessLifecycle.getWakefulness() == WAKEFULNESS_AWAKE 352 || mWakefulnessLifecycle.getWakefulness() == WAKEFULNESS_WAKING) { 353 // Too late to change the wakeup animation. 354 return; 355 } 356 mAnimateWakeup = animateWakeup; 357 } 358 359 @Override setAnimateScreenOff(boolean animateScreenOff)360 public void setAnimateScreenOff(boolean animateScreenOff) { 361 mAnimateScreenOff = animateScreenOff; 362 } 363 364 @Override onSlpiTap(float screenX, float screenY)365 public void onSlpiTap(float screenX, float screenY) { 366 if (screenX > 0 && screenY > 0 && mAmbientIndicationContainer != null 367 && mAmbientIndicationContainer.getVisibility() == View.VISIBLE) { 368 int[] locationOnScreen = new int[2]; 369 mAmbientIndicationContainer.getLocationOnScreen(locationOnScreen); 370 float viewX = screenX - locationOnScreen[0]; 371 float viewY = screenY - locationOnScreen[1]; 372 if (0 <= viewX && viewX <= mAmbientIndicationContainer.getWidth() 373 && 0 <= viewY && viewY <= mAmbientIndicationContainer.getHeight()) { 374 375 // Dispatch a tap 376 long now = SystemClock.elapsedRealtime(); 377 MotionEvent ev = MotionEvent.obtain( 378 now, now, MotionEvent.ACTION_DOWN, screenX, screenY, 0); 379 mAmbientIndicationContainer.dispatchTouchEvent(ev); 380 ev.recycle(); 381 ev = MotionEvent.obtain( 382 now, now, MotionEvent.ACTION_UP, screenX, screenY, 0); 383 mAmbientIndicationContainer.dispatchTouchEvent(ev); 384 ev.recycle(); 385 } 386 } 387 } 388 389 @Override setDozeScreenBrightness(int value)390 public void setDozeScreenBrightness(int value) { 391 mNotificationShadeWindowController.setDozeScreenBrightness(value); 392 } 393 394 @Override setAodDimmingScrim(float scrimOpacity)395 public void setAodDimmingScrim(float scrimOpacity) { 396 mScrimController.setAodFrontScrimAlpha(scrimOpacity); 397 } 398 399 400 401 @Override prepareForGentleSleep(Runnable onDisplayOffCallback)402 public void prepareForGentleSleep(Runnable onDisplayOffCallback) { 403 if (mPendingScreenOffCallback != null) { 404 Log.w(TAG, "Overlapping onDisplayOffCallback. Ignoring previous one."); 405 } 406 mPendingScreenOffCallback = onDisplayOffCallback; 407 mStatusBar.updateScrimController(); 408 } 409 410 @Override cancelGentleSleep()411 public void cancelGentleSleep() { 412 mPendingScreenOffCallback = null; 413 if (mScrimController.getState() == ScrimState.OFF) { 414 mStatusBar.updateScrimController(); 415 } 416 } 417 418 /** 419 * When the dozing host is waiting for scrims to fade out to change the display state. 420 */ hasPendingScreenOffCallback()421 boolean hasPendingScreenOffCallback() { 422 return mPendingScreenOffCallback != null; 423 } 424 425 /** 426 * Executes an nullifies the pending display state callback. 427 * 428 * @see #hasPendingScreenOffCallback() 429 * @see #prepareForGentleSleep(Runnable) 430 */ executePendingScreenOffCallback()431 void executePendingScreenOffCallback() { 432 if (mPendingScreenOffCallback == null) { 433 return; 434 } 435 mPendingScreenOffCallback.run(); 436 mPendingScreenOffCallback = null; 437 } 438 shouldAnimateWakeup()439 boolean shouldAnimateWakeup() { 440 return mAnimateWakeup; 441 } 442 shouldAnimateScreenOff()443 boolean shouldAnimateScreenOff() { 444 return mAnimateScreenOff; 445 } 446 getIgnoreTouchWhilePulsing()447 boolean getIgnoreTouchWhilePulsing() { 448 return mIgnoreTouchWhilePulsing; 449 } 450 setDozeSuppressed(boolean suppressed)451 void setDozeSuppressed(boolean suppressed) { 452 if (suppressed == mSuppressed) { 453 return; 454 } 455 mSuppressed = suppressed; 456 for (Callback callback : mCallbacks) { 457 callback.onDozeSuppressedChanged(suppressed); 458 } 459 } 460 isDozeSuppressed()461 public boolean isDozeSuppressed() { 462 return mSuppressed; 463 } 464 } 465