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