Home | History | Annotate | Download | only in server
      1 /*
      2  * Copyright (C) 2013 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.server;
     18 
     19 import android.app.Activity;
     20 import android.app.ActivityManagerNative;
     21 import android.app.AlarmManager;
     22 import android.app.PendingIntent;
     23 import android.content.BroadcastReceiver;
     24 import android.content.Context;
     25 import android.content.Intent;
     26 import android.content.IntentFilter;
     27 import android.os.Handler;
     28 import android.os.PowerManager;
     29 import android.os.PowerManager.WakeLock;
     30 import android.os.RemoteException;
     31 import android.os.SystemClock;
     32 import android.os.UserHandle;
     33 import android.util.Log;
     34 import android.util.Slog;
     35 
     36 /**
     37  * This service observes the device state and when applicable sends
     38  * broadcasts at the beginning and at the end of a period during which
     39  * observers can perform idle maintenance tasks. Typical use of the
     40  * idle maintenance is to perform somehow expensive tasks that can be
     41  * postponed to a moment when they will not degrade user experience.
     42  *
     43  * The current implementation is very simple. The start of a maintenance
     44  * window is announced if: the screen is off or showing a dream AND the
     45  * battery level is more than twenty percent AND at least one hour passed
     46  * activity).
     47  *
     48  * The end of a maintenance window is announced only if: a start was
     49  * announced AND the screen turned on or a dream was stopped.
     50  */
     51 public class IdleMaintenanceService extends BroadcastReceiver {
     52 
     53     private static final boolean DEBUG = false;
     54 
     55     private static final String LOG_TAG = IdleMaintenanceService.class.getSimpleName();
     56 
     57     private static final int LAST_USER_ACTIVITY_TIME_INVALID = -1;
     58 
     59     private static final long MIN_IDLE_MAINTENANCE_INTERVAL_MILLIS = 24 * 60 * 60 * 1000; // 1 day
     60 
     61     private static final int MIN_BATTERY_LEVEL_IDLE_MAINTENANCE_START_CHARGING = 30; // percent
     62 
     63     private static final int MIN_BATTERY_LEVEL_IDLE_MAINTENANCE_START_NOT_CHARGING = 80; // percent
     64 
     65     private static final int MIN_BATTERY_LEVEL_IDLE_MAINTENANCE_RUNNING = 20; // percent
     66 
     67     private static final long MIN_USER_INACTIVITY_IDLE_MAINTENANCE_START = 71 * 60 * 1000; // 71 min
     68 
     69     private static final long MAX_IDLE_MAINTENANCE_DURATION = 71 * 60 * 1000; // 71 min
     70 
     71     private static final String ACTION_UPDATE_IDLE_MAINTENANCE_STATE =
     72         "com.android.server.IdleMaintenanceService.action.UPDATE_IDLE_MAINTENANCE_STATE";
     73 
     74     private static final String ACTION_FORCE_IDLE_MAINTENANCE =
     75         "com.android.server.IdleMaintenanceService.action.FORCE_IDLE_MAINTENANCE";
     76 
     77     private static final Intent sIdleMaintenanceStartIntent;
     78     static {
     79         sIdleMaintenanceStartIntent = new Intent(Intent.ACTION_IDLE_MAINTENANCE_START);
     80         sIdleMaintenanceStartIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
     81     };
     82 
     83     private static final Intent sIdleMaintenanceEndIntent;
     84     static {
     85         sIdleMaintenanceEndIntent = new Intent(Intent.ACTION_IDLE_MAINTENANCE_END);
     86         sIdleMaintenanceEndIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
     87     }
     88 
     89     private final AlarmManager mAlarmService;
     90 
     91     private final BatteryService mBatteryService;
     92 
     93     private final PendingIntent mUpdateIdleMaintenanceStatePendingIntent;
     94 
     95     private final Context mContext;
     96 
     97     private final WakeLock mWakeLock;
     98 
     99     private final Handler mHandler;
    100 
    101     private long mLastIdleMaintenanceStartTimeMillis;
    102 
    103     private long mLastUserActivityElapsedTimeMillis = LAST_USER_ACTIVITY_TIME_INVALID;
    104 
    105     private boolean mIdleMaintenanceStarted;
    106 
    107     public IdleMaintenanceService(Context context, BatteryService batteryService) {
    108         mContext = context;
    109         mBatteryService = batteryService;
    110 
    111         mAlarmService = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
    112 
    113         PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
    114         mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, LOG_TAG);
    115 
    116         mHandler = new Handler(mContext.getMainLooper());
    117 
    118         Intent intent = new Intent(ACTION_UPDATE_IDLE_MAINTENANCE_STATE);
    119         intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
    120         mUpdateIdleMaintenanceStatePendingIntent = PendingIntent.getBroadcast(mContext, 0,
    121                 intent, PendingIntent.FLAG_UPDATE_CURRENT);
    122 
    123         register(mHandler);
    124     }
    125 
    126     public void register(Handler handler) {
    127         IntentFilter intentFilter = new IntentFilter();
    128 
    129         // Alarm actions.
    130         intentFilter.addAction(ACTION_UPDATE_IDLE_MAINTENANCE_STATE);
    131 
    132         // Battery actions.
    133         intentFilter.addAction(Intent.ACTION_BATTERY_CHANGED);
    134 
    135         // Screen actions.
    136         intentFilter.addAction(Intent.ACTION_SCREEN_ON);
    137         intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
    138 
    139         // Dream actions.
    140         intentFilter.addAction(Intent.ACTION_DREAMING_STARTED);
    141         intentFilter.addAction(Intent.ACTION_DREAMING_STOPPED);
    142 
    143         mContext.registerReceiverAsUser(this, UserHandle.ALL,
    144                 intentFilter, null, mHandler);
    145 
    146         intentFilter = new IntentFilter();
    147         intentFilter.addAction(ACTION_FORCE_IDLE_MAINTENANCE);
    148         mContext.registerReceiverAsUser(this, UserHandle.ALL,
    149                 intentFilter, android.Manifest.permission.SET_ACTIVITY_WATCHER, mHandler);
    150     }
    151 
    152     private void scheduleUpdateIdleMaintenanceState(long delayMillis) {
    153         final long triggetRealTimeMillis = SystemClock.elapsedRealtime() + delayMillis;
    154         mAlarmService.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggetRealTimeMillis,
    155                 mUpdateIdleMaintenanceStatePendingIntent);
    156     }
    157 
    158     private void unscheduleUpdateIdleMaintenanceState() {
    159         mAlarmService.cancel(mUpdateIdleMaintenanceStatePendingIntent);
    160     }
    161 
    162     private void updateIdleMaintenanceState(boolean noisy) {
    163         if (mIdleMaintenanceStarted) {
    164             // Idle maintenance can be interrupted by user activity, or duration
    165             // time out, or low battery.
    166             if (!lastUserActivityPermitsIdleMaintenanceRunning()
    167                     || !batteryLevelAndMaintenanceTimeoutPermitsIdleMaintenanceRunning()) {
    168                 unscheduleUpdateIdleMaintenanceState();
    169                 mIdleMaintenanceStarted = false;
    170                 EventLogTags.writeIdleMaintenanceWindowFinish(SystemClock.elapsedRealtime(),
    171                         mLastUserActivityElapsedTimeMillis, mBatteryService.getBatteryLevel(),
    172                         isBatteryCharging() ? 1 : 0);
    173                 sendIdleMaintenanceEndIntent();
    174                 // We stopped since we don't have enough battery or timed out but the
    175                 // user is not using the device, so we should be able to run maintenance
    176                 // in the next maintenance window since the battery may be charged
    177                 // without interaction and the min interval between maintenances passed.
    178                 if (!batteryLevelAndMaintenanceTimeoutPermitsIdleMaintenanceRunning()) {
    179                     scheduleUpdateIdleMaintenanceState(
    180                             getNextIdleMaintenanceIntervalStartFromNow());
    181                 }
    182             }
    183         } else if (deviceStatePermitsIdleMaintenanceStart(noisy)
    184                 && lastUserActivityPermitsIdleMaintenanceStart(noisy)
    185                 && lastRunPermitsIdleMaintenanceStart(noisy)) {
    186             // Now that we started idle maintenance, we should schedule another
    187             // update for the moment when the idle maintenance times out.
    188             scheduleUpdateIdleMaintenanceState(MAX_IDLE_MAINTENANCE_DURATION);
    189             mIdleMaintenanceStarted = true;
    190             EventLogTags.writeIdleMaintenanceWindowStart(SystemClock.elapsedRealtime(),
    191                     mLastUserActivityElapsedTimeMillis, mBatteryService.getBatteryLevel(),
    192                     isBatteryCharging() ? 1 : 0);
    193             mLastIdleMaintenanceStartTimeMillis = SystemClock.elapsedRealtime();
    194             sendIdleMaintenanceStartIntent();
    195         } else if (lastUserActivityPermitsIdleMaintenanceStart(noisy)) {
    196              if (lastRunPermitsIdleMaintenanceStart(noisy)) {
    197                 // The user does not use the device and we did not run maintenance in more
    198                 // than the min interval between runs, so schedule an update - maybe the
    199                 // battery will be charged latter.
    200                 scheduleUpdateIdleMaintenanceState(MIN_USER_INACTIVITY_IDLE_MAINTENANCE_START);
    201              } else {
    202                  // The user does not use the device but we have run maintenance in the min
    203                  // interval between runs, so schedule an update after the min interval ends.
    204                  scheduleUpdateIdleMaintenanceState(
    205                          getNextIdleMaintenanceIntervalStartFromNow());
    206              }
    207         }
    208     }
    209 
    210     private long getNextIdleMaintenanceIntervalStartFromNow() {
    211         return mLastIdleMaintenanceStartTimeMillis + MIN_IDLE_MAINTENANCE_INTERVAL_MILLIS
    212                 - SystemClock.elapsedRealtime();
    213     }
    214 
    215     private void sendIdleMaintenanceStartIntent() {
    216         mWakeLock.acquire();
    217         try {
    218             ActivityManagerNative.getDefault().performIdleMaintenance();
    219         } catch (RemoteException e) {
    220         }
    221         mContext.sendOrderedBroadcastAsUser(sIdleMaintenanceStartIntent, UserHandle.ALL,
    222                 null, this, mHandler, Activity.RESULT_OK, null, null);
    223     }
    224 
    225     private void sendIdleMaintenanceEndIntent() {
    226         mWakeLock.acquire();
    227         mContext.sendOrderedBroadcastAsUser(sIdleMaintenanceEndIntent, UserHandle.ALL,
    228                 null, this, mHandler, Activity.RESULT_OK, null, null);
    229     }
    230 
    231     private boolean deviceStatePermitsIdleMaintenanceStart(boolean noisy) {
    232         final int minBatteryLevel = isBatteryCharging()
    233                 ? MIN_BATTERY_LEVEL_IDLE_MAINTENANCE_START_CHARGING
    234                 : MIN_BATTERY_LEVEL_IDLE_MAINTENANCE_START_NOT_CHARGING;
    235         boolean allowed = (mLastUserActivityElapsedTimeMillis != LAST_USER_ACTIVITY_TIME_INVALID
    236                 && mBatteryService.getBatteryLevel() > minBatteryLevel);
    237         if (!allowed && noisy) {
    238             Slog.i("IdleMaintenance", "Idle maintenance not allowed due to power");
    239         }
    240         return allowed;
    241     }
    242 
    243     private boolean lastUserActivityPermitsIdleMaintenanceStart(boolean noisy) {
    244         // The last time the user poked the device is above the threshold.
    245         boolean allowed = (mLastUserActivityElapsedTimeMillis != LAST_USER_ACTIVITY_TIME_INVALID
    246                 && SystemClock.elapsedRealtime() - mLastUserActivityElapsedTimeMillis
    247                     > MIN_USER_INACTIVITY_IDLE_MAINTENANCE_START);
    248         if (!allowed && noisy) {
    249             Slog.i("IdleMaintenance", "Idle maintenance not allowed due to last user activity");
    250         }
    251         return allowed;
    252     }
    253 
    254     private boolean lastRunPermitsIdleMaintenanceStart(boolean noisy) {
    255         // Enough time passed since the last maintenance run.
    256         boolean allowed = SystemClock.elapsedRealtime() - mLastIdleMaintenanceStartTimeMillis
    257                 > MIN_IDLE_MAINTENANCE_INTERVAL_MILLIS;
    258         if (!allowed && noisy) {
    259             Slog.i("IdleMaintenance", "Idle maintenance not allowed due time since last");
    260         }
    261         return allowed;
    262     }
    263 
    264     private boolean lastUserActivityPermitsIdleMaintenanceRunning() {
    265         // The user is not using the device.
    266         return (mLastUserActivityElapsedTimeMillis != LAST_USER_ACTIVITY_TIME_INVALID);
    267     }
    268 
    269     private boolean batteryLevelAndMaintenanceTimeoutPermitsIdleMaintenanceRunning() {
    270         // Battery not too low and the maintenance duration did not timeout.
    271         return (mBatteryService.getBatteryLevel() > MIN_BATTERY_LEVEL_IDLE_MAINTENANCE_RUNNING
    272                 && mLastIdleMaintenanceStartTimeMillis + MAX_IDLE_MAINTENANCE_DURATION
    273                         > SystemClock.elapsedRealtime());
    274     }
    275 
    276     private boolean isBatteryCharging() {
    277         return mBatteryService.getPlugType() > 0
    278                 && mBatteryService.getInvalidCharger() == 0;
    279     }
    280 
    281     @Override
    282     public void onReceive(Context context, Intent intent) {
    283         if (DEBUG) {
    284             Log.i(LOG_TAG, intent.getAction());
    285         }
    286         String action = intent.getAction();
    287         if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
    288             // We care about battery only if maintenance is in progress so we can
    289             // stop it if battery is too low. Note that here we assume that the
    290             // maintenance clients are properly holding a wake lock. We will
    291             // refactor the maintenance to use services instead of intents for the
    292             // next release. The only client for this for now is internal an holds
    293             // a wake lock correctly.
    294             if (mIdleMaintenanceStarted) {
    295                 updateIdleMaintenanceState(false);
    296             }
    297         } else if (Intent.ACTION_SCREEN_ON.equals(action)
    298                 || Intent.ACTION_DREAMING_STOPPED.equals(action)) {
    299             mLastUserActivityElapsedTimeMillis = LAST_USER_ACTIVITY_TIME_INVALID;
    300             // Unschedule any future updates since we already know that maintenance
    301             // cannot be performed since the user is back.
    302             unscheduleUpdateIdleMaintenanceState();
    303             // If the screen went on/stopped dreaming, we know the user is using the
    304             // device which means that idle maintenance should be stopped if running.
    305             updateIdleMaintenanceState(false);
    306         } else if (Intent.ACTION_SCREEN_OFF.equals(action)
    307                 || Intent.ACTION_DREAMING_STARTED.equals(action)) {
    308             mLastUserActivityElapsedTimeMillis = SystemClock.elapsedRealtime();
    309             // If screen went off/started dreaming, we may be able to start idle maintenance
    310             // after the minimal user inactivity elapses. We schedule an alarm for when
    311             // this timeout elapses since the device may go to sleep by then.
    312             scheduleUpdateIdleMaintenanceState(MIN_USER_INACTIVITY_IDLE_MAINTENANCE_START);
    313         } else if (ACTION_UPDATE_IDLE_MAINTENANCE_STATE.equals(action)) {
    314             updateIdleMaintenanceState(false);
    315         } else if (ACTION_FORCE_IDLE_MAINTENANCE.equals(action)) {
    316             long now = SystemClock.elapsedRealtime() - 1;
    317             mLastUserActivityElapsedTimeMillis = now - MIN_USER_INACTIVITY_IDLE_MAINTENANCE_START;
    318             mLastIdleMaintenanceStartTimeMillis = now - MIN_IDLE_MAINTENANCE_INTERVAL_MILLIS;
    319             updateIdleMaintenanceState(true);
    320         } else if (Intent.ACTION_IDLE_MAINTENANCE_START.equals(action)
    321                 || Intent.ACTION_IDLE_MAINTENANCE_END.equals(action)) {
    322             // We were holding a wake lock while broadcasting the idle maintenance
    323             // intents but now that we finished the broadcast release the wake lock.
    324             mWakeLock.release();
    325         }
    326     }
    327 }
    328