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