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