1 /*
2  * Copyright (C) 2016 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.doze;
18 
19 import static android.app.StatusBarManager.SESSION_KEYGUARD;
20 
21 import static com.android.systemui.doze.DozeMachine.State.DOZE_SUSPEND_TRIGGERS;
22 import static com.android.systemui.doze.DozeMachine.State.FINISH;
23 import static com.android.systemui.doze.DozeMachine.State.UNINITIALIZED;
24 
25 import android.annotation.Nullable;
26 import android.content.BroadcastReceiver;
27 import android.content.Context;
28 import android.content.Intent;
29 import android.content.IntentFilter;
30 import android.hardware.display.AmbientDisplayConfiguration;
31 import android.os.SystemClock;
32 import android.text.format.Formatter;
33 import android.util.IndentingPrintWriter;
34 import android.util.Log;
35 import android.view.Display;
36 
37 import androidx.annotation.NonNull;
38 
39 import com.android.internal.annotations.VisibleForTesting;
40 import com.android.internal.logging.InstanceId;
41 import com.android.internal.logging.UiEvent;
42 import com.android.internal.logging.UiEventLogger;
43 import com.android.systemui.biometrics.AuthController;
44 import com.android.systemui.broadcast.BroadcastDispatcher;
45 import com.android.systemui.dock.DockManager;
46 import com.android.systemui.doze.DozeMachine.State;
47 import com.android.systemui.doze.dagger.DozeScope;
48 import com.android.systemui.log.SessionTracker;
49 import com.android.systemui.settings.UserTracker;
50 import com.android.systemui.statusbar.phone.DozeParameters;
51 import com.android.systemui.statusbar.policy.DevicePostureController;
52 import com.android.systemui.statusbar.policy.KeyguardStateController;
53 import com.android.systemui.util.Assert;
54 import com.android.systemui.util.sensors.AsyncSensorManager;
55 import com.android.systemui.util.sensors.ProximityCheck;
56 import com.android.systemui.util.sensors.ProximitySensor;
57 import com.android.systemui.util.settings.SecureSettings;
58 import com.android.systemui.util.wakelock.WakeLock;
59 
60 import java.io.PrintWriter;
61 import java.util.Optional;
62 import java.util.function.Consumer;
63 
64 import javax.inject.Inject;
65 
66 /**
67  * Handles triggers for ambient state changes.
68  */
69 @DozeScope
70 public class DozeTriggers implements DozeMachine.Part {
71 
72     private static final String TAG = "DozeTriggers";
73     private static final boolean DEBUG = DozeService.DEBUG;
74 
75     /** adb shell am broadcast -a com.android.systemui.doze.pulse com.android.systemui */
76     private static final String PULSE_ACTION = "com.android.systemui.doze.pulse";
77 
78     /**
79      * Last value sent by the wake-display sensor.
80      * Assuming that the screen should start on.
81      */
82     private static boolean sWakeDisplaySensorState = true;
83 
84     private static final int PROXIMITY_TIMEOUT_DELAY_MS = 500;
85 
86     private final Context mContext;
87     private final SessionTracker mSessionTracker;
88     private DozeMachine mMachine;
89     private final DozeLog mDozeLog;
90     private final DozeSensors mDozeSensors;
91     private final DozeHost mDozeHost;
92     private final AmbientDisplayConfiguration mConfig;
93     private final DozeParameters mDozeParameters;
94     private final AsyncSensorManager mSensorManager;
95     private final WakeLock mWakeLock;
96     private final boolean mAllowPulseTriggers;
97     private final TriggerReceiver mBroadcastReceiver = new TriggerReceiver();
98     private final DockEventListener mDockEventListener = new DockEventListener();
99     private final DockManager mDockManager;
100     private final ProximityCheck mProxCheck;
101     private final BroadcastDispatcher mBroadcastDispatcher;
102     private final AuthController mAuthController;
103     private final KeyguardStateController mKeyguardStateController;
104     private final UserTracker mUserTracker;
105     private final UiEventLogger mUiEventLogger;
106 
107     private long mNotificationPulseTime;
108     private Runnable mAodInterruptRunnable;
109 
110     /** see {@link #onProximityFar} prox for callback */
111     private boolean mWantProxSensor;
112     private boolean mWantTouchScreenSensors;
113     private boolean mWantSensors;
114     private boolean mInAod;
115 
116     private final UserTracker.Callback mUserChangedCallback =
117             new UserTracker.Callback() {
118                 @Override
119                 public void onUserChanged(int newUser, @NonNull Context userContext) {
120                     mDozeSensors.onUserSwitched();
121                 }
122             };
123 
124     @VisibleForTesting
125     public enum DozingUpdateUiEvent implements UiEventLogger.UiEventEnum {
126         @UiEvent(doc = "Dozing updated due to notification.")
127         DOZING_UPDATE_NOTIFICATION(433),
128 
129         @UiEvent(doc = "Dozing updated due to sigmotion.")
130         DOZING_UPDATE_SIGMOTION(434),
131 
132         @UiEvent(doc = "Dozing updated because sensor was picked up.")
133         DOZING_UPDATE_SENSOR_PICKUP(435),
134 
135         @UiEvent(doc = "Dozing updated because sensor was double tapped.")
136         DOZING_UPDATE_SENSOR_DOUBLE_TAP(436),
137 
138         @UiEvent(doc = "Dozing updated because sensor was long squeezed.")
139         DOZING_UPDATE_SENSOR_LONG_SQUEEZE(437),
140 
141         @UiEvent(doc = "Dozing updated due to docking.")
142         DOZING_UPDATE_DOCKING(438),
143 
144         @UiEvent(doc = "Dozing updated because sensor woke up.")
145         DOZING_UPDATE_SENSOR_WAKEUP(439),
146 
147         @UiEvent(doc = "Dozing updated because sensor woke up the lockscreen.")
148         DOZING_UPDATE_SENSOR_WAKE_LOCKSCREEN(440),
149 
150         @UiEvent(doc = "Dozing updated because sensor was tapped.")
151         DOZING_UPDATE_SENSOR_TAP(441),
152 
153         @UiEvent(doc = "Dozing updated because on display auth was triggered from AOD.")
154         DOZING_UPDATE_AUTH_TRIGGERED(657),
155 
156         @UiEvent(doc = "Dozing updated because quick pickup sensor woke up.")
157         DOZING_UPDATE_QUICK_PICKUP(708),
158 
159         @UiEvent(doc = "Dozing updated - sensor wakeup timed out (from quick pickup or presence)")
160         DOZING_UPDATE_WAKE_TIMEOUT(794);
161 
162         private final int mId;
163 
DozingUpdateUiEvent(int id)164         DozingUpdateUiEvent(int id) {
165             mId = id;
166         }
167 
168         @Override
getId()169         public int getId() {
170             return mId;
171         }
172 
fromReason(int reason)173         static DozingUpdateUiEvent fromReason(int reason) {
174             switch (reason) {
175                 case 1: return DOZING_UPDATE_NOTIFICATION;
176                 case 2: return DOZING_UPDATE_SIGMOTION;
177                 case 3: return DOZING_UPDATE_SENSOR_PICKUP;
178                 case 4: return DOZING_UPDATE_SENSOR_DOUBLE_TAP;
179                 case 5: return DOZING_UPDATE_SENSOR_LONG_SQUEEZE;
180                 case 6: return DOZING_UPDATE_DOCKING;
181                 case 7: return DOZING_UPDATE_SENSOR_WAKEUP;
182                 case 8: return DOZING_UPDATE_SENSOR_WAKE_LOCKSCREEN;
183                 case 9: return DOZING_UPDATE_SENSOR_TAP;
184                 case 10: return DOZING_UPDATE_AUTH_TRIGGERED;
185                 case 11: return DOZING_UPDATE_QUICK_PICKUP;
186                 default: return null;
187             }
188         }
189     }
190 
191     @Inject
DozeTriggers(Context context, DozeHost dozeHost, AmbientDisplayConfiguration config, DozeParameters dozeParameters, AsyncSensorManager sensorManager, WakeLock wakeLock, DockManager dockManager, ProximitySensor proximitySensor, ProximityCheck proxCheck, DozeLog dozeLog, BroadcastDispatcher broadcastDispatcher, SecureSettings secureSettings, AuthController authController, UiEventLogger uiEventLogger, SessionTracker sessionTracker, KeyguardStateController keyguardStateController, DevicePostureController devicePostureController, UserTracker userTracker)192     public DozeTriggers(Context context, DozeHost dozeHost,
193             AmbientDisplayConfiguration config,
194             DozeParameters dozeParameters, AsyncSensorManager sensorManager,
195             WakeLock wakeLock, DockManager dockManager,
196             ProximitySensor proximitySensor,
197             ProximityCheck proxCheck,
198             DozeLog dozeLog, BroadcastDispatcher broadcastDispatcher,
199             SecureSettings secureSettings, AuthController authController,
200             UiEventLogger uiEventLogger,
201             SessionTracker sessionTracker,
202             KeyguardStateController keyguardStateController,
203             DevicePostureController devicePostureController,
204             UserTracker userTracker) {
205         mContext = context;
206         mDozeHost = dozeHost;
207         mConfig = config;
208         mDozeParameters = dozeParameters;
209         mSensorManager = sensorManager;
210         mWakeLock = wakeLock;
211         mAllowPulseTriggers = true;
212         mSessionTracker = sessionTracker;
213 
214         mDozeSensors = new DozeSensors(mContext.getResources(), mSensorManager, dozeParameters,
215                 config, wakeLock, this::onSensor, this::onProximityFar, dozeLog, proximitySensor,
216                 secureSettings, authController, devicePostureController, userTracker);
217         mDockManager = dockManager;
218         mProxCheck = proxCheck;
219         mDozeLog = dozeLog;
220         mBroadcastDispatcher = broadcastDispatcher;
221         mAuthController = authController;
222         mUiEventLogger = uiEventLogger;
223         mKeyguardStateController = keyguardStateController;
224         mUserTracker = userTracker;
225     }
226 
227     @Override
setDozeMachine(DozeMachine dozeMachine)228     public void setDozeMachine(DozeMachine dozeMachine) {
229         mMachine = dozeMachine;
230     }
231 
232     @Override
destroy()233     public void destroy() {
234         mDozeSensors.destroy();
235         mProxCheck.destroy();
236     }
237 
onNotification(Runnable onPulseSuppressedListener)238     private void onNotification(Runnable onPulseSuppressedListener) {
239         if (DozeMachine.DEBUG) {
240             Log.d(TAG, "requestNotificationPulse");
241         }
242         if (!sWakeDisplaySensorState) {
243             Log.d(TAG, "Wake display false. Pulse denied.");
244             runIfNotNull(onPulseSuppressedListener);
245             mDozeLog.tracePulseDropped("wakeDisplaySensor");
246             return;
247         }
248         mNotificationPulseTime = SystemClock.elapsedRealtime();
249         if (!mConfig.pulseOnNotificationEnabled(mUserTracker.getUserId())) {
250             runIfNotNull(onPulseSuppressedListener);
251             mDozeLog.tracePulseDropped("pulseOnNotificationsDisabled");
252             return;
253         }
254         if (mDozeHost.isAlwaysOnSuppressed()) {
255             runIfNotNull(onPulseSuppressedListener);
256             mDozeLog.tracePulseDropped("dozeSuppressed");
257             return;
258         }
259         requestPulse(DozeLog.PULSE_REASON_NOTIFICATION, false /* performedProxCheck */,
260                 onPulseSuppressedListener);
261         mDozeLog.traceNotificationPulse();
262     }
263 
runIfNotNull(Runnable runnable)264     private static void runIfNotNull(Runnable runnable) {
265         if (runnable != null) {
266             runnable.run();
267         }
268     }
269 
proximityCheckThenCall(Consumer<Boolean> callback, boolean alreadyPerformedProxCheck, int reason)270     private void proximityCheckThenCall(Consumer<Boolean> callback,
271             boolean alreadyPerformedProxCheck,
272             int reason) {
273         Boolean cachedProxNear = mDozeSensors.isProximityCurrentlyNear();
274         if (alreadyPerformedProxCheck) {
275             callback.accept(null);
276         } else if (cachedProxNear != null) {
277             callback.accept(cachedProxNear);
278         } else {
279             final long start = SystemClock.uptimeMillis();
280             mProxCheck.check(PROXIMITY_TIMEOUT_DELAY_MS, near -> {
281                 final long end = SystemClock.uptimeMillis();
282                 mDozeLog.traceProximityResult(
283                         near != null && near,
284                         end - start,
285                         reason);
286                 callback.accept(near);
287                 mWakeLock.release(TAG);
288             });
289             mWakeLock.acquire(TAG);
290         }
291     }
292 
293     @VisibleForTesting
onSensor(int pulseReason, float screenX, float screenY, float[] rawValues)294     void onSensor(int pulseReason, float screenX, float screenY, float[] rawValues) {
295         boolean isDoubleTap = pulseReason == DozeLog.REASON_SENSOR_DOUBLE_TAP;
296         boolean isTap = pulseReason == DozeLog.REASON_SENSOR_TAP;
297         boolean isPickup = pulseReason == DozeLog.REASON_SENSOR_PICKUP;
298         boolean isLongPress = pulseReason == DozeLog.PULSE_REASON_SENSOR_LONG_PRESS;
299         boolean isWakeOnPresence = pulseReason == DozeLog.REASON_SENSOR_WAKE_UP_PRESENCE;
300         boolean isWakeOnReach = pulseReason == DozeLog.PULSE_REASON_SENSOR_WAKE_REACH;
301         boolean isUdfpsLongPress = pulseReason == DozeLog.REASON_SENSOR_UDFPS_LONG_PRESS;
302         boolean isQuickPickup = pulseReason == DozeLog.REASON_SENSOR_QUICK_PICKUP;
303         boolean isWakeDisplayEvent = isQuickPickup || ((isWakeOnPresence || isWakeOnReach)
304                 && rawValues != null && rawValues.length > 0 && rawValues[0] != 0);
305 
306         if (isWakeOnPresence) {
307             onWakeScreen(isWakeDisplayEvent,
308                     mMachine.isExecutingTransition() ? null : mMachine.getState(),
309                     pulseReason);
310         } else if (isLongPress) {
311             requestPulse(pulseReason, true /* alreadyPerformedProxCheck */,
312                     null /* onPulseSuppressedListener */);
313         } else if (isWakeOnReach || isQuickPickup) {
314             if (isWakeDisplayEvent) {
315                 requestPulse(pulseReason, true /* alreadyPerformedProxCheck */,
316                         null /* onPulseSuppressedListener */);
317             }
318         } else {
319             proximityCheckThenCall((isNear) -> {
320                 if (isNear != null && isNear) {
321                     // In pocket, drop event.
322                     mDozeLog.traceSensorEventDropped(pulseReason, "prox reporting near");
323                     return;
324                 }
325                 if (isDoubleTap || isTap) {
326                     mDozeHost.onSlpiTap(screenX, screenY);
327                     gentleWakeUp(pulseReason);
328                 } else if (isPickup) {
329                     if (shouldDropPickupEvent())  {
330                         mDozeLog.traceSensorEventDropped(pulseReason, "keyguard occluded");
331                         return;
332                     }
333                     gentleWakeUp(pulseReason);
334                 } else if (isUdfpsLongPress) {
335                     if (canPulse(mMachine.getState(), true)) {
336                         mDozeLog.d("updfsLongPress - setting aodInterruptRunnable to run when "
337                                 + "the display is on");
338                         // Since the gesture won't be received by the UDFPS view, we need to
339                         // manually inject an event once the display is ON
340                         mAodInterruptRunnable = () ->
341                                 mAuthController.onAodInterrupt((int) screenX, (int) screenY,
342                                         rawValues[3] /* major */, rawValues[4] /* minor */);
343                     } else {
344                         mDozeLog.d("udfpsLongPress - Not sending aodInterrupt. "
345                                 + "Unsupported doze state.");
346                     }
347                     requestPulse(DozeLog.REASON_SENSOR_UDFPS_LONG_PRESS, true, null);
348                 } else {
349                     mDozeHost.extendPulse(pulseReason);
350                 }
351             }, true /* alreadyPerformedProxCheck */, pulseReason);
352         }
353 
354         if (isPickup && !shouldDropPickupEvent()) {
355             final long timeSinceNotification =
356                     SystemClock.elapsedRealtime() - mNotificationPulseTime;
357             final boolean withinVibrationThreshold =
358                     timeSinceNotification < mDozeParameters.getPickupVibrationThreshold();
359             mDozeLog.tracePickupWakeUp(withinVibrationThreshold);
360         }
361     }
362 
363     private boolean shouldDropPickupEvent() {
364         return mKeyguardStateController.isOccluded();
365     }
366 
367     private void gentleWakeUp(@DozeLog.Reason int reason) {
368         // Log screen wake up reason (lift/pickup, tap, double-tap)
369         Optional.ofNullable(DozingUpdateUiEvent.fromReason(reason))
370                 .ifPresent(uiEventEnum -> mUiEventLogger.log(uiEventEnum, getKeyguardSessionId()));
371         if (mDozeParameters.getDisplayNeedsBlanking()) {
372             // Let's prepare the display to wake-up by drawing black.
373             // This will cover the hardware wake-up sequence, where the display
374             // becomes black for a few frames.
375             mDozeHost.setAodDimmingScrim(1f);
376         }
377         mMachine.wakeUp(reason);
378     }
379 
onProximityFar(boolean far)380     private void onProximityFar(boolean far) {
381         // Proximity checks are asynchronous and the user might have interacted with the phone
382         // when a new event is arriving. This means that a state transition might have happened
383         // and the proximity check is now obsolete.
384         if (mMachine.isExecutingTransition()) {
385             mDozeLog.d("onProximityFar called during transition. Ignoring sensor response.");
386             return;
387         }
388 
389         final boolean near = !far;
390         final DozeMachine.State state = mMachine.getState();
391         final boolean paused = (state == DozeMachine.State.DOZE_AOD_PAUSED);
392         final boolean pausing = (state == DozeMachine.State.DOZE_AOD_PAUSING);
393         final boolean aod = (state == DozeMachine.State.DOZE_AOD);
394 
395         if (state == DozeMachine.State.DOZE_PULSING
396                 || state == DozeMachine.State.DOZE_PULSING_BRIGHT) {
397             mDozeLog.traceSetIgnoreTouchWhilePulsing(near);
398             mDozeHost.onIgnoreTouchWhilePulsing(near);
399         }
400 
401         if (far && (paused || pausing)) {
402             mDozeLog.d("Prox FAR, unpausing AOD");
403             mMachine.requestState(DozeMachine.State.DOZE_AOD);
404         } else if (near && aod) {
405             mDozeLog.d("Prox NEAR, starting pausing AOD countdown");
406             mMachine.requestState(DozeMachine.State.DOZE_AOD_PAUSING);
407         }
408     }
409 
410     /**
411      * When a wake screen event is received from a sensor
412      * @param wake {@code true} when it's time to wake up, {@code false} when we should sleep.
413      * @param state The current state, or null if the state could not be determined due to enqueued
414      *              transitions.
415      */
onWakeScreen(boolean wake, @Nullable DozeMachine.State state, int reason)416     private void onWakeScreen(boolean wake, @Nullable DozeMachine.State state, int reason) {
417         mDozeLog.traceWakeDisplay(wake, reason);
418         sWakeDisplaySensorState = wake;
419 
420         if (wake) {
421             proximityCheckThenCall((isNear) -> {
422                 if (isNear != null && isNear) {
423                     // In pocket, drop event.
424                     return;
425                 }
426                 if (state == DozeMachine.State.DOZE) {
427                     mMachine.requestState(DozeMachine.State.DOZE_AOD);
428                     // Log sensor triggered
429                     Optional.ofNullable(DozingUpdateUiEvent.fromReason(reason))
430                             .ifPresent(uiEventEnum ->
431                                     mUiEventLogger.log(uiEventEnum, getKeyguardSessionId()));
432                 }
433             }, false /* alreadyPerformedProxCheck */, reason);
434         } else {
435             boolean paused = (state == DozeMachine.State.DOZE_AOD_PAUSED);
436             boolean pausing = (state == DozeMachine.State.DOZE_AOD_PAUSING);
437 
438             if (!pausing && !paused) {
439                 mMachine.requestState(DozeMachine.State.DOZE);
440                 // log wake timeout
441                 mUiEventLogger.log(DozingUpdateUiEvent.DOZING_UPDATE_WAKE_TIMEOUT);
442             }
443         }
444     }
445 
446     @Override
transitionTo(DozeMachine.State oldState, DozeMachine.State newState)447     public void transitionTo(DozeMachine.State oldState, DozeMachine.State newState) {
448         if (oldState == DOZE_SUSPEND_TRIGGERS && (newState != FINISH
449                 && newState != UNINITIALIZED)) {
450             // Register callbacks that were unregistered when we switched to
451             // DOZE_SUSPEND_TRIGGERS state.
452             registerCallbacks();
453         }
454         switch (newState) {
455             case INITIALIZED:
456                 mAodInterruptRunnable = null;
457                 sWakeDisplaySensorState = true;
458                 registerCallbacks();
459                 mDozeSensors.requestTemporaryDisable();
460                 break;
461             case DOZE:
462                 mAodInterruptRunnable = null;
463                 mWantProxSensor = false;
464                 mWantSensors = true;
465                 mWantTouchScreenSensors = true;
466                 mInAod = false;
467                 break;
468             case DOZE_AOD:
469                 mAodInterruptRunnable = null;
470                 mWantProxSensor = true;
471                 mWantSensors = true;
472                 mWantTouchScreenSensors = true;
473                 mInAod = true;
474                 if (!sWakeDisplaySensorState) {
475                     onWakeScreen(false, newState, DozeLog.REASON_SENSOR_WAKE_UP_PRESENCE);
476                 }
477                 break;
478             case DOZE_AOD_PAUSED:
479             case DOZE_AOD_PAUSING:
480                 mWantProxSensor = true;
481                 break;
482             case DOZE_PULSING:
483             case DOZE_PULSING_BRIGHT:
484                 mWantProxSensor = true;
485                 mWantTouchScreenSensors = false;
486                 break;
487             case DOZE_AOD_DOCKED:
488                 mWantProxSensor = false;
489                 mWantTouchScreenSensors = false;
490                 break;
491             case DOZE_PULSE_DONE:
492                 mDozeSensors.requestTemporaryDisable();
493                 break;
494             case DOZE_SUSPEND_TRIGGERS:
495             case FINISH:
496                 stopListeningToAllTriggers();
497                 break;
498             default:
499         }
500         mDozeSensors.setListening(mWantSensors, mWantTouchScreenSensors, mInAod);
501     }
502 
registerCallbacks()503     private void registerCallbacks() {
504         mBroadcastReceiver.register(mBroadcastDispatcher);
505         mDockManager.addListener(mDockEventListener);
506         mDozeHost.addCallback(mHostCallback);
507         mUserTracker.addCallback(mUserChangedCallback, mContext.getMainExecutor());
508     }
509 
unregisterCallbacks()510     private void unregisterCallbacks() {
511         mBroadcastReceiver.unregister(mBroadcastDispatcher);
512         mDozeHost.removeCallback(mHostCallback);
513         mDockManager.removeListener(mDockEventListener);
514         mUserTracker.removeCallback(mUserChangedCallback);
515     }
516 
stopListeningToAllTriggers()517     private void stopListeningToAllTriggers() {
518         unregisterCallbacks();
519         mDozeSensors.setListening(false, false, false);
520         mDozeSensors.setProxListening(false);
521         mWantSensors = false;
522         mWantProxSensor = false;
523         mWantTouchScreenSensors = false;
524         mInAod = false;
525     }
526 
527     @Override
onScreenState(int state)528     public void onScreenState(int state) {
529         mDozeSensors.onScreenState(state);
530         final boolean lowPowerStateOrOff = state == Display.STATE_DOZE
531                 || state == Display.STATE_DOZE_SUSPEND || state == Display.STATE_OFF;
532         mDozeSensors.setProxListening(mWantProxSensor && lowPowerStateOrOff);
533         mDozeSensors.setListeningWithPowerState(mWantSensors, mWantTouchScreenSensors,
534                 mInAod, lowPowerStateOrOff);
535 
536         if (mAodInterruptRunnable != null && state == Display.STATE_ON) {
537             mAodInterruptRunnable.run();
538             mAodInterruptRunnable = null;
539         }
540     }
541 
requestPulse(final int reason, boolean performedProxCheck, Runnable onPulseSuppressedListener)542     private void requestPulse(final int reason, boolean performedProxCheck,
543             Runnable onPulseSuppressedListener) {
544         Assert.isMainThread();
545         mDozeHost.extendPulse(reason);
546 
547         // we can't determine the dozing state if we're currently transitioning
548         final DozeMachine.State dozeState =
549                 mMachine.isExecutingTransition() ? null : mMachine.getState();
550 
551         // When already pulsing we're allowed to show the wallpaper directly without
552         // requesting a new pulse.
553         if (dozeState == DozeMachine.State.DOZE_PULSING
554                 && reason == DozeLog.PULSE_REASON_SENSOR_WAKE_REACH) {
555             mMachine.requestState(DozeMachine.State.DOZE_PULSING_BRIGHT);
556             return;
557         }
558 
559         if (!mAllowPulseTriggers || mDozeHost.isPulsePending()
560                 || !canPulse(dozeState, performedProxCheck)) {
561             if (!mAllowPulseTriggers) {
562                 mDozeLog.tracePulseDropped("requestPulse - !mAllowPulseTriggers");
563             } else if (mDozeHost.isPulsePending()) {
564                 mDozeLog.tracePulseDropped("requestPulse - pulsePending");
565             } else if (!canPulse(dozeState, performedProxCheck)) {
566                 mDozeLog.tracePulseDropped("requestPulse - dozeState cannot pulse", dozeState);
567             }
568             runIfNotNull(onPulseSuppressedListener);
569             return;
570         }
571 
572         mDozeHost.setPulsePending(true);
573         proximityCheckThenCall((isNear) -> {
574             if (isNear != null && isNear) {
575                 // in pocket, abort pulse
576                 mDozeLog.tracePulseDropped("requestPulse - inPocket");
577                 mDozeHost.setPulsePending(false);
578                 runIfNotNull(onPulseSuppressedListener);
579             } else {
580                 // not in pocket, continue pulsing
581                 final boolean isPulsePending = mDozeHost.isPulsePending();
582                 mDozeHost.setPulsePending(false);
583                 if (!isPulsePending || mDozeHost.isPulsingBlocked()
584                         || !canPulse(dozeState, performedProxCheck)) {
585                     if (!isPulsePending) {
586                         mDozeLog.tracePulseDropped("continuePulseRequest - pulse no longer"
587                                 + " pending, pulse was cancelled before it could start"
588                                 + " transitioning to pulsing state.");
589                     } else if (mDozeHost.isPulsingBlocked()) {
590                         mDozeLog.tracePulseDropped("continuePulseRequest - pulsingBlocked");
591                     } else if (!canPulse(dozeState, performedProxCheck)) {
592                         mDozeLog.tracePulseDropped("continuePulseRequest"
593                                 + " - doze state cannot pulse", dozeState);
594                     }
595                     runIfNotNull(onPulseSuppressedListener);
596                     return;
597                 }
598 
599                 mMachine.requestPulse(reason);
600             }
601         }, !mDozeParameters.getProxCheckBeforePulse() || performedProxCheck, reason);
602 
603         // Logs request pulse reason on AOD screen.
604         Optional.ofNullable(DozingUpdateUiEvent.fromReason(reason))
605                 .ifPresent(uiEventEnum -> mUiEventLogger.log(uiEventEnum, getKeyguardSessionId()));
606     }
607 
canPulse(DozeMachine.State dozeState, boolean pulsePerformedProximityCheck)608     private boolean canPulse(DozeMachine.State dozeState, boolean pulsePerformedProximityCheck) {
609         final boolean dozePausedOrPausing = dozeState == State.DOZE_AOD_PAUSED
610                 || dozeState == State.DOZE_AOD_PAUSING;
611         return dozeState == DozeMachine.State.DOZE
612                 || dozeState == DozeMachine.State.DOZE_AOD
613                 || dozeState == DozeMachine.State.DOZE_AOD_DOCKED
614                 || (dozePausedOrPausing && pulsePerformedProximityCheck);
615     }
616 
617     @Nullable
getKeyguardSessionId()618     private InstanceId getKeyguardSessionId() {
619         return mSessionTracker.getSessionId(SESSION_KEYGUARD);
620     }
621 
622     @Override
dump(PrintWriter pw)623     public void dump(PrintWriter pw) {
624         pw.println(" mAodInterruptRunnable=" + mAodInterruptRunnable);
625 
626         pw.print(" notificationPulseTime=");
627         pw.println(Formatter.formatShortElapsedTime(mContext, mNotificationPulseTime));
628 
629         pw.println(" DozeHost#isPulsePending=" + mDozeHost.isPulsePending());
630         pw.println("DozeSensors:");
631         IndentingPrintWriter idpw = new IndentingPrintWriter(pw);
632         idpw.increaseIndent();
633         mDozeSensors.dump(idpw);
634     }
635 
636     private class TriggerReceiver extends BroadcastReceiver {
637         private boolean mRegistered;
638 
639         @Override
onReceive(Context context, Intent intent)640         public void onReceive(Context context, Intent intent) {
641             if (PULSE_ACTION.equals(intent.getAction())) {
642                 if (DozeMachine.DEBUG) Log.d(TAG, "Received pulse intent");
643                 requestPulse(DozeLog.PULSE_REASON_INTENT, false, /* performedProxCheck */
644                         null /* onPulseSuppressedListener */);
645             }
646         }
647 
register(BroadcastDispatcher broadcastDispatcher)648         public void register(BroadcastDispatcher broadcastDispatcher) {
649             if (mRegistered) {
650                 return;
651             }
652             IntentFilter filter = new IntentFilter(PULSE_ACTION);
653             broadcastDispatcher.registerReceiver(this, filter);
654             mRegistered = true;
655         }
656 
unregister(BroadcastDispatcher broadcastDispatcher)657         public void unregister(BroadcastDispatcher broadcastDispatcher) {
658             if (!mRegistered) {
659                 return;
660             }
661             broadcastDispatcher.unregisterReceiver(this);
662             mRegistered = false;
663         }
664     }
665 
666     private class DockEventListener implements DockManager.DockEventListener {
667         @Override
onEvent(int event)668         public void onEvent(int event) {
669             if (DEBUG) Log.d(TAG, "dock event = " + event);
670             switch (event) {
671                 case DockManager.STATE_DOCKED:
672                 case DockManager.STATE_DOCKED_HIDE:
673                     mDozeSensors.ignoreTouchScreenSensorsSettingInterferingWithDocking(true);
674                     break;
675                 case DockManager.STATE_NONE:
676                     mDozeSensors.ignoreTouchScreenSensorsSettingInterferingWithDocking(false);
677                     break;
678                 default:
679                     // no-op
680             }
681         }
682     }
683 
684     private DozeHost.Callback mHostCallback = new DozeHost.Callback() {
685         @Override
686         public void onNotificationAlerted(Runnable onPulseSuppressedListener) {
687             onNotification(onPulseSuppressedListener);
688         }
689     };
690 }
691