blob: b0a1aca37d7cacb80b39e0a759103a819514eb7a [file] [log] [blame]
Svetoslavb3038ec2013-02-13 14:39:30 -08001/*
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
17package com.android.server;
18
19import android.app.Activity;
Dianne Hackborn35f72be2013-09-16 10:57:39 -070020import android.app.ActivityManagerNative;
Svetoslav6a08a122013-05-03 11:24:26 -070021import android.app.AlarmManager;
22import android.app.PendingIntent;
Svetoslavb3038ec2013-02-13 14:39:30 -080023import android.content.BroadcastReceiver;
24import android.content.Context;
25import android.content.Intent;
26import android.content.IntentFilter;
Svetoslavb3038ec2013-02-13 14:39:30 -080027import android.os.Handler;
Svetoslavb3038ec2013-02-13 14:39:30 -080028import android.os.PowerManager;
29import android.os.PowerManager.WakeLock;
Dianne Hackborn35f72be2013-09-16 10:57:39 -070030import android.os.RemoteException;
Svetoslavb3038ec2013-02-13 14:39:30 -080031import android.os.SystemClock;
32import android.os.UserHandle;
33import android.util.Log;
Dianne Hackborn35f72be2013-09-16 10:57:39 -070034import android.util.Slog;
Svetoslavb3038ec2013-02-13 14:39:30 -080035
Svetoslavb3038ec2013-02-13 14:39:30 -080036/**
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
Svetoslavb3038ec2013-02-13 14:39:30 -080046 * 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 */
51public class IdleMaintenanceService extends BroadcastReceiver {
52
Svetoslav6a08a122013-05-03 11:24:26 -070053 private static final boolean DEBUG = false;
Svetoslavb3038ec2013-02-13 14:39:30 -080054
55 private static final String LOG_TAG = IdleMaintenanceService.class.getSimpleName();
56
57 private static final int LAST_USER_ACTIVITY_TIME_INVALID = -1;
58
Svetoslav6a08a122013-05-03 11:24:26 -070059 private static final long MIN_IDLE_MAINTENANCE_INTERVAL_MILLIS = 24 * 60 * 60 * 1000; // 1 day
Svetoslavb3038ec2013-02-13 14:39:30 -080060
Svetoslavf23b64d2013-04-25 14:45:54 -070061 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
Svetoslav6a08a122013-05-03 11:24:26 -070065 private static final int MIN_BATTERY_LEVEL_IDLE_MAINTENANCE_RUNNING = 20; // percent
Svetoslavf23b64d2013-04-25 14:45:54 -070066
Svetoslav6a08a122013-05-03 11:24:26 -070067 private static final long MIN_USER_INACTIVITY_IDLE_MAINTENANCE_START = 71 * 60 * 1000; // 71 min
Svetoslavb3038ec2013-02-13 14:39:30 -080068
Svetoslav6a08a122013-05-03 11:24:26 -070069 private static final long MAX_IDLE_MAINTENANCE_DURATION = 71 * 60 * 1000; // 71 min
Svetoslavb3038ec2013-02-13 14:39:30 -080070
Svetoslav6a08a122013-05-03 11:24:26 -070071 private static final String ACTION_UPDATE_IDLE_MAINTENANCE_STATE =
72 "com.android.server.IdleMaintenanceService.action.UPDATE_IDLE_MAINTENANCE_STATE";
73
Dianne Hackborn35f72be2013-09-16 10:57:39 -070074 private static final String ACTION_FORCE_IDLE_MAINTENANCE =
75 "com.android.server.IdleMaintenanceService.action.FORCE_IDLE_MAINTENANCE";
76
Svetoslav6a08a122013-05-03 11:24:26 -070077 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;
Svetoslavb3038ec2013-02-13 14:39:30 -080094
95 private final Context mContext;
96
97 private final WakeLock mWakeLock;
98
99 private final Handler mHandler;
100
Svetoslav6a08a122013-05-03 11:24:26 -0700101 private long mLastIdleMaintenanceStartTimeMillis;
Svetoslavb3038ec2013-02-13 14:39:30 -0800102
103 private long mLastUserActivityElapsedTimeMillis = LAST_USER_ACTIVITY_TIME_INVALID;
104
Svetoslavb3038ec2013-02-13 14:39:30 -0800105 private boolean mIdleMaintenanceStarted;
106
Svetoslav6a08a122013-05-03 11:24:26 -0700107 public IdleMaintenanceService(Context context, BatteryService batteryService) {
Svetoslavb3038ec2013-02-13 14:39:30 -0800108 mContext = context;
Svetoslav6a08a122013-05-03 11:24:26 -0700109 mBatteryService = batteryService;
110
111 mAlarmService = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
Svetoslavb3038ec2013-02-13 14:39:30 -0800112
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
Svetoslav6a08a122013-05-03 11:24:26 -0700118 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
Dianne Hackborn35f72be2013-09-16 10:57:39 -0700123 register(mHandler);
Svetoslavb3038ec2013-02-13 14:39:30 -0800124 }
125
Dianne Hackborn35f72be2013-09-16 10:57:39 -0700126 public void register(Handler handler) {
Svetoslavb3038ec2013-02-13 14:39:30 -0800127 IntentFilter intentFilter = new IntentFilter();
128
Svetoslav6a08a122013-05-03 11:24:26 -0700129 // Alarm actions.
130 intentFilter.addAction(ACTION_UPDATE_IDLE_MAINTENANCE_STATE);
131
Svetoslavb3038ec2013-02-13 14:39:30 -0800132 // 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,
Dianne Hackborn35f72be2013-09-16 10:57:39 -0700144 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);
Svetoslavb3038ec2013-02-13 14:39:30 -0800150 }
151
Svetoslav6a08a122013-05-03 11:24:26 -0700152 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
Dianne Hackborn35f72be2013-09-16 10:57:39 -0700162 private void updateIdleMaintenanceState(boolean noisy) {
Svetoslavb3038ec2013-02-13 14:39:30 -0800163 if (mIdleMaintenanceStarted) {
Svetoslav6a08a122013-05-03 11:24:26 -0700164 // Idle maintenance can be interrupted by user activity, or duration
165 // time out, or low battery.
166 if (!lastUserActivityPermitsIdleMaintenanceRunning()
167 || !batteryLevelAndMaintenanceTimeoutPermitsIdleMaintenanceRunning()) {
168 unscheduleUpdateIdleMaintenanceState();
Svetoslavb3038ec2013-02-13 14:39:30 -0800169 mIdleMaintenanceStarted = false;
Svetoslavf23b64d2013-04-25 14:45:54 -0700170 EventLogTags.writeIdleMaintenanceWindowFinish(SystemClock.elapsedRealtime(),
Svetoslav6a08a122013-05-03 11:24:26 -0700171 mLastUserActivityElapsedTimeMillis, mBatteryService.getBatteryLevel(),
172 isBatteryCharging() ? 1 : 0);
Svetoslavb3038ec2013-02-13 14:39:30 -0800173 sendIdleMaintenanceEndIntent();
Svetoslav6a08a122013-05-03 11:24:26 -0700174 // 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 }
Svetoslavb3038ec2013-02-13 14:39:30 -0800182 }
Dianne Hackborn35f72be2013-09-16 10:57:39 -0700183 } else if (deviceStatePermitsIdleMaintenanceStart(noisy)
184 && lastUserActivityPermitsIdleMaintenanceStart(noisy)
185 && lastRunPermitsIdleMaintenanceStart(noisy)) {
Svetoslav6a08a122013-05-03 11:24:26 -0700186 // 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);
Svetoslavb3038ec2013-02-13 14:39:30 -0800189 mIdleMaintenanceStarted = true;
Svetoslavf23b64d2013-04-25 14:45:54 -0700190 EventLogTags.writeIdleMaintenanceWindowStart(SystemClock.elapsedRealtime(),
Svetoslav6a08a122013-05-03 11:24:26 -0700191 mLastUserActivityElapsedTimeMillis, mBatteryService.getBatteryLevel(),
192 isBatteryCharging() ? 1 : 0);
Svetoslavf23b64d2013-04-25 14:45:54 -0700193 mLastIdleMaintenanceStartTimeMillis = SystemClock.elapsedRealtime();
Svetoslavb3038ec2013-02-13 14:39:30 -0800194 sendIdleMaintenanceStartIntent();
Dianne Hackborn35f72be2013-09-16 10:57:39 -0700195 } else if (lastUserActivityPermitsIdleMaintenanceStart(noisy)) {
196 if (lastRunPermitsIdleMaintenanceStart(noisy)) {
Svetoslav6a08a122013-05-03 11:24:26 -0700197 // 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 }
Svetoslavb3038ec2013-02-13 14:39:30 -0800207 }
208 }
209
Svetoslav6a08a122013-05-03 11:24:26 -0700210 private long getNextIdleMaintenanceIntervalStartFromNow() {
211 return mLastIdleMaintenanceStartTimeMillis + MIN_IDLE_MAINTENANCE_INTERVAL_MILLIS
212 - SystemClock.elapsedRealtime();
213 }
214
Svetoslavb3038ec2013-02-13 14:39:30 -0800215 private void sendIdleMaintenanceStartIntent() {
Svetoslavb3038ec2013-02-13 14:39:30 -0800216 mWakeLock.acquire();
Dianne Hackborn35f72be2013-09-16 10:57:39 -0700217 try {
218 ActivityManagerNative.getDefault().performIdleMaintenance();
219 } catch (RemoteException e) {
220 }
Svetoslav6a08a122013-05-03 11:24:26 -0700221 mContext.sendOrderedBroadcastAsUser(sIdleMaintenanceStartIntent, UserHandle.ALL,
Svetoslavb3038ec2013-02-13 14:39:30 -0800222 null, this, mHandler, Activity.RESULT_OK, null, null);
223 }
224
225 private void sendIdleMaintenanceEndIntent() {
Svetoslavb3038ec2013-02-13 14:39:30 -0800226 mWakeLock.acquire();
Svetoslav6a08a122013-05-03 11:24:26 -0700227 mContext.sendOrderedBroadcastAsUser(sIdleMaintenanceEndIntent, UserHandle.ALL,
Svetoslavb3038ec2013-02-13 14:39:30 -0800228 null, this, mHandler, Activity.RESULT_OK, null, null);
229 }
230
Dianne Hackborn35f72be2013-09-16 10:57:39 -0700231 private boolean deviceStatePermitsIdleMaintenanceStart(boolean noisy) {
Svetoslav6a08a122013-05-03 11:24:26 -0700232 final int minBatteryLevel = isBatteryCharging()
Svetoslavf23b64d2013-04-25 14:45:54 -0700233 ? MIN_BATTERY_LEVEL_IDLE_MAINTENANCE_START_CHARGING
234 : MIN_BATTERY_LEVEL_IDLE_MAINTENANCE_START_NOT_CHARGING;
Dianne Hackborn35f72be2013-09-16 10:57:39 -0700235 boolean allowed = (mLastUserActivityElapsedTimeMillis != LAST_USER_ACTIVITY_TIME_INVALID
Svetoslav6a08a122013-05-03 11:24:26 -0700236 && mBatteryService.getBatteryLevel() > minBatteryLevel);
Dianne Hackborn35f72be2013-09-16 10:57:39 -0700237 if (!allowed && noisy) {
238 Slog.i("IdleMaintenance", "Idle maintenance not allowed due to power");
239 }
240 return allowed;
Svetoslavb3038ec2013-02-13 14:39:30 -0800241 }
242
Dianne Hackborn35f72be2013-09-16 10:57:39 -0700243 private boolean lastUserActivityPermitsIdleMaintenanceStart(boolean noisy) {
Svetoslav6a08a122013-05-03 11:24:26 -0700244 // The last time the user poked the device is above the threshold.
Dianne Hackborn35f72be2013-09-16 10:57:39 -0700245 boolean allowed = (mLastUserActivityElapsedTimeMillis != LAST_USER_ACTIVITY_TIME_INVALID
Svetoslav6a08a122013-05-03 11:24:26 -0700246 && SystemClock.elapsedRealtime() - mLastUserActivityElapsedTimeMillis
247 > MIN_USER_INACTIVITY_IDLE_MAINTENANCE_START);
Dianne Hackborn35f72be2013-09-16 10:57:39 -0700248 if (!allowed && noisy) {
249 Slog.i("IdleMaintenance", "Idle maintenance not allowed due to last user activity");
250 }
251 return allowed;
Svetoslavb3038ec2013-02-13 14:39:30 -0800252 }
253
Dianne Hackborn35f72be2013-09-16 10:57:39 -0700254 private boolean lastRunPermitsIdleMaintenanceStart(boolean noisy) {
Svetoslav6a08a122013-05-03 11:24:26 -0700255 // Enough time passed since the last maintenance run.
Dianne Hackborn35f72be2013-09-16 10:57:39 -0700256 boolean allowed = SystemClock.elapsedRealtime() - mLastIdleMaintenanceStartTimeMillis
Svetoslav6a08a122013-05-03 11:24:26 -0700257 > MIN_IDLE_MAINTENANCE_INTERVAL_MILLIS;
Dianne Hackborn35f72be2013-09-16 10:57:39 -0700258 if (!allowed && noisy) {
259 Slog.i("IdleMaintenance", "Idle maintenance not allowed due time since last");
260 }
261 return allowed;
Svetoslav6a08a122013-05-03 11:24:26 -0700262 }
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;
Svetoslavb3038ec2013-02-13 14:39:30 -0800279 }
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)) {
Svetoslav6a08a122013-05-03 11:24:26 -0700288 // 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) {
Dianne Hackborn35f72be2013-09-16 10:57:39 -0700295 updateIdleMaintenanceState(false);
Svetoslav6a08a122013-05-03 11:24:26 -0700296 }
Svetoslavb3038ec2013-02-13 14:39:30 -0800297 } else if (Intent.ACTION_SCREEN_ON.equals(action)
298 || Intent.ACTION_DREAMING_STOPPED.equals(action)) {
299 mLastUserActivityElapsedTimeMillis = LAST_USER_ACTIVITY_TIME_INVALID;
Svetoslav6a08a122013-05-03 11:24:26 -0700300 // 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.
Dianne Hackborn35f72be2013-09-16 10:57:39 -0700305 updateIdleMaintenanceState(false);
Svetoslavb3038ec2013-02-13 14:39:30 -0800306 } else if (Intent.ACTION_SCREEN_OFF.equals(action)
307 || Intent.ACTION_DREAMING_STARTED.equals(action)) {
308 mLastUserActivityElapsedTimeMillis = SystemClock.elapsedRealtime();
Svetoslav6a08a122013-05-03 11:24:26 -0700309 // 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)) {
Dianne Hackborn35f72be2013-09-16 10:57:39 -0700314 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);
Svetoslavb3038ec2013-02-13 14:39:30 -0800320 } else if (Intent.ACTION_IDLE_MAINTENANCE_START.equals(action)
321 || Intent.ACTION_IDLE_MAINTENANCE_END.equals(action)) {
Svetoslav6a08a122013-05-03 11:24:26 -0700322 // We were holding a wake lock while broadcasting the idle maintenance
323 // intents but now that we finished the broadcast release the wake lock.
Svetoslavb3038ec2013-02-13 14:39:30 -0800324 mWakeLock.release();
Svetoslavb3038ec2013-02-13 14:39:30 -0800325 }
Svetoslavb3038ec2013-02-13 14:39:30 -0800326 }
327}