blob: 40603ab24c1e2e1a54a555d9562f8f00818a77c2 [file] [log] [blame]
Adrian Roosff2c4562016-11-03 12:13:36 -07001/*
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
17package com.android.systemui.doze;
18
19import android.annotation.MainThread;
Issei Suzukica19e6e2019-02-26 12:39:11 +010020import android.hardware.display.AmbientDisplayConfiguration;
Adrian Roosa6c03f82017-07-26 16:20:30 +020021import android.os.Trace;
Adrian Roos0261fb22017-03-07 20:20:35 +000022import android.os.UserHandle;
Adrian Roosff2c4562016-11-03 12:13:36 -070023import android.util.Log;
24import android.view.Display;
25
26import com.android.internal.util.Preconditions;
Jerry Changae4dc4b2019-10-16 18:45:03 +080027import com.android.systemui.dock.DockManager;
Lucas Dupin737b8512019-08-09 10:35:34 -070028import com.android.systemui.keyguard.WakefulnessLifecycle;
29import com.android.systemui.keyguard.WakefulnessLifecycle.Wakefulness;
Lucas Dupin12663d32018-02-08 11:42:22 -080030import com.android.systemui.statusbar.phone.DozeParameters;
Lucas Dupinbd7366d2019-09-25 13:39:21 -070031import com.android.systemui.statusbar.policy.BatteryController;
Adrian Roosff2c4562016-11-03 12:13:36 -070032import com.android.systemui.util.Assert;
Adrian Roosc1b50322017-02-27 21:07:58 +010033import com.android.systemui.util.wakelock.WakeLock;
Adrian Roosff2c4562016-11-03 12:13:36 -070034
35import java.io.PrintWriter;
36import java.util.ArrayList;
37
38/**
39 * Orchestrates all things doze.
40 *
41 * DozeMachine implements a state machine that orchestrates how the UI and triggers work and
42 * interfaces with the power and screen states.
43 *
44 * During state transitions and in certain states, DozeMachine holds a wake lock.
45 */
46public class DozeMachine {
47
48 static final String TAG = "DozeMachine";
49 static final boolean DEBUG = DozeService.DEBUG;
Beverlycc4a62f2019-09-26 14:55:28 -040050 private final DozeLog mDozeLog;
Lucas Dupinee4c9b72019-02-18 17:04:58 -080051 private static final String REASON_CHANGE_STATE = "DozeMachine#requestState";
52 private static final String REASON_HELD_FOR_STATE = "DozeMachine#heldForState";
Adrian Roosff2c4562016-11-03 12:13:36 -070053
Adrian Roosd35d4ca2017-04-19 14:31:03 -070054 public enum State {
Adrian Roosff2c4562016-11-03 12:13:36 -070055 /** Default state. Transition to INITIALIZED to get Doze going. */
56 UNINITIALIZED,
57 /** Doze components are set up. Followed by transition to DOZE or DOZE_AOD. */
58 INITIALIZED,
59 /** Regular doze. Device is asleep and listening for pulse triggers. */
60 DOZE,
61 /** Always-on doze. Device is asleep, showing UI and listening for pulse triggers. */
62 DOZE_AOD,
63 /** Pulse has been requested. Device is awake and preparing UI */
64 DOZE_REQUEST_PULSE,
65 /** Pulse is showing. Device is awake and showing UI. */
66 DOZE_PULSING,
Lucas Dupin5f00fa52019-03-27 22:46:53 -070067 /** Pulse is showing with bright wallpaper. Device is awake and showing UI. */
68 DOZE_PULSING_BRIGHT,
Adrian Roosff2c4562016-11-03 12:13:36 -070069 /** Pulse is done showing. Followed by transition to DOZE or DOZE_AOD. */
70 DOZE_PULSE_DONE,
71 /** Doze is done. DozeService is finished. */
Adrian Roos67cca742017-04-13 16:52:51 -070072 FINISH,
73 /** AOD, but the display is temporarily off. */
Adrian Roos6023ccb2017-06-28 16:22:02 +020074 DOZE_AOD_PAUSED,
75 /** AOD, prox is near, transitions to DOZE_AOD_PAUSED after a timeout. */
Jerry Changae4dc4b2019-10-16 18:45:03 +080076 DOZE_AOD_PAUSING,
77 /** Always-on doze. Device is awake, showing docking UI and listening for pulse triggers. */
78 DOZE_AOD_DOCKED;
Adrian Roosb84dc182016-12-02 09:01:09 -080079
80 boolean canPulse() {
81 switch (this) {
82 case DOZE:
83 case DOZE_AOD:
Adrian Roos67cca742017-04-13 16:52:51 -070084 case DOZE_AOD_PAUSED:
Adrian Roos6023ccb2017-06-28 16:22:02 +020085 case DOZE_AOD_PAUSING:
Jerry Changae4dc4b2019-10-16 18:45:03 +080086 case DOZE_AOD_DOCKED:
Adrian Roosb84dc182016-12-02 09:01:09 -080087 return true;
88 default:
89 return false;
90 }
91 }
92
93 boolean staysAwake() {
94 switch (this) {
95 case DOZE_REQUEST_PULSE:
96 case DOZE_PULSING:
Lucas Dupin5f00fa52019-03-27 22:46:53 -070097 case DOZE_PULSING_BRIGHT:
Jerry Changae4dc4b2019-10-16 18:45:03 +080098 case DOZE_AOD_DOCKED:
Adrian Roosb84dc182016-12-02 09:01:09 -080099 return true;
100 default:
101 return false;
102 }
103 }
104
Lucas Dupin12663d32018-02-08 11:42:22 -0800105 int screenState(DozeParameters parameters) {
Adrian Roosb84dc182016-12-02 09:01:09 -0800106 switch (this) {
107 case UNINITIALIZED:
108 case INITIALIZED:
Lucas Dupin16cfe452018-02-08 13:14:50 -0800109 case DOZE_REQUEST_PULSE:
110 return parameters.shouldControlScreenOff() ? Display.STATE_ON
111 : Display.STATE_OFF;
Adrian Roos67cca742017-04-13 16:52:51 -0700112 case DOZE_AOD_PAUSED:
Lucas Dupin16cfe452018-02-08 13:14:50 -0800113 case DOZE:
Adrian Roosb84dc182016-12-02 09:01:09 -0800114 return Display.STATE_OFF;
115 case DOZE_PULSING:
Lucas Dupin5f00fa52019-03-27 22:46:53 -0700116 case DOZE_PULSING_BRIGHT:
Jerry Changae4dc4b2019-10-16 18:45:03 +0800117 case DOZE_AOD_DOCKED:
Adrian Roos2324d852017-04-27 15:50:14 -0700118 return Display.STATE_ON;
Adrian Roosb84dc182016-12-02 09:01:09 -0800119 case DOZE_AOD:
Adrian Roos6023ccb2017-06-28 16:22:02 +0200120 case DOZE_AOD_PAUSING:
Adrian Roosa1e6b312017-03-28 16:20:34 -0700121 return Display.STATE_DOZE_SUSPEND;
Adrian Roosb84dc182016-12-02 09:01:09 -0800122 default:
123 return Display.STATE_UNKNOWN;
124 }
125 }
Adrian Roosff2c4562016-11-03 12:13:36 -0700126 }
127
128 private final Service mDozeService;
Adrian Roosc1b50322017-02-27 21:07:58 +0100129 private final WakeLock mWakeLock;
Adrian Roos0261fb22017-03-07 20:20:35 +0000130 private final AmbientDisplayConfiguration mConfig;
Lucas Dupin737b8512019-08-09 10:35:34 -0700131 private final WakefulnessLifecycle mWakefulnessLifecycle;
Lucas Dupinbd7366d2019-09-25 13:39:21 -0700132 private final BatteryController mBatteryController;
Adrian Roosff2c4562016-11-03 12:13:36 -0700133 private Part[] mParts;
134
135 private final ArrayList<State> mQueuedRequests = new ArrayList<>();
136 private State mState = State.UNINITIALIZED;
Adrian Roosd7b9d102017-04-28 15:42:58 -0700137 private int mPulseReason;
Adrian Roosff2c4562016-11-03 12:13:36 -0700138 private boolean mWakeLockHeldForCurrentState = false;
Jerry Changae4dc4b2019-10-16 18:45:03 +0800139 private DockManager mDockManager;
Adrian Roosff2c4562016-11-03 12:13:36 -0700140
Jerry Changae4dc4b2019-10-16 18:45:03 +0800141 public DozeMachine(Service service, AmbientDisplayConfiguration config, WakeLock wakeLock,
142 WakefulnessLifecycle wakefulnessLifecycle, BatteryController batteryController,
143 DozeLog dozeLog, DockManager dockManager) {
Adrian Roosff2c4562016-11-03 12:13:36 -0700144 mDozeService = service;
Adrian Roos0261fb22017-03-07 20:20:35 +0000145 mConfig = config;
Lucas Dupin737b8512019-08-09 10:35:34 -0700146 mWakefulnessLifecycle = wakefulnessLifecycle;
Adrian Roosff2c4562016-11-03 12:13:36 -0700147 mWakeLock = wakeLock;
Lucas Dupinbd7366d2019-09-25 13:39:21 -0700148 mBatteryController = batteryController;
Beverlycc4a62f2019-09-26 14:55:28 -0400149 mDozeLog = dozeLog;
Jerry Changae4dc4b2019-10-16 18:45:03 +0800150 mDockManager = dockManager;
Adrian Roosff2c4562016-11-03 12:13:36 -0700151 }
152
153 /** Initializes the set of {@link Part}s. Must be called exactly once after construction. */
154 public void setParts(Part[] parts) {
155 Preconditions.checkState(mParts == null);
156 mParts = parts;
157 }
158
159 /**
160 * Requests transitioning to {@code requestedState}.
161 *
162 * This can be called during a state transition, in which case it will be queued until all
163 * queued state transitions are done.
164 *
165 * A wake lock is held while the transition is happening.
166 *
167 * Note that {@link #transitionPolicy} can modify what state will be transitioned to.
168 */
169 @MainThread
170 public void requestState(State requestedState) {
Adrian Roosd7b9d102017-04-28 15:42:58 -0700171 Preconditions.checkArgument(requestedState != State.DOZE_REQUEST_PULSE);
Beverlycc4a62f2019-09-26 14:55:28 -0400172 requestState(requestedState, DozeEvent.PULSE_REASON_NONE);
Adrian Roosd7b9d102017-04-28 15:42:58 -0700173 }
174
175 @MainThread
176 public void requestPulse(int pulseReason) {
177 // Must not be called during a transition. There's no inherent problem with that,
178 // but there's currently no need to execute from a transition and it simplifies the
179 // code to not have to worry about keeping the pulseReason in mQueuedRequests.
180 Preconditions.checkState(!isExecutingTransition());
181 requestState(State.DOZE_REQUEST_PULSE, pulseReason);
182 }
183
184 private void requestState(State requestedState, int pulseReason) {
Adrian Roosff2c4562016-11-03 12:13:36 -0700185 Assert.isMainThread();
186 if (DEBUG) {
187 Log.i(TAG, "request: current=" + mState + " req=" + requestedState,
188 new Throwable("here"));
189 }
190
191 boolean runNow = !isExecutingTransition();
192 mQueuedRequests.add(requestedState);
193 if (runNow) {
Lucas Dupinee4c9b72019-02-18 17:04:58 -0800194 mWakeLock.acquire(REASON_CHANGE_STATE);
Adrian Roosff2c4562016-11-03 12:13:36 -0700195 for (int i = 0; i < mQueuedRequests.size(); i++) {
196 // Transitions in Parts can call back into requestState, which will
197 // cause mQueuedRequests to grow.
Adrian Roosd7b9d102017-04-28 15:42:58 -0700198 transitionTo(mQueuedRequests.get(i), pulseReason);
Adrian Roosff2c4562016-11-03 12:13:36 -0700199 }
200 mQueuedRequests.clear();
Lucas Dupinee4c9b72019-02-18 17:04:58 -0800201 mWakeLock.release(REASON_CHANGE_STATE);
Adrian Roosff2c4562016-11-03 12:13:36 -0700202 }
203 }
204
205 /**
206 * @return the current state.
207 *
208 * This must not be called during a transition.
209 */
210 @MainThread
211 public State getState() {
212 Assert.isMainThread();
Lucas Dupinb7fd1eb2019-03-28 12:05:17 -0700213 if (isExecutingTransition()) {
214 throw new IllegalStateException("Cannot get state because there were pending "
215 + "transitions: " + mQueuedRequests.toString());
216 }
Adrian Roosff2c4562016-11-03 12:13:36 -0700217 return mState;
218 }
219
Adrian Roosd7b9d102017-04-28 15:42:58 -0700220 /**
221 * @return the current pulse reason.
222 *
223 * This is only valid if the machine is currently in one of the pulse states.
224 */
225 @MainThread
226 public int getPulseReason() {
227 Assert.isMainThread();
228 Preconditions.checkState(mState == State.DOZE_REQUEST_PULSE
229 || mState == State.DOZE_PULSING
Lucas Dupin5f00fa52019-03-27 22:46:53 -0700230 || mState == State.DOZE_PULSING_BRIGHT
Adrian Roosd7b9d102017-04-28 15:42:58 -0700231 || mState == State.DOZE_PULSE_DONE, "must be in pulsing state, but is " + mState);
232 return mPulseReason;
233 }
234
Adrian Roos4fb1f512017-02-14 14:01:32 +0100235 /** Requests the PowerManager to wake up now. */
236 public void wakeUp() {
237 mDozeService.requestWakeUp();
238 }
239
Lucas Dupin1946b312019-03-21 14:48:23 -0700240 public boolean isExecutingTransition() {
Adrian Roosff2c4562016-11-03 12:13:36 -0700241 return !mQueuedRequests.isEmpty();
242 }
243
Adrian Roosd7b9d102017-04-28 15:42:58 -0700244 private void transitionTo(State requestedState, int pulseReason) {
Adrian Roosff2c4562016-11-03 12:13:36 -0700245 State newState = transitionPolicy(requestedState);
246
247 if (DEBUG) {
248 Log.i(TAG, "transition: old=" + mState + " req=" + requestedState + " new=" + newState);
249 }
250
251 if (newState == mState) {
252 return;
253 }
254
255 validateTransition(newState);
256
257 State oldState = mState;
258 mState = newState;
259
Beverlycc4a62f2019-09-26 14:55:28 -0400260 mDozeLog.traceState(newState);
Adrian Roosa6c03f82017-07-26 16:20:30 +0200261 Trace.traceCounter(Trace.TRACE_TAG_APP, "doze_machine_state", newState.ordinal());
262
Adrian Roosd7b9d102017-04-28 15:42:58 -0700263 updatePulseReason(newState, oldState, pulseReason);
Adrian Roosff2c4562016-11-03 12:13:36 -0700264 performTransitionOnComponents(oldState, newState);
Adrian Roosff2c4562016-11-03 12:13:36 -0700265 updateWakeLockState(newState);
266
267 resolveIntermediateState(newState);
268 }
269
Adrian Roosd7b9d102017-04-28 15:42:58 -0700270 private void updatePulseReason(State newState, State oldState, int pulseReason) {
271 if (newState == State.DOZE_REQUEST_PULSE) {
272 mPulseReason = pulseReason;
273 } else if (oldState == State.DOZE_PULSE_DONE) {
Beverlycc4a62f2019-09-26 14:55:28 -0400274 mPulseReason = DozeEvent.PULSE_REASON_NONE;
Adrian Roosd7b9d102017-04-28 15:42:58 -0700275 }
276 }
277
Adrian Roosff2c4562016-11-03 12:13:36 -0700278 private void performTransitionOnComponents(State oldState, State newState) {
279 for (Part p : mParts) {
280 p.transitionTo(oldState, newState);
281 }
282
283 switch (newState) {
284 case FINISH:
285 mDozeService.finish();
286 break;
287 default:
288 }
289 }
290
291 private void validateTransition(State newState) {
Adrian Roosb84dc182016-12-02 09:01:09 -0800292 try {
293 switch (mState) {
294 case FINISH:
295 Preconditions.checkState(newState == State.FINISH);
296 break;
297 case UNINITIALIZED:
298 Preconditions.checkState(newState == State.INITIALIZED);
299 break;
300 }
301 switch (newState) {
302 case UNINITIALIZED:
303 throw new IllegalArgumentException("can't transition to UNINITIALIZED");
304 case INITIALIZED:
305 Preconditions.checkState(mState == State.UNINITIALIZED);
306 break;
307 case DOZE_PULSING:
308 Preconditions.checkState(mState == State.DOZE_REQUEST_PULSE);
309 break;
310 case DOZE_PULSE_DONE:
Adrian Rooscd139a62016-12-16 12:23:51 -0800311 Preconditions.checkState(
Lucas Dupin5f00fa52019-03-27 22:46:53 -0700312 mState == State.DOZE_REQUEST_PULSE || mState == State.DOZE_PULSING
313 || mState == State.DOZE_PULSING_BRIGHT);
Adrian Roosb84dc182016-12-02 09:01:09 -0800314 break;
315 default:
316 break;
317 }
318 } catch (RuntimeException e) {
319 throw new IllegalStateException("Illegal Transition: " + mState + " -> " + newState, e);
Adrian Roosff2c4562016-11-03 12:13:36 -0700320 }
321 }
322
323 private State transitionPolicy(State requestedState) {
324 if (mState == State.FINISH) {
325 return State.FINISH;
326 }
Adrian Roos6023ccb2017-06-28 16:22:02 +0200327 if ((mState == State.DOZE_AOD_PAUSED || mState == State.DOZE_AOD_PAUSING
328 || mState == State.DOZE_AOD || mState == State.DOZE)
Adrian Roos67cca742017-04-13 16:52:51 -0700329 && requestedState == State.DOZE_PULSE_DONE) {
330 Log.i(TAG, "Dropping pulse done because current state is already done: " + mState);
331 return mState;
332 }
Lucas Dupinbd7366d2019-09-25 13:39:21 -0700333 if (requestedState == State.DOZE_AOD && mBatteryController.isAodPowerSave()) {
334 return State.DOZE;
335 }
Adrian Roosb84dc182016-12-02 09:01:09 -0800336 if (requestedState == State.DOZE_REQUEST_PULSE && !mState.canPulse()) {
337 Log.i(TAG, "Dropping pulse request because current state can't pulse: " + mState);
338 return mState;
339 }
Adrian Roosff2c4562016-11-03 12:13:36 -0700340 return requestedState;
341 }
342
343 private void updateWakeLockState(State newState) {
Adrian Roosb84dc182016-12-02 09:01:09 -0800344 boolean staysAwake = newState.staysAwake();
345 if (mWakeLockHeldForCurrentState && !staysAwake) {
Lucas Dupinee4c9b72019-02-18 17:04:58 -0800346 mWakeLock.release(REASON_HELD_FOR_STATE);
Adrian Roosae0c5e82016-11-16 19:56:19 -0800347 mWakeLockHeldForCurrentState = false;
Adrian Roosb84dc182016-12-02 09:01:09 -0800348 } else if (!mWakeLockHeldForCurrentState && staysAwake) {
Lucas Dupinee4c9b72019-02-18 17:04:58 -0800349 mWakeLock.acquire(REASON_HELD_FOR_STATE);
Adrian Roosae0c5e82016-11-16 19:56:19 -0800350 mWakeLockHeldForCurrentState = true;
Adrian Roosff2c4562016-11-03 12:13:36 -0700351 }
352 }
353
Adrian Roosff2c4562016-11-03 12:13:36 -0700354 private void resolveIntermediateState(State state) {
355 switch (state) {
356 case INITIALIZED:
357 case DOZE_PULSE_DONE:
Lucas Dupin737b8512019-08-09 10:35:34 -0700358 final State nextState;
359 @Wakefulness int wakefulness = mWakefulnessLifecycle.getWakefulness();
360 if (wakefulness == WakefulnessLifecycle.WAKEFULNESS_AWAKE
361 || wakefulness == WakefulnessLifecycle.WAKEFULNESS_WAKING) {
362 nextState = State.FINISH;
Jerry Changae4dc4b2019-10-16 18:45:03 +0800363 } else if (mDockManager.isDocked()) {
364 nextState = mDockManager.isHidden() ? State.DOZE : State.DOZE_AOD_DOCKED;
Lucas Dupin737b8512019-08-09 10:35:34 -0700365 } else if (mConfig.alwaysOnEnabled(UserHandle.USER_CURRENT)) {
366 nextState = State.DOZE_AOD;
367 } else {
368 nextState = State.DOZE;
369 }
370
Beverlycc4a62f2019-09-26 14:55:28 -0400371 transitionTo(nextState, DozeEvent.PULSE_REASON_NONE);
Adrian Roosff2c4562016-11-03 12:13:36 -0700372 break;
373 default:
374 break;
375 }
376 }
377
378 /** Dumps the current state */
379 public void dump(PrintWriter pw) {
380 pw.print(" state="); pw.println(mState);
381 pw.print(" wakeLockHeldForCurrentState="); pw.println(mWakeLockHeldForCurrentState);
Lucas Dupinee4c9b72019-02-18 17:04:58 -0800382 pw.print(" wakeLock="); pw.println(mWakeLock);
Adrian Roosff2c4562016-11-03 12:13:36 -0700383 pw.println("Parts:");
384 for (Part p : mParts) {
385 p.dump(pw);
386 }
387 }
388
389 /** A part of the DozeMachine that needs to be notified about state changes. */
390 public interface Part {
391 /**
392 * Transition from {@code oldState} to {@code newState}.
393 *
394 * This method is guaranteed to only be called while a wake lock is held.
395 */
396 void transitionTo(State oldState, State newState);
397
398 /** Dump current state. For debugging only. */
399 default void dump(PrintWriter pw) {}
400 }
401
402 /** A wrapper interface for {@link android.service.dreams.DreamService} */
403 public interface Service {
404 /** Finish dreaming. */
405 void finish();
406
407 /** Request a display state. See {@link android.view.Display#STATE_DOZE}. */
408 void setDozeScreenState(int state);
Adrian Roos4fb1f512017-02-14 14:01:32 +0100409
410 /** Request waking up. */
411 void requestWakeUp();
Adrian Roos2981eb02017-05-26 18:40:09 -0700412
413 /** Set screen brightness */
414 void setDozeScreenBrightness(int brightness);
415
416 class Delegate implements Service {
417 private final Service mDelegate;
418
419 public Delegate(Service delegate) {
420 mDelegate = delegate;
421 }
422
423 @Override
424 public void finish() {
425 mDelegate.finish();
426 }
427
428 @Override
429 public void setDozeScreenState(int state) {
430 mDelegate.setDozeScreenState(state);
431 }
432
433 @Override
434 public void requestWakeUp() {
435 mDelegate.requestWakeUp();
436 }
437
438 @Override
439 public void setDozeScreenBrightness(int brightness) {
440 mDelegate.setDozeScreenBrightness(brightness);
441 }
442 }
Adrian Roosff2c4562016-11-03 12:13:36 -0700443 }
444}