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