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