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