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 android.app.AlarmManager; 20 import android.content.Context; 21 import android.os.Handler; 22 import android.os.SystemClock; 23 import android.text.format.Formatter; 24 import android.util.Log; 25 26 import com.android.systemui.util.AlarmTimeout; 27 import com.android.systemui.util.wakelock.WakeLock; 28 29 import java.util.Calendar; 30 import java.util.GregorianCalendar; 31 32 /** 33 * The policy controlling doze. 34 */ 35 public class DozeUi implements DozeMachine.Part { 36 37 private static final long TIME_TICK_DEADLINE_MILLIS = 90 * 1000; // 1.5min 38 private final Context mContext; 39 private final DozeHost mHost; 40 private final Handler mHandler; 41 private final WakeLock mWakeLock; 42 private final DozeMachine mMachine; 43 private final AlarmTimeout mTimeTicker; 44 45 private long mLastTimeTickElapsed = 0; 46 47 public DozeUi(Context context, AlarmManager alarmManager, DozeMachine machine, 48 WakeLock wakeLock, DozeHost host, Handler handler) { 49 mContext = context; 50 mMachine = machine; 51 mWakeLock = wakeLock; 52 mHost = host; 53 mHandler = handler; 54 55 mTimeTicker = new AlarmTimeout(alarmManager, this::onTimeTick, "doze_time_tick", handler); 56 } 57 58 private void pulseWhileDozing(int reason) { 59 mHost.pulseWhileDozing( 60 new DozeHost.PulseCallback() { 61 @Override 62 public void onPulseStarted() { 63 mMachine.requestState(DozeMachine.State.DOZE_PULSING); 64 } 65 66 @Override 67 public void onPulseFinished() { 68 mMachine.requestState(DozeMachine.State.DOZE_PULSE_DONE); 69 } 70 }, reason); 71 } 72 73 @Override 74 public void transitionTo(DozeMachine.State oldState, DozeMachine.State newState) { 75 switch (newState) { 76 case DOZE_AOD: 77 case DOZE_AOD_PAUSING: 78 scheduleTimeTick(); 79 break; 80 case DOZE: 81 case DOZE_AOD_PAUSED: 82 unscheduleTimeTick(); 83 break; 84 case DOZE_REQUEST_PULSE: 85 pulseWhileDozing(mMachine.getPulseReason()); 86 break; 87 case INITIALIZED: 88 mHost.startDozing(); 89 break; 90 case FINISH: 91 mHost.stopDozing(); 92 unscheduleTimeTick(); 93 break; 94 } 95 updateAnimateWakeup(newState); 96 } 97 98 private void updateAnimateWakeup(DozeMachine.State state) { 99 switch (state) { 100 case DOZE_REQUEST_PULSE: 101 case DOZE_PULSING: 102 case DOZE_PULSE_DONE: 103 mHost.setAnimateWakeup(true); 104 break; 105 case FINISH: 106 // Keep current state. 107 break; 108 default: 109 mHost.setAnimateWakeup(false); 110 break; 111 } 112 } 113 114 private void scheduleTimeTick() { 115 if (mTimeTicker.isScheduled()) { 116 return; 117 } 118 119 long delta = roundToNextMinute(System.currentTimeMillis()) - System.currentTimeMillis(); 120 mTimeTicker.schedule(delta, AlarmTimeout.MODE_IGNORE_IF_SCHEDULED); 121 mLastTimeTickElapsed = SystemClock.elapsedRealtime(); 122 } 123 124 private void unscheduleTimeTick() { 125 if (!mTimeTicker.isScheduled()) { 126 return; 127 } 128 verifyLastTimeTick(); 129 mTimeTicker.cancel(); 130 } 131 132 private void verifyLastTimeTick() { 133 long millisSinceLastTick = SystemClock.elapsedRealtime() - mLastTimeTickElapsed; 134 if (millisSinceLastTick > TIME_TICK_DEADLINE_MILLIS) { 135 String delay = Formatter.formatShortElapsedTime(mContext, millisSinceLastTick); 136 DozeLog.traceMissedTick(delay); 137 Log.e(DozeMachine.TAG, "Missed AOD time tick by " + delay); 138 } 139 } 140 141 private long roundToNextMinute(long timeInMillis) { 142 Calendar calendar = GregorianCalendar.getInstance(); 143 calendar.setTimeInMillis(timeInMillis); 144 calendar.set(Calendar.MILLISECOND, 0); 145 calendar.set(Calendar.SECOND, 0); 146 calendar.add(Calendar.MINUTE, 1); 147 148 return calendar.getTimeInMillis(); 149 } 150 151 private void onTimeTick() { 152 verifyLastTimeTick(); 153 154 mHost.dozeTimeTick(); 155 156 // Keep wakelock until a frame has been pushed. 157 mHandler.post(mWakeLock.wrap(() -> {})); 158 159 scheduleTimeTick(); 160 } 161 } 162