• 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 static com.android.systemui.doze.DozeMachine.State.DOZE;
20 import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD_PAUSED;
21 
22 import android.app.AlarmManager;
23 import android.content.Context;
24 import android.os.Handler;
25 import android.os.SystemClock;
26 import android.text.format.Formatter;
27 import android.util.Log;
28 
29 import com.android.internal.annotations.VisibleForTesting;
30 import com.android.keyguard.KeyguardUpdateMonitor;
31 import com.android.keyguard.KeyguardUpdateMonitorCallback;
32 import com.android.systemui.statusbar.phone.DozeParameters;
33 import com.android.systemui.util.AlarmTimeout;
34 import com.android.systemui.util.wakelock.WakeLock;
35 
36 import java.util.Calendar;
37 
38 /**
39  * The policy controlling doze.
40  */
41 public class DozeUi implements DozeMachine.Part {
42 
43     private static final long TIME_TICK_DEADLINE_MILLIS = 90 * 1000; // 1.5min
44     private final Context mContext;
45     private final DozeHost mHost;
46     private final Handler mHandler;
47     private final WakeLock mWakeLock;
48     private final DozeMachine mMachine;
49     private final AlarmTimeout mTimeTicker;
50     private final boolean mCanAnimateTransition;
51     private final DozeParameters mDozeParameters;
52     private final DozeLog mDozeLog;
53 
54     private boolean mKeyguardShowing;
55     private final KeyguardUpdateMonitorCallback mKeyguardVisibilityCallback =
56             new KeyguardUpdateMonitorCallback() {
57 
58                 @Override
59                 public void onKeyguardVisibilityChanged(boolean showing) {
60                     mKeyguardShowing = showing;
61                     updateAnimateScreenOff();
62                 }
63             };
64 
65     private long mLastTimeTickElapsed = 0;
66 
DozeUi(Context context, AlarmManager alarmManager, DozeMachine machine, WakeLock wakeLock, DozeHost host, Handler handler, DozeParameters params, KeyguardUpdateMonitor keyguardUpdateMonitor, DozeLog dozeLog)67     public DozeUi(Context context, AlarmManager alarmManager, DozeMachine machine,
68             WakeLock wakeLock, DozeHost host, Handler handler,
69             DozeParameters params, KeyguardUpdateMonitor keyguardUpdateMonitor,
70             DozeLog dozeLog) {
71         mContext = context;
72         mMachine = machine;
73         mWakeLock = wakeLock;
74         mHost = host;
75         mHandler = handler;
76         mCanAnimateTransition = !params.getDisplayNeedsBlanking();
77         mDozeParameters = params;
78         mTimeTicker = new AlarmTimeout(alarmManager, this::onTimeTick, "doze_time_tick", handler);
79         keyguardUpdateMonitor.registerCallback(mKeyguardVisibilityCallback);
80         mDozeLog = dozeLog;
81     }
82 
83     /**
84      * Decide if we're taking over the screen-off animation
85      * when the device was configured to skip doze after screen off.
86      */
updateAnimateScreenOff()87     private void updateAnimateScreenOff() {
88         if (mCanAnimateTransition) {
89             final boolean controlScreenOff = mDozeParameters.getAlwaysOn() && mKeyguardShowing
90                     && !mHost.isPowerSaveActive();
91             mDozeParameters.setControlScreenOffAnimation(controlScreenOff);
92             mHost.setAnimateScreenOff(controlScreenOff);
93         }
94     }
95 
pulseWhileDozing(int reason)96     private void pulseWhileDozing(int reason) {
97         mHost.pulseWhileDozing(
98                 new DozeHost.PulseCallback() {
99                     @Override
100                     public void onPulseStarted() {
101                         try {
102                             mMachine.requestState(
103                                     reason == DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN
104                                             ? DozeMachine.State.DOZE_PULSING_BRIGHT
105                                             : DozeMachine.State.DOZE_PULSING);
106                         } catch (IllegalStateException e) {
107                             // It's possible that the pulse was asynchronously cancelled while
108                             // we were waiting for it to start (under stress conditions.)
109                             // In those cases we should just ignore it. b/127657926
110                         }
111                     }
112 
113                     @Override
114                     public void onPulseFinished() {
115                         mMachine.requestState(DozeMachine.State.DOZE_PULSE_DONE);
116                     }
117                 }, reason);
118     }
119 
120     @Override
transitionTo(DozeMachine.State oldState, DozeMachine.State newState)121     public void transitionTo(DozeMachine.State oldState, DozeMachine.State newState) {
122         switch (newState) {
123             case DOZE_AOD:
124             case DOZE_AOD_DOCKED:
125                 if (oldState == DOZE_AOD_PAUSED || oldState == DOZE) {
126                     // Whenever turning on the display, it's necessary to push a new frame.
127                     // The display buffers will be empty and need to be filled.
128                     mHost.dozeTimeTick();
129                     // The first frame may arrive when the display isn't ready yet.
130                     mHandler.postDelayed(mWakeLock.wrap(mHost::dozeTimeTick), 500);
131                 }
132                 scheduleTimeTick();
133                 break;
134             case DOZE_AOD_PAUSING:
135                 scheduleTimeTick();
136                 break;
137             case DOZE:
138             case DOZE_AOD_PAUSED:
139                 unscheduleTimeTick();
140                 break;
141             case DOZE_REQUEST_PULSE:
142                 scheduleTimeTick();
143                 pulseWhileDozing(mMachine.getPulseReason());
144                 break;
145             case INITIALIZED:
146                 mHost.startDozing();
147                 break;
148             case FINISH:
149                 mHost.stopDozing();
150                 unscheduleTimeTick();
151                 break;
152         }
153         updateAnimateWakeup(newState);
154     }
155 
updateAnimateWakeup(DozeMachine.State state)156     private void updateAnimateWakeup(DozeMachine.State state) {
157         switch (state) {
158             case DOZE_REQUEST_PULSE:
159             case DOZE_PULSING:
160             case DOZE_PULSING_BRIGHT:
161             case DOZE_PULSE_DONE:
162                 mHost.setAnimateWakeup(true);
163                 break;
164             case FINISH:
165                 // Keep current state.
166                 break;
167             default:
168                 mHost.setAnimateWakeup(mCanAnimateTransition && mDozeParameters.getAlwaysOn());
169                 break;
170         }
171     }
172 
scheduleTimeTick()173     private void scheduleTimeTick() {
174         if (mTimeTicker.isScheduled()) {
175             return;
176         }
177 
178         long time = System.currentTimeMillis();
179         long delta = roundToNextMinute(time) - System.currentTimeMillis();
180         boolean scheduled = mTimeTicker.schedule(delta, AlarmTimeout.MODE_IGNORE_IF_SCHEDULED);
181         if (scheduled) {
182             mDozeLog.traceTimeTickScheduled(time, time + delta);
183         }
184         mLastTimeTickElapsed = SystemClock.elapsedRealtime();
185     }
186 
unscheduleTimeTick()187     private void unscheduleTimeTick() {
188         if (!mTimeTicker.isScheduled()) {
189             return;
190         }
191         verifyLastTimeTick();
192         mTimeTicker.cancel();
193     }
194 
verifyLastTimeTick()195     private void verifyLastTimeTick() {
196         long millisSinceLastTick = SystemClock.elapsedRealtime() - mLastTimeTickElapsed;
197         if (millisSinceLastTick > TIME_TICK_DEADLINE_MILLIS) {
198             String delay = Formatter.formatShortElapsedTime(mContext, millisSinceLastTick);
199             mDozeLog.traceMissedTick(delay);
200             Log.e(DozeMachine.TAG, "Missed AOD time tick by " + delay);
201         }
202     }
203 
roundToNextMinute(long timeInMillis)204     private long roundToNextMinute(long timeInMillis) {
205         Calendar calendar = Calendar.getInstance();
206         calendar.setTimeInMillis(timeInMillis);
207         calendar.set(Calendar.MILLISECOND, 0);
208         calendar.set(Calendar.SECOND, 0);
209         calendar.add(Calendar.MINUTE, 1);
210 
211         return calendar.getTimeInMillis();
212     }
213 
onTimeTick()214     private void onTimeTick() {
215         verifyLastTimeTick();
216 
217         mHost.dozeTimeTick();
218 
219         // Keep wakelock until a frame has been pushed.
220         mHandler.post(mWakeLock.wrap(() -> {}));
221 
222         scheduleTimeTick();
223     }
224 
225     @VisibleForTesting
getKeyguardCallback()226     KeyguardUpdateMonitorCallback getKeyguardCallback() {
227         return mKeyguardVisibilityCallback;
228     }
229 }
230