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