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