Home | History | Annotate | Download | only in doze
      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