• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 mAnimateScreenOff;
67     private boolean mIgnoreTouchWhilePulsing;
68     private Runnable mPendingScreenOffCallback;
69     @VisibleForTesting
70     boolean mWakeLockScreenPerformsAuth = SystemProperties.getBoolean(
71             "persist.sysui.wake_performs_auth", true);
72     private boolean mDozingRequested;
73     private boolean mPulsing;
74     private final WakefulnessLifecycle mWakefulnessLifecycle;
75     private final SysuiStatusBarStateController mStatusBarStateController;
76     private final DeviceProvisionedController mDeviceProvisionedController;
77     private final HeadsUpManagerPhone mHeadsUpManagerPhone;
78     private final BatteryController mBatteryController;
79     private final ScrimController mScrimController;
80     private final Lazy<BiometricUnlockController> mBiometricUnlockControllerLazy;
81     private final KeyguardViewMediator mKeyguardViewMediator;
82     private final Lazy<AssistManager> mAssistManagerLazy;
83     private final DozeScrimController mDozeScrimController;
84     private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
85     private final PulseExpansionHandler mPulseExpansionHandler;
86     private final NotificationShadeWindowController mNotificationShadeWindowController;
87     private final NotificationWakeUpCoordinator mNotificationWakeUpCoordinator;
88     private NotificationShadeWindowViewController mNotificationShadeWindowViewController;
89     private final AuthController mAuthController;
90     private final 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, PulseExpansionHandler pulseExpansionHandler, NotificationShadeWindowController notificationShadeWindowController, NotificationWakeUpCoordinator notificationWakeUpCoordinator, AuthController authController, NotificationIconAreaController notificationIconAreaController)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             PulseExpansionHandler pulseExpansionHandler,
109             NotificationShadeWindowController notificationShadeWindowController,
110             NotificationWakeUpCoordinator notificationWakeUpCoordinator,
111             AuthController authController,
112             NotificationIconAreaController notificationIconAreaController) {
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         mPulseExpansionHandler = pulseExpansionHandler;
128         mNotificationShadeWindowController = notificationShadeWindowController;
129         mNotificationWakeUpCoordinator = notificationWakeUpCoordinator;
130         mAuthController = authController;
131         mNotificationIconAreaController = notificationIconAreaController;
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, StatusBarKeyguardViewManager statusBarKeyguardViewManager, NotificationShadeWindowViewController notificationShadeWindowViewController, NotificationPanelViewController notificationPanel, View ambientIndicationContainer)139     public void initialize(
140             StatusBar statusBar,
141             StatusBarKeyguardViewManager statusBarKeyguardViewManager,
142             NotificationShadeWindowViewController notificationShadeWindowViewController,
143             NotificationPanelViewController notificationPanel,
144             View ambientIndicationContainer) {
145         mStatusBar = statusBar;
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                 mStatusBarKeyguardViewManager.setPulsing(pulsing);
258                 mKeyguardViewMediator.setPulsing(pulsing);
259                 mNotificationPanel.setPulsing(pulsing);
260                 mStatusBarStateController.setPulsing(pulsing);
261                 mIgnoreTouchWhilePulsing = false;
262                 if (mKeyguardUpdateMonitor != null && passiveAuthInterrupt) {
263                     mKeyguardUpdateMonitor.onAuthInterruptDetected(pulsing /* active */);
264                 }
265                 mStatusBar.updateScrimController();
266                 mPulseExpansionHandler.setPulsing(pulsing);
267                 mNotificationWakeUpCoordinator.setPulsing(pulsing);
268             }
269         }, reason);
270         // DozeScrimController is in pulse state, now let's ask ScrimController to start
271         // pulsing and draw the black frame, if necessary.
272         mStatusBar.updateScrimController();
273     }
274 
275     @Override
stopDozing()276     public void stopDozing() {
277         if (mDozingRequested) {
278             mDozingRequested = false;
279             updateDozing();
280             mDozeLog.traceDozing(mStatusBarStateController.isDozing());
281         }
282     }
283 
284     @Override
onIgnoreTouchWhilePulsing(boolean ignore)285     public void onIgnoreTouchWhilePulsing(boolean ignore) {
286         if (ignore != mIgnoreTouchWhilePulsing) {
287             mDozeLog.tracePulseTouchDisabledByProx(ignore);
288         }
289         mIgnoreTouchWhilePulsing = ignore;
290         if (mStatusBarStateController.isDozing() && ignore) {
291             mNotificationShadeWindowViewController.cancelCurrentTouch();
292         }
293     }
294 
295     @Override
dozeTimeTick()296     public void dozeTimeTick() {
297         mNotificationPanel.dozeTimeTick();
298         mAuthController.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 brightness)390     public void setDozeScreenBrightness(int brightness) {
391         mDozeLog.traceDozeScreenBrightness(brightness);
392         mNotificationShadeWindowController.setDozeScreenBrightness(brightness);
393     }
394 
395     @Override
setAodDimmingScrim(float scrimOpacity)396     public void setAodDimmingScrim(float scrimOpacity) {
397         mDozeLog.traceSetAodDimmingScrim(scrimOpacity);
398         mScrimController.setAodFrontScrimAlpha(scrimOpacity);
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         mDozeLog.traceDozingSuppressed(mSuppressed);
457         for (Callback callback : mCallbacks) {
458             callback.onDozeSuppressedChanged(suppressed);
459         }
460     }
461 
isDozeSuppressed()462     public boolean isDozeSuppressed() {
463         return mSuppressed;
464     }
465 }
466