• 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.systemui.dagger.qualifiers.Main;
30 import com.android.systemui.doze.dagger.DozeScope;
31 import com.android.systemui.plugins.statusbar.StatusBarStateController;
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 import javax.inject.Inject;
39 
40 /**
41  * The policy controlling doze.
42  */
43 @DozeScope
44 public class DozeUi implements DozeMachine.Part {
45     private static final long TIME_TICK_DEADLINE_MILLIS = 90 * 1000; // 1.5min
46     private final Context mContext;
47     private final DozeHost mHost;
48     private final Handler mHandler;
49     private final WakeLock mWakeLock;
50     private DozeMachine mMachine;
51     private final AlarmTimeout mTimeTicker;
52     private final boolean mCanAnimateTransition;
53     private final DozeParameters mDozeParameters;
54     private final DozeLog mDozeLog;
55     private final StatusBarStateController mStatusBarStateController;
56 
57     private long mLastTimeTickElapsed = 0;
58 
59     @Inject
DozeUi(Context context, AlarmManager alarmManager, WakeLock wakeLock, DozeHost host, @Main Handler handler, DozeParameters params, StatusBarStateController statusBarStateController, DozeLog dozeLog)60     public DozeUi(Context context, AlarmManager alarmManager,
61             WakeLock wakeLock, DozeHost host, @Main Handler handler,
62             DozeParameters params,
63             StatusBarStateController statusBarStateController,
64             DozeLog dozeLog) {
65         mContext = context;
66         mWakeLock = wakeLock;
67         mHost = host;
68         mHandler = handler;
69         mCanAnimateTransition = !params.getDisplayNeedsBlanking();
70         mDozeParameters = params;
71         mTimeTicker = new AlarmTimeout(alarmManager, this::onTimeTick, "doze_time_tick", handler);
72         mDozeLog = dozeLog;
73         mStatusBarStateController = statusBarStateController;
74     }
75 
76     @Override
setDozeMachine(DozeMachine dozeMachine)77     public void setDozeMachine(DozeMachine dozeMachine) {
78         mMachine = dozeMachine;
79     }
80 
pulseWhileDozing(int reason)81     private void pulseWhileDozing(int reason) {
82         mHost.pulseWhileDozing(
83                 new DozeHost.PulseCallback() {
84                     @Override
85                     public void onPulseStarted() {
86                         try {
87                             mMachine.requestState(
88                                     reason == DozeLog.PULSE_REASON_SENSOR_WAKE_REACH
89                                             ? DozeMachine.State.DOZE_PULSING_BRIGHT
90                                             : DozeMachine.State.DOZE_PULSING);
91                         } catch (IllegalStateException e) {
92                             // It's possible that the pulse was asynchronously cancelled while
93                             // we were waiting for it to start (under stress conditions.)
94                             // In those cases we should just ignore it. b/127657926
95                         }
96                     }
97 
98                     @Override
99                     public void onPulseFinished() {
100                         mMachine.requestState(DozeMachine.State.DOZE_PULSE_DONE);
101                     }
102                 }, reason);
103     }
104 
105     @Override
transitionTo(DozeMachine.State oldState, DozeMachine.State newState)106     public void transitionTo(DozeMachine.State oldState, DozeMachine.State newState) {
107         switch (newState) {
108             case DOZE_AOD:
109             case DOZE_AOD_DOCKED:
110                 if (oldState == DOZE_AOD_PAUSED || oldState == DOZE) {
111                     // Whenever turning on the display, it's necessary to push a new frame.
112                     // The display buffers will be empty and need to be filled.
113                     mHost.dozeTimeTick();
114                     // The first frame may arrive when the display isn't ready yet.
115                     mHandler.postDelayed(mWakeLock.wrap(mHost::dozeTimeTick), 500);
116                 }
117                 scheduleTimeTick();
118                 break;
119             case DOZE_AOD_PAUSING:
120                 scheduleTimeTick();
121                 break;
122             case DOZE:
123             case DOZE_AOD_PAUSED:
124             case DOZE_SUSPEND_TRIGGERS:
125                 unscheduleTimeTick();
126                 break;
127             case DOZE_REQUEST_PULSE:
128                 scheduleTimeTick();
129                 pulseWhileDozing(mMachine.getPulseReason());
130                 break;
131             case INITIALIZED:
132                 mHost.startDozing();
133                 break;
134             case FINISH:
135                 mHost.stopDozing();
136                 unscheduleTimeTick();
137                 break;
138         }
139         updateAnimateWakeup(newState);
140     }
141 
updateAnimateWakeup(DozeMachine.State state)142     private void updateAnimateWakeup(DozeMachine.State state) {
143         switch (state) {
144             case DOZE_REQUEST_PULSE:
145             case DOZE_PULSING:
146             case DOZE_PULSING_BRIGHT:
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 time = System.currentTimeMillis();
165         long delta = roundToNextMinute(time) - System.currentTimeMillis();
166         boolean scheduled = mTimeTicker.schedule(delta, AlarmTimeout.MODE_IGNORE_IF_SCHEDULED);
167         if (scheduled) {
168             mDozeLog.traceTimeTickScheduled(time, time + delta);
169         }
170         mLastTimeTickElapsed = SystemClock.elapsedRealtime();
171     }
172 
unscheduleTimeTick()173     private void unscheduleTimeTick() {
174         if (!mTimeTicker.isScheduled()) {
175             return;
176         }
177         verifyLastTimeTick();
178         mTimeTicker.cancel();
179     }
180 
verifyLastTimeTick()181     private void verifyLastTimeTick() {
182         long millisSinceLastTick = SystemClock.elapsedRealtime() - mLastTimeTickElapsed;
183         if (millisSinceLastTick > TIME_TICK_DEADLINE_MILLIS) {
184             String delay = Formatter.formatShortElapsedTime(mContext, millisSinceLastTick);
185             mDozeLog.traceMissedTick(delay);
186             Log.e(DozeMachine.TAG, "Missed AOD time tick by " + delay);
187         }
188     }
189 
roundToNextMinute(long timeInMillis)190     private long roundToNextMinute(long timeInMillis) {
191         Calendar calendar = Calendar.getInstance();
192         calendar.setTimeInMillis(timeInMillis);
193         calendar.set(Calendar.MILLISECOND, 0);
194         calendar.set(Calendar.SECOND, 0);
195         calendar.add(Calendar.MINUTE, 1);
196 
197         return calendar.getTimeInMillis();
198     }
199 
onTimeTick()200     private void onTimeTick() {
201         verifyLastTimeTick();
202 
203         mHost.dozeTimeTick();
204 
205         // Keep wakelock until a frame has been pushed.
206         mHandler.post(mWakeLock.wrap(() -> {}));
207 
208         scheduleTimeTick();
209     }
210 }
211