blob: 584d4bc5f4c87ab364af5e6e5016aa48fd518fbe [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;
Svetoslav6a08a122013-05-03 11:24:26 -070020import android.app.AlarmManager;
21import android.app.PendingIntent;
Svetoslavb3038ec2013-02-13 14:39:30 -080022import android.content.BroadcastReceiver;
23import android.content.Context;
24import android.content.Intent;
25import android.content.IntentFilter;
Svetoslavb3038ec2013-02-13 14:39:30 -080026import android.os.Handler;
27import android.os.Looper;
28import android.os.PowerManager;
29import android.os.PowerManager.WakeLock;
30import android.os.SystemClock;
31import android.os.UserHandle;
32import android.util.Log;
33
Svetoslavb3038ec2013-02-13 14:39:30 -080034/**
35 * This service observes the device state and when applicable sends
36 * broadcasts at the beginning and at the end of a period during which
37 * observers can perform idle maintenance tasks. Typical use of the
38 * idle maintenance is to perform somehow expensive tasks that can be
39 * postponed to a moment when they will not degrade user experience.
40 *
41 * The current implementation is very simple. The start of a maintenance
42 * window is announced if: the screen is off or showing a dream AND the
43 * battery level is more than twenty percent AND at least one hour passed
Svetoslavb3038ec2013-02-13 14:39:30 -080044 * activity).
45 *
46 * The end of a maintenance window is announced only if: a start was
47 * announced AND the screen turned on or a dream was stopped.
48 */
49public class IdleMaintenanceService extends BroadcastReceiver {
50
Svetoslav6a08a122013-05-03 11:24:26 -070051 private static final boolean DEBUG = false;
Svetoslavb3038ec2013-02-13 14:39:30 -080052
53 private static final String LOG_TAG = IdleMaintenanceService.class.getSimpleName();
54
55 private static final int LAST_USER_ACTIVITY_TIME_INVALID = -1;
56
Svetoslav6a08a122013-05-03 11:24:26 -070057 private static final long MIN_IDLE_MAINTENANCE_INTERVAL_MILLIS = 24 * 60 * 60 * 1000; // 1 day
Svetoslavb3038ec2013-02-13 14:39:30 -080058
Svetoslavf23b64d2013-04-25 14:45:54 -070059 private static final int MIN_BATTERY_LEVEL_IDLE_MAINTENANCE_START_CHARGING = 30; // percent
60
61 private static final int MIN_BATTERY_LEVEL_IDLE_MAINTENANCE_START_NOT_CHARGING = 80; // percent
62
Svetoslav6a08a122013-05-03 11:24:26 -070063 private static final int MIN_BATTERY_LEVEL_IDLE_MAINTENANCE_RUNNING = 20; // percent
Svetoslavf23b64d2013-04-25 14:45:54 -070064
Svetoslav6a08a122013-05-03 11:24:26 -070065 private static final long MIN_USER_INACTIVITY_IDLE_MAINTENANCE_START = 71 * 60 * 1000; // 71 min
Svetoslavb3038ec2013-02-13 14:39:30 -080066
Svetoslav6a08a122013-05-03 11:24:26 -070067 private static final long MAX_IDLE_MAINTENANCE_DURATION = 71 * 60 * 1000; // 71 min
Svetoslavb3038ec2013-02-13 14:39:30 -080068
Svetoslav6a08a122013-05-03 11:24:26 -070069 private static final String ACTION_UPDATE_IDLE_MAINTENANCE_STATE =
70 "com.android.server.IdleMaintenanceService.action.UPDATE_IDLE_MAINTENANCE_STATE";
71
72 private static final Intent sIdleMaintenanceStartIntent;
73 static {
74 sIdleMaintenanceStartIntent = new Intent(Intent.ACTION_IDLE_MAINTENANCE_START);
75 sIdleMaintenanceStartIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
76 };
77
78 private static final Intent sIdleMaintenanceEndIntent;
79 static {
80 sIdleMaintenanceEndIntent = new Intent(Intent.ACTION_IDLE_MAINTENANCE_END);
81 sIdleMaintenanceEndIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
82 }
83
84 private final AlarmManager mAlarmService;
85
86 private final BatteryService mBatteryService;
87
88 private final PendingIntent mUpdateIdleMaintenanceStatePendingIntent;
Svetoslavb3038ec2013-02-13 14:39:30 -080089
90 private final Context mContext;
91
92 private final WakeLock mWakeLock;
93
94 private final Handler mHandler;
95
Svetoslav6a08a122013-05-03 11:24:26 -070096 private long mLastIdleMaintenanceStartTimeMillis;
Svetoslavb3038ec2013-02-13 14:39:30 -080097
98 private long mLastUserActivityElapsedTimeMillis = LAST_USER_ACTIVITY_TIME_INVALID;
99
Svetoslavb3038ec2013-02-13 14:39:30 -0800100 private boolean mIdleMaintenanceStarted;
101
Svetoslav6a08a122013-05-03 11:24:26 -0700102 public IdleMaintenanceService(Context context, BatteryService batteryService) {
Svetoslavb3038ec2013-02-13 14:39:30 -0800103 mContext = context;
Svetoslav6a08a122013-05-03 11:24:26 -0700104 mBatteryService = batteryService;
105
106 mAlarmService = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
Svetoslavb3038ec2013-02-13 14:39:30 -0800107
108 PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
109 mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, LOG_TAG);
110
111 mHandler = new Handler(mContext.getMainLooper());
112
Svetoslav6a08a122013-05-03 11:24:26 -0700113 Intent intent = new Intent(ACTION_UPDATE_IDLE_MAINTENANCE_STATE);
114 intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
115 mUpdateIdleMaintenanceStatePendingIntent = PendingIntent.getBroadcast(mContext, 0,
116 intent, PendingIntent.FLAG_UPDATE_CURRENT);
117
Svetoslavb3038ec2013-02-13 14:39:30 -0800118 register(mContext.getMainLooper());
119 }
120
121 public void register(Looper looper) {
122 IntentFilter intentFilter = new IntentFilter();
123
Svetoslav6a08a122013-05-03 11:24:26 -0700124 // Alarm actions.
125 intentFilter.addAction(ACTION_UPDATE_IDLE_MAINTENANCE_STATE);
126
Svetoslavb3038ec2013-02-13 14:39:30 -0800127 // Battery actions.
128 intentFilter.addAction(Intent.ACTION_BATTERY_CHANGED);
129
130 // Screen actions.
131 intentFilter.addAction(Intent.ACTION_SCREEN_ON);
132 intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
133
134 // Dream actions.
135 intentFilter.addAction(Intent.ACTION_DREAMING_STARTED);
136 intentFilter.addAction(Intent.ACTION_DREAMING_STOPPED);
137
138 mContext.registerReceiverAsUser(this, UserHandle.ALL,
139 intentFilter, null, new Handler(looper));
140 }
141
Svetoslav6a08a122013-05-03 11:24:26 -0700142 private void scheduleUpdateIdleMaintenanceState(long delayMillis) {
143 final long triggetRealTimeMillis = SystemClock.elapsedRealtime() + delayMillis;
144 mAlarmService.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggetRealTimeMillis,
145 mUpdateIdleMaintenanceStatePendingIntent);
146 }
147
148 private void unscheduleUpdateIdleMaintenanceState() {
149 mAlarmService.cancel(mUpdateIdleMaintenanceStatePendingIntent);
150 }
151
Svetoslavb3038ec2013-02-13 14:39:30 -0800152 private void updateIdleMaintenanceState() {
153 if (mIdleMaintenanceStarted) {
Svetoslav6a08a122013-05-03 11:24:26 -0700154 // Idle maintenance can be interrupted by user activity, or duration
155 // time out, or low battery.
156 if (!lastUserActivityPermitsIdleMaintenanceRunning()
157 || !batteryLevelAndMaintenanceTimeoutPermitsIdleMaintenanceRunning()) {
158 unscheduleUpdateIdleMaintenanceState();
Svetoslavb3038ec2013-02-13 14:39:30 -0800159 mIdleMaintenanceStarted = false;
Svetoslavf23b64d2013-04-25 14:45:54 -0700160 EventLogTags.writeIdleMaintenanceWindowFinish(SystemClock.elapsedRealtime(),
Svetoslav6a08a122013-05-03 11:24:26 -0700161 mLastUserActivityElapsedTimeMillis, mBatteryService.getBatteryLevel(),
162 isBatteryCharging() ? 1 : 0);
Svetoslavb3038ec2013-02-13 14:39:30 -0800163 sendIdleMaintenanceEndIntent();
Svetoslav6a08a122013-05-03 11:24:26 -0700164 // We stopped since we don't have enough battery or timed out but the
165 // user is not using the device, so we should be able to run maintenance
166 // in the next maintenance window since the battery may be charged
167 // without interaction and the min interval between maintenances passed.
168 if (!batteryLevelAndMaintenanceTimeoutPermitsIdleMaintenanceRunning()) {
169 scheduleUpdateIdleMaintenanceState(
170 getNextIdleMaintenanceIntervalStartFromNow());
171 }
Svetoslavb3038ec2013-02-13 14:39:30 -0800172 }
Svetoslavf23b64d2013-04-25 14:45:54 -0700173 } else if (deviceStatePermitsIdleMaintenanceStart()
Svetoslavb3038ec2013-02-13 14:39:30 -0800174 && lastUserActivityPermitsIdleMaintenanceStart()
175 && lastRunPermitsIdleMaintenanceStart()) {
Svetoslav6a08a122013-05-03 11:24:26 -0700176 // Now that we started idle maintenance, we should schedule another
177 // update for the moment when the idle maintenance times out.
178 scheduleUpdateIdleMaintenanceState(MAX_IDLE_MAINTENANCE_DURATION);
Svetoslavb3038ec2013-02-13 14:39:30 -0800179 mIdleMaintenanceStarted = true;
Svetoslavf23b64d2013-04-25 14:45:54 -0700180 EventLogTags.writeIdleMaintenanceWindowStart(SystemClock.elapsedRealtime(),
Svetoslav6a08a122013-05-03 11:24:26 -0700181 mLastUserActivityElapsedTimeMillis, mBatteryService.getBatteryLevel(),
182 isBatteryCharging() ? 1 : 0);
Svetoslavf23b64d2013-04-25 14:45:54 -0700183 mLastIdleMaintenanceStartTimeMillis = SystemClock.elapsedRealtime();
Svetoslavb3038ec2013-02-13 14:39:30 -0800184 sendIdleMaintenanceStartIntent();
Svetoslav6a08a122013-05-03 11:24:26 -0700185 } else if (lastUserActivityPermitsIdleMaintenanceStart()) {
186 if (lastRunPermitsIdleMaintenanceStart()) {
187 // The user does not use the device and we did not run maintenance in more
188 // than the min interval between runs, so schedule an update - maybe the
189 // battery will be charged latter.
190 scheduleUpdateIdleMaintenanceState(MIN_USER_INACTIVITY_IDLE_MAINTENANCE_START);
191 } else {
192 // The user does not use the device but we have run maintenance in the min
193 // interval between runs, so schedule an update after the min interval ends.
194 scheduleUpdateIdleMaintenanceState(
195 getNextIdleMaintenanceIntervalStartFromNow());
196 }
Svetoslavb3038ec2013-02-13 14:39:30 -0800197 }
198 }
199
Svetoslav6a08a122013-05-03 11:24:26 -0700200 private long getNextIdleMaintenanceIntervalStartFromNow() {
201 return mLastIdleMaintenanceStartTimeMillis + MIN_IDLE_MAINTENANCE_INTERVAL_MILLIS
202 - SystemClock.elapsedRealtime();
203 }
204
Svetoslavb3038ec2013-02-13 14:39:30 -0800205 private void sendIdleMaintenanceStartIntent() {
Svetoslavb3038ec2013-02-13 14:39:30 -0800206 mWakeLock.acquire();
Svetoslav6a08a122013-05-03 11:24:26 -0700207 mContext.sendOrderedBroadcastAsUser(sIdleMaintenanceStartIntent, UserHandle.ALL,
Svetoslavb3038ec2013-02-13 14:39:30 -0800208 null, this, mHandler, Activity.RESULT_OK, null, null);
209 }
210
211 private void sendIdleMaintenanceEndIntent() {
Svetoslavb3038ec2013-02-13 14:39:30 -0800212 mWakeLock.acquire();
Svetoslav6a08a122013-05-03 11:24:26 -0700213 mContext.sendOrderedBroadcastAsUser(sIdleMaintenanceEndIntent, UserHandle.ALL,
Svetoslavb3038ec2013-02-13 14:39:30 -0800214 null, this, mHandler, Activity.RESULT_OK, null, null);
215 }
216
Svetoslavf23b64d2013-04-25 14:45:54 -0700217 private boolean deviceStatePermitsIdleMaintenanceStart() {
Svetoslav6a08a122013-05-03 11:24:26 -0700218 final int minBatteryLevel = isBatteryCharging()
Svetoslavf23b64d2013-04-25 14:45:54 -0700219 ? MIN_BATTERY_LEVEL_IDLE_MAINTENANCE_START_CHARGING
220 : MIN_BATTERY_LEVEL_IDLE_MAINTENANCE_START_NOT_CHARGING;
Svetoslavb3038ec2013-02-13 14:39:30 -0800221 return (mLastUserActivityElapsedTimeMillis != LAST_USER_ACTIVITY_TIME_INVALID
Svetoslav6a08a122013-05-03 11:24:26 -0700222 && mBatteryService.getBatteryLevel() > minBatteryLevel);
Svetoslavb3038ec2013-02-13 14:39:30 -0800223 }
224
225 private boolean lastUserActivityPermitsIdleMaintenanceStart() {
Svetoslav6a08a122013-05-03 11:24:26 -0700226 // The last time the user poked the device is above the threshold.
227 return (mLastUserActivityElapsedTimeMillis != LAST_USER_ACTIVITY_TIME_INVALID
228 && SystemClock.elapsedRealtime() - mLastUserActivityElapsedTimeMillis
229 > MIN_USER_INACTIVITY_IDLE_MAINTENANCE_START);
Svetoslavb3038ec2013-02-13 14:39:30 -0800230 }
231
232 private boolean lastRunPermitsIdleMaintenanceStart() {
Svetoslav6a08a122013-05-03 11:24:26 -0700233 // Enough time passed since the last maintenance run.
234 return SystemClock.elapsedRealtime() - mLastIdleMaintenanceStartTimeMillis
235 > MIN_IDLE_MAINTENANCE_INTERVAL_MILLIS;
236 }
237
238 private boolean lastUserActivityPermitsIdleMaintenanceRunning() {
239 // The user is not using the device.
240 return (mLastUserActivityElapsedTimeMillis != LAST_USER_ACTIVITY_TIME_INVALID);
241 }
242
243 private boolean batteryLevelAndMaintenanceTimeoutPermitsIdleMaintenanceRunning() {
244 // Battery not too low and the maintenance duration did not timeout.
245 return (mBatteryService.getBatteryLevel() > MIN_BATTERY_LEVEL_IDLE_MAINTENANCE_RUNNING
246 && mLastIdleMaintenanceStartTimeMillis + MAX_IDLE_MAINTENANCE_DURATION
247 > SystemClock.elapsedRealtime());
248 }
249
250 private boolean isBatteryCharging() {
251 return mBatteryService.getPlugType() > 0
252 && mBatteryService.getInvalidCharger() == 0;
Svetoslavb3038ec2013-02-13 14:39:30 -0800253 }
254
255 @Override
256 public void onReceive(Context context, Intent intent) {
257 if (DEBUG) {
258 Log.i(LOG_TAG, intent.getAction());
259 }
260 String action = intent.getAction();
261 if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
Svetoslav6a08a122013-05-03 11:24:26 -0700262 // We care about battery only if maintenance is in progress so we can
263 // stop it if battery is too low. Note that here we assume that the
264 // maintenance clients are properly holding a wake lock. We will
265 // refactor the maintenance to use services instead of intents for the
266 // next release. The only client for this for now is internal an holds
267 // a wake lock correctly.
268 if (mIdleMaintenanceStarted) {
269 updateIdleMaintenanceState();
270 }
Svetoslavb3038ec2013-02-13 14:39:30 -0800271 } else if (Intent.ACTION_SCREEN_ON.equals(action)
272 || Intent.ACTION_DREAMING_STOPPED.equals(action)) {
273 mLastUserActivityElapsedTimeMillis = LAST_USER_ACTIVITY_TIME_INVALID;
Svetoslav6a08a122013-05-03 11:24:26 -0700274 // Unschedule any future updates since we already know that maintenance
275 // cannot be performed since the user is back.
276 unscheduleUpdateIdleMaintenanceState();
277 // If the screen went on/stopped dreaming, we know the user is using the
278 // device which means that idle maintenance should be stopped if running.
279 updateIdleMaintenanceState();
Svetoslavb3038ec2013-02-13 14:39:30 -0800280 } else if (Intent.ACTION_SCREEN_OFF.equals(action)
281 || Intent.ACTION_DREAMING_STARTED.equals(action)) {
282 mLastUserActivityElapsedTimeMillis = SystemClock.elapsedRealtime();
Svetoslav6a08a122013-05-03 11:24:26 -0700283 // If screen went off/started dreaming, we may be able to start idle maintenance
284 // after the minimal user inactivity elapses. We schedule an alarm for when
285 // this timeout elapses since the device may go to sleep by then.
286 scheduleUpdateIdleMaintenanceState(MIN_USER_INACTIVITY_IDLE_MAINTENANCE_START);
287 } else if (ACTION_UPDATE_IDLE_MAINTENANCE_STATE.equals(action)) {
288 updateIdleMaintenanceState();
Svetoslavb3038ec2013-02-13 14:39:30 -0800289 } else if (Intent.ACTION_IDLE_MAINTENANCE_START.equals(action)
290 || Intent.ACTION_IDLE_MAINTENANCE_END.equals(action)) {
Svetoslav6a08a122013-05-03 11:24:26 -0700291 // We were holding a wake lock while broadcasting the idle maintenance
292 // intents but now that we finished the broadcast release the wake lock.
Svetoslavb3038ec2013-02-13 14:39:30 -0800293 mWakeLock.release();
Svetoslavb3038ec2013-02-13 14:39:30 -0800294 }
Svetoslavb3038ec2013-02-13 14:39:30 -0800295 }
296}