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 17package com.android.systemui.doze; 18 19import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD_PAUSED; 20 21import android.app.AlarmManager; 22import android.content.Context; 23import android.os.Handler; 24import android.os.SystemClock; 25import android.text.format.Formatter; 26import android.util.Log; 27 28import com.android.internal.annotations.VisibleForTesting; 29import com.android.keyguard.KeyguardUpdateMonitor; 30import com.android.keyguard.KeyguardUpdateMonitorCallback; 31import com.android.systemui.statusbar.phone.DozeParameters; 32import com.android.systemui.util.AlarmTimeout; 33import com.android.systemui.util.wakelock.WakeLock; 34 35import java.util.Calendar; 36import java.util.GregorianCalendar; 37 38/** 39 * The policy controlling doze. 40 */ 41public 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 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 */ 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 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 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 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 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 169 private void unscheduleTimeTick() { 170 if (!mTimeTicker.isScheduled()) { 171 return; 172 } 173 verifyLastTimeTick(); 174 mTimeTicker.cancel(); 175 } 176 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 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 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 208 KeyguardUpdateMonitorCallback getKeyguardCallback() { 209 return mKeyguardVisibilityCallback; 210 } 211} 212