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