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.MainThread; 20 import android.os.Trace; 21 import android.os.UserHandle; 22 import android.util.Log; 23 import android.view.Display; 24 25 import com.android.internal.hardware.AmbientDisplayConfiguration; 26 import com.android.internal.util.Preconditions; 27 import com.android.systemui.statusbar.phone.DozeParameters; 28 import com.android.systemui.util.Assert; 29 import com.android.systemui.util.wakelock.WakeLock; 30 31 import java.io.PrintWriter; 32 import java.util.ArrayList; 33 34 /** 35 * Orchestrates all things doze. 36 * 37 * DozeMachine implements a state machine that orchestrates how the UI and triggers work and 38 * interfaces with the power and screen states. 39 * 40 * During state transitions and in certain states, DozeMachine holds a wake lock. 41 */ 42 public class DozeMachine { 43 44 static final String TAG = "DozeMachine"; 45 static final boolean DEBUG = DozeService.DEBUG; 46 47 public enum State { 48 /** Default state. Transition to INITIALIZED to get Doze going. */ 49 UNINITIALIZED, 50 /** Doze components are set up. Followed by transition to DOZE or DOZE_AOD. */ 51 INITIALIZED, 52 /** Regular doze. Device is asleep and listening for pulse triggers. */ 53 DOZE, 54 /** Always-on doze. Device is asleep, showing UI and listening for pulse triggers. */ 55 DOZE_AOD, 56 /** Pulse has been requested. Device is awake and preparing UI */ 57 DOZE_REQUEST_PULSE, 58 /** Pulse is showing. Device is awake and showing UI. */ 59 DOZE_PULSING, 60 /** Pulse is done showing. Followed by transition to DOZE or DOZE_AOD. */ 61 DOZE_PULSE_DONE, 62 /** Doze is done. DozeService is finished. */ 63 FINISH, 64 /** AOD, but the display is temporarily off. */ 65 DOZE_AOD_PAUSED, 66 /** AOD, prox is near, transitions to DOZE_AOD_PAUSED after a timeout. */ 67 DOZE_AOD_PAUSING; 68 canPulse()69 boolean canPulse() { 70 switch (this) { 71 case DOZE: 72 case DOZE_AOD: 73 case DOZE_AOD_PAUSED: 74 case DOZE_AOD_PAUSING: 75 return true; 76 default: 77 return false; 78 } 79 } 80 staysAwake()81 boolean staysAwake() { 82 switch (this) { 83 case DOZE_REQUEST_PULSE: 84 case DOZE_PULSING: 85 return true; 86 default: 87 return false; 88 } 89 } 90 screenState(DozeParameters parameters)91 int screenState(DozeParameters parameters) { 92 switch (this) { 93 case UNINITIALIZED: 94 case INITIALIZED: 95 case DOZE_REQUEST_PULSE: 96 return parameters.shouldControlScreenOff() ? Display.STATE_ON 97 : Display.STATE_OFF; 98 case DOZE_AOD_PAUSED: 99 case DOZE: 100 return Display.STATE_OFF; 101 case DOZE_PULSING: 102 return Display.STATE_ON; 103 case DOZE_AOD: 104 case DOZE_AOD_PAUSING: 105 return Display.STATE_DOZE_SUSPEND; 106 default: 107 return Display.STATE_UNKNOWN; 108 } 109 } 110 } 111 112 private final Service mDozeService; 113 private final WakeLock mWakeLock; 114 private final AmbientDisplayConfiguration mConfig; 115 private Part[] mParts; 116 117 private final ArrayList<State> mQueuedRequests = new ArrayList<>(); 118 private State mState = State.UNINITIALIZED; 119 private int mPulseReason; 120 private boolean mWakeLockHeldForCurrentState = false; 121 DozeMachine(Service service, AmbientDisplayConfiguration config, WakeLock wakeLock)122 public DozeMachine(Service service, AmbientDisplayConfiguration config, 123 WakeLock wakeLock) { 124 mDozeService = service; 125 mConfig = config; 126 mWakeLock = wakeLock; 127 } 128 129 /** Initializes the set of {@link Part}s. Must be called exactly once after construction. */ setParts(Part[] parts)130 public void setParts(Part[] parts) { 131 Preconditions.checkState(mParts == null); 132 mParts = parts; 133 } 134 135 /** 136 * Requests transitioning to {@code requestedState}. 137 * 138 * This can be called during a state transition, in which case it will be queued until all 139 * queued state transitions are done. 140 * 141 * A wake lock is held while the transition is happening. 142 * 143 * Note that {@link #transitionPolicy} can modify what state will be transitioned to. 144 */ 145 @MainThread requestState(State requestedState)146 public void requestState(State requestedState) { 147 Preconditions.checkArgument(requestedState != State.DOZE_REQUEST_PULSE); 148 requestState(requestedState, DozeLog.PULSE_REASON_NONE); 149 } 150 151 @MainThread requestPulse(int pulseReason)152 public void requestPulse(int pulseReason) { 153 // Must not be called during a transition. There's no inherent problem with that, 154 // but there's currently no need to execute from a transition and it simplifies the 155 // code to not have to worry about keeping the pulseReason in mQueuedRequests. 156 Preconditions.checkState(!isExecutingTransition()); 157 requestState(State.DOZE_REQUEST_PULSE, pulseReason); 158 } 159 requestState(State requestedState, int pulseReason)160 private void requestState(State requestedState, int pulseReason) { 161 Assert.isMainThread(); 162 if (DEBUG) { 163 Log.i(TAG, "request: current=" + mState + " req=" + requestedState, 164 new Throwable("here")); 165 } 166 167 boolean runNow = !isExecutingTransition(); 168 mQueuedRequests.add(requestedState); 169 if (runNow) { 170 mWakeLock.acquire(); 171 for (int i = 0; i < mQueuedRequests.size(); i++) { 172 // Transitions in Parts can call back into requestState, which will 173 // cause mQueuedRequests to grow. 174 transitionTo(mQueuedRequests.get(i), pulseReason); 175 } 176 mQueuedRequests.clear(); 177 mWakeLock.release(); 178 } 179 } 180 181 /** 182 * @return the current state. 183 * 184 * This must not be called during a transition. 185 */ 186 @MainThread getState()187 public State getState() { 188 Assert.isMainThread(); 189 Preconditions.checkState(!isExecutingTransition()); 190 return mState; 191 } 192 193 /** 194 * @return the current pulse reason. 195 * 196 * This is only valid if the machine is currently in one of the pulse states. 197 */ 198 @MainThread getPulseReason()199 public int getPulseReason() { 200 Assert.isMainThread(); 201 Preconditions.checkState(mState == State.DOZE_REQUEST_PULSE 202 || mState == State.DOZE_PULSING 203 || mState == State.DOZE_PULSE_DONE, "must be in pulsing state, but is " + mState); 204 return mPulseReason; 205 } 206 207 /** Requests the PowerManager to wake up now. */ wakeUp()208 public void wakeUp() { 209 mDozeService.requestWakeUp(); 210 } 211 isExecutingTransition()212 private boolean isExecutingTransition() { 213 return !mQueuedRequests.isEmpty(); 214 } 215 transitionTo(State requestedState, int pulseReason)216 private void transitionTo(State requestedState, int pulseReason) { 217 State newState = transitionPolicy(requestedState); 218 219 if (DEBUG) { 220 Log.i(TAG, "transition: old=" + mState + " req=" + requestedState + " new=" + newState); 221 } 222 223 if (newState == mState) { 224 return; 225 } 226 227 validateTransition(newState); 228 229 State oldState = mState; 230 mState = newState; 231 232 DozeLog.traceState(newState); 233 Trace.traceCounter(Trace.TRACE_TAG_APP, "doze_machine_state", newState.ordinal()); 234 235 updatePulseReason(newState, oldState, pulseReason); 236 performTransitionOnComponents(oldState, newState); 237 updateWakeLockState(newState); 238 239 resolveIntermediateState(newState); 240 } 241 updatePulseReason(State newState, State oldState, int pulseReason)242 private void updatePulseReason(State newState, State oldState, int pulseReason) { 243 if (newState == State.DOZE_REQUEST_PULSE) { 244 mPulseReason = pulseReason; 245 } else if (oldState == State.DOZE_PULSE_DONE) { 246 mPulseReason = DozeLog.PULSE_REASON_NONE; 247 } 248 } 249 performTransitionOnComponents(State oldState, State newState)250 private void performTransitionOnComponents(State oldState, State newState) { 251 for (Part p : mParts) { 252 p.transitionTo(oldState, newState); 253 } 254 255 switch (newState) { 256 case FINISH: 257 mDozeService.finish(); 258 break; 259 default: 260 } 261 } 262 validateTransition(State newState)263 private void validateTransition(State newState) { 264 try { 265 switch (mState) { 266 case FINISH: 267 Preconditions.checkState(newState == State.FINISH); 268 break; 269 case UNINITIALIZED: 270 Preconditions.checkState(newState == State.INITIALIZED); 271 break; 272 } 273 switch (newState) { 274 case UNINITIALIZED: 275 throw new IllegalArgumentException("can't transition to UNINITIALIZED"); 276 case INITIALIZED: 277 Preconditions.checkState(mState == State.UNINITIALIZED); 278 break; 279 case DOZE_PULSING: 280 Preconditions.checkState(mState == State.DOZE_REQUEST_PULSE); 281 break; 282 case DOZE_PULSE_DONE: 283 Preconditions.checkState( 284 mState == State.DOZE_REQUEST_PULSE || mState == State.DOZE_PULSING); 285 break; 286 default: 287 break; 288 } 289 } catch (RuntimeException e) { 290 throw new IllegalStateException("Illegal Transition: " + mState + " -> " + newState, e); 291 } 292 } 293 transitionPolicy(State requestedState)294 private State transitionPolicy(State requestedState) { 295 if (mState == State.FINISH) { 296 return State.FINISH; 297 } 298 if ((mState == State.DOZE_AOD_PAUSED || mState == State.DOZE_AOD_PAUSING 299 || mState == State.DOZE_AOD || mState == State.DOZE) 300 && requestedState == State.DOZE_PULSE_DONE) { 301 Log.i(TAG, "Dropping pulse done because current state is already done: " + mState); 302 return mState; 303 } 304 if (requestedState == State.DOZE_REQUEST_PULSE && !mState.canPulse()) { 305 Log.i(TAG, "Dropping pulse request because current state can't pulse: " + mState); 306 return mState; 307 } 308 return requestedState; 309 } 310 updateWakeLockState(State newState)311 private void updateWakeLockState(State newState) { 312 boolean staysAwake = newState.staysAwake(); 313 if (mWakeLockHeldForCurrentState && !staysAwake) { 314 mWakeLock.release(); 315 mWakeLockHeldForCurrentState = false; 316 } else if (!mWakeLockHeldForCurrentState && staysAwake) { 317 mWakeLock.acquire(); 318 mWakeLockHeldForCurrentState = true; 319 } 320 } 321 resolveIntermediateState(State state)322 private void resolveIntermediateState(State state) { 323 switch (state) { 324 case INITIALIZED: 325 case DOZE_PULSE_DONE: 326 transitionTo(mConfig.alwaysOnEnabled(UserHandle.USER_CURRENT) 327 ? DozeMachine.State.DOZE_AOD : DozeMachine.State.DOZE, 328 DozeLog.PULSE_REASON_NONE); 329 break; 330 default: 331 break; 332 } 333 } 334 335 /** Dumps the current state */ dump(PrintWriter pw)336 public void dump(PrintWriter pw) { 337 pw.print(" state="); pw.println(mState); 338 pw.print(" wakeLockHeldForCurrentState="); pw.println(mWakeLockHeldForCurrentState); 339 pw.println("Parts:"); 340 for (Part p : mParts) { 341 p.dump(pw); 342 } 343 } 344 345 /** A part of the DozeMachine that needs to be notified about state changes. */ 346 public interface Part { 347 /** 348 * Transition from {@code oldState} to {@code newState}. 349 * 350 * This method is guaranteed to only be called while a wake lock is held. 351 */ transitionTo(State oldState, State newState)352 void transitionTo(State oldState, State newState); 353 354 /** Dump current state. For debugging only. */ dump(PrintWriter pw)355 default void dump(PrintWriter pw) {} 356 } 357 358 /** A wrapper interface for {@link android.service.dreams.DreamService} */ 359 public interface Service { 360 /** Finish dreaming. */ finish()361 void finish(); 362 363 /** Request a display state. See {@link android.view.Display#STATE_DOZE}. */ setDozeScreenState(int state)364 void setDozeScreenState(int state); 365 366 /** Request waking up. */ requestWakeUp()367 void requestWakeUp(); 368 369 /** Set screen brightness */ setDozeScreenBrightness(int brightness)370 void setDozeScreenBrightness(int brightness); 371 372 class Delegate implements Service { 373 private final Service mDelegate; 374 Delegate(Service delegate)375 public Delegate(Service delegate) { 376 mDelegate = delegate; 377 } 378 379 @Override finish()380 public void finish() { 381 mDelegate.finish(); 382 } 383 384 @Override setDozeScreenState(int state)385 public void setDozeScreenState(int state) { 386 mDelegate.setDozeScreenState(state); 387 } 388 389 @Override requestWakeUp()390 public void requestWakeUp() { 391 mDelegate.requestWakeUp(); 392 } 393 394 @Override setDozeScreenBrightness(int brightness)395 public void setDozeScreenBrightness(int brightness) { 396 mDelegate.setDozeScreenBrightness(brightness); 397 } 398 } 399 } 400 } 401