blob: acc6abe89b33ad2155f7bd1840178a7b07b8f209 [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
Svetoslav6a08a122013-05-03 11:24:26 -070019import android.app.AlarmManager;
20import android.app.PendingIntent;
Christopher Tated417d622013-08-19 16:14:25 -070021import android.app.maintenance.IIdleCallback;
22import android.app.maintenance.IIdleService;
23import android.app.maintenance.IdleService;
Svetoslavb3038ec2013-02-13 14:39:30 -080024import android.content.BroadcastReceiver;
Christopher Tated417d622013-08-19 16:14:25 -070025import android.content.ComponentName;
Svetoslavb3038ec2013-02-13 14:39:30 -080026import android.content.Context;
27import android.content.Intent;
28import android.content.IntentFilter;
Christopher Tated417d622013-08-19 16:14:25 -070029import android.content.ServiceConnection;
30import android.content.pm.PackageManager;
31import android.content.pm.ResolveInfo;
Svetoslavb3038ec2013-02-13 14:39:30 -080032import android.os.Handler;
Svetoslavb3038ec2013-02-13 14:39:30 -080033import android.os.PowerManager;
34import android.os.PowerManager.WakeLock;
Christopher Tated417d622013-08-19 16:14:25 -070035import android.os.IBinder;
36import android.os.Looper;
37import android.os.Message;
38import android.os.Process;
Dianne Hackborn35f72be2013-09-16 10:57:39 -070039import android.os.RemoteException;
Svetoslavb3038ec2013-02-13 14:39:30 -080040import android.os.SystemClock;
41import android.os.UserHandle;
Christopher Tated417d622013-08-19 16:14:25 -070042import android.os.WorkSource;
43import android.util.ArrayMap;
Svetoslavb3038ec2013-02-13 14:39:30 -080044import android.util.Log;
Dianne Hackborn35f72be2013-09-16 10:57:39 -070045import android.util.Slog;
Christopher Tated417d622013-08-19 16:14:25 -070046import android.util.SparseArray;
47
48import java.util.LinkedList;
49import java.util.List;
50import java.util.Random;
Svetoslavb3038ec2013-02-13 14:39:30 -080051
Svetoslavb3038ec2013-02-13 14:39:30 -080052/**
53 * This service observes the device state and when applicable sends
54 * broadcasts at the beginning and at the end of a period during which
55 * observers can perform idle maintenance tasks. Typical use of the
56 * idle maintenance is to perform somehow expensive tasks that can be
57 * postponed to a moment when they will not degrade user experience.
58 *
59 * The current implementation is very simple. The start of a maintenance
60 * window is announced if: the screen is off or showing a dream AND the
61 * battery level is more than twenty percent AND at least one hour passed
Svetoslavb3038ec2013-02-13 14:39:30 -080062 * activity).
63 *
64 * The end of a maintenance window is announced only if: a start was
65 * announced AND the screen turned on or a dream was stopped.
Christopher Tated417d622013-08-19 16:14:25 -070066 *
67 * Method naming note:
68 * Methods whose name ends with "Tm" must only be called from the main thread.
Svetoslavb3038ec2013-02-13 14:39:30 -080069 */
70public class IdleMaintenanceService extends BroadcastReceiver {
71
Svetoslav6a08a122013-05-03 11:24:26 -070072 private static final boolean DEBUG = false;
Svetoslavb3038ec2013-02-13 14:39:30 -080073
Christopher Tated417d622013-08-19 16:14:25 -070074 private static final String TAG = IdleMaintenanceService.class.getSimpleName();
Svetoslavb3038ec2013-02-13 14:39:30 -080075
76 private static final int LAST_USER_ACTIVITY_TIME_INVALID = -1;
77
Svetoslav6a08a122013-05-03 11:24:26 -070078 private static final long MIN_IDLE_MAINTENANCE_INTERVAL_MILLIS = 24 * 60 * 60 * 1000; // 1 day
Svetoslavb3038ec2013-02-13 14:39:30 -080079
Svetoslavf23b64d2013-04-25 14:45:54 -070080 private static final int MIN_BATTERY_LEVEL_IDLE_MAINTENANCE_START_CHARGING = 30; // percent
81
82 private static final int MIN_BATTERY_LEVEL_IDLE_MAINTENANCE_START_NOT_CHARGING = 80; // percent
83
Svetoslav6a08a122013-05-03 11:24:26 -070084 private static final int MIN_BATTERY_LEVEL_IDLE_MAINTENANCE_RUNNING = 20; // percent
Svetoslavf23b64d2013-04-25 14:45:54 -070085
Svetoslav6a08a122013-05-03 11:24:26 -070086 private static final long MIN_USER_INACTIVITY_IDLE_MAINTENANCE_START = 71 * 60 * 1000; // 71 min
Svetoslavb3038ec2013-02-13 14:39:30 -080087
Svetoslav6a08a122013-05-03 11:24:26 -070088 private static final long MAX_IDLE_MAINTENANCE_DURATION = 71 * 60 * 1000; // 71 min
Svetoslavb3038ec2013-02-13 14:39:30 -080089
Svetoslav6a08a122013-05-03 11:24:26 -070090 private static final String ACTION_UPDATE_IDLE_MAINTENANCE_STATE =
91 "com.android.server.IdleMaintenanceService.action.UPDATE_IDLE_MAINTENANCE_STATE";
92
Dianne Hackborn35f72be2013-09-16 10:57:39 -070093 private static final String ACTION_FORCE_IDLE_MAINTENANCE =
94 "com.android.server.IdleMaintenanceService.action.FORCE_IDLE_MAINTENANCE";
95
Christopher Tated417d622013-08-19 16:14:25 -070096 static final int MSG_OP_COMPLETE = 1;
97 static final int MSG_IDLE_FINISHED = 2;
98 static final int MSG_TIMEOUT = 3;
Svetoslav6a08a122013-05-03 11:24:26 -070099
Christopher Tated417d622013-08-19 16:14:25 -0700100 // when a timeout happened, what were we expecting?
101 static final int VERB_BINDING = 1;
102 static final int VERB_IDLING = 2;
103 static final int VERB_ENDING = 3;
104
105 // What are our relevant timeouts / allocated slices?
106 static final long OP_TIMEOUT = 8 * 1000; // 8 seconds to bind or ack the start
107 static final long IDLE_TIMESLICE = 10 * 60 * 1000; // ten minutes for each idler
Svetoslav6a08a122013-05-03 11:24:26 -0700108
109 private final AlarmManager mAlarmService;
Svetoslav6a08a122013-05-03 11:24:26 -0700110 private final BatteryService mBatteryService;
Svetoslav6a08a122013-05-03 11:24:26 -0700111 private final PendingIntent mUpdateIdleMaintenanceStatePendingIntent;
Svetoslavb3038ec2013-02-13 14:39:30 -0800112 private final Context mContext;
Svetoslavb3038ec2013-02-13 14:39:30 -0800113 private final WakeLock mWakeLock;
Christopher Tated417d622013-08-19 16:14:25 -0700114 private final WorkSource mSystemWorkSource = new WorkSource(Process.myUid());
Svetoslavb3038ec2013-02-13 14:39:30 -0800115
Svetoslav6a08a122013-05-03 11:24:26 -0700116 private long mLastIdleMaintenanceStartTimeMillis;
Svetoslavb3038ec2013-02-13 14:39:30 -0800117 private long mLastUserActivityElapsedTimeMillis = LAST_USER_ACTIVITY_TIME_INVALID;
Svetoslavb3038ec2013-02-13 14:39:30 -0800118 private boolean mIdleMaintenanceStarted;
119
Christopher Tated417d622013-08-19 16:14:25 -0700120 final IdleCallback mCallback;
121 final Handler mHandler;
122
123 final Random mTokenGenerator = new Random();
124
125 int makeToken() {
126 int token;
127 do {
128 token = mTokenGenerator.nextInt(Integer.MAX_VALUE);
129 } while (token == 0);
130 return token;
131 }
132
133 class ActiveTask {
134 public IdleServiceInfo who;
135 public int verb;
136 public int token;
137
138 ActiveTask(IdleServiceInfo target, int action) {
139 who = target;
140 verb = action;
141 token = makeToken();
142 }
143
144 @Override
145 public String toString() {
146 return "ActiveTask{" + Integer.toHexString(this.hashCode())
147 + " : verb=" + verb
148 + " : token=" + token
149 + " : "+ who + "}";
150 }
151 }
152
153 // What operations are in flight?
154 final SparseArray<ActiveTask> mPendingOperations = new SparseArray<ActiveTask>();
155
156 // Idle service queue management
157 class IdleServiceInfo {
158 public final ComponentName componentName;
159 public final int uid;
160 public IIdleService service;
161
162 IdleServiceInfo(ResolveInfo info, ComponentName cname) {
163 componentName = cname; // derived from 'info' but this avoids an extra object
164 uid = info.serviceInfo.applicationInfo.uid;
165 service = null;
166 }
167
168 @Override
169 public int hashCode() {
170 return componentName.hashCode();
171 }
172
173 @Override
174 public String toString() {
175 return "IdleServiceInfo{" + componentName
176 + " / " + (service == null ? "null" : service.asBinder()) + "}";
177 }
178 }
179
180 final ArrayMap<ComponentName, IdleServiceInfo> mIdleServices =
181 new ArrayMap<ComponentName, IdleServiceInfo>();
182 final LinkedList<IdleServiceInfo> mIdleServiceQueue = new LinkedList<IdleServiceInfo>();
183 IdleServiceInfo mCurrentIdler; // set when we've committed to launching an idler
184 IdleServiceInfo mLastIdler; // end of queue when idling begins
185
186 void reportNoTimeout(int token, boolean result) {
187 final Message msg = mHandler.obtainMessage(MSG_OP_COMPLETE, result ? 1 : 0, token);
188 mHandler.sendMessage(msg);
189 }
190
191 // Binder acknowledgment trampoline
192 class IdleCallback extends IIdleCallback.Stub {
193 @Override
194 public void acknowledgeStart(int token, boolean result) throws RemoteException {
195 reportNoTimeout(token, result);
196 }
197
198 @Override
199 public void acknowledgeStop(int token) throws RemoteException {
200 reportNoTimeout(token, false);
201 }
202
203 @Override
204 public void idleFinished(int token) throws RemoteException {
205 if (DEBUG) {
206 Slog.v(TAG, "idleFinished: " + token);
207 }
208 final Message msg = mHandler.obtainMessage(MSG_IDLE_FINISHED, 0, token);
209 mHandler.sendMessage(msg);
210 }
211 }
212
213 // Stuff that we run on a Handler
214 class IdleHandler extends Handler {
215 public IdleHandler(Looper looper) {
216 super(looper);
217 }
218
219 @Override
220 public void handleMessage(Message msg) {
221 final int token = msg.arg2;
222
223 switch (msg.what) {
224 case MSG_OP_COMPLETE: {
225 if (DEBUG) {
226 Slog.i(TAG, "MSG_OP_COMPLETE of " + token);
227 }
228 ActiveTask task = mPendingOperations.get(token);
229 if (task != null) {
230 mPendingOperations.remove(token);
231 removeMessages(MSG_TIMEOUT);
232
233 handleOpCompleteTm(task, msg.arg1);
234 } else {
235 // Can happen in a race between timeout and actual
236 // (belated) completion of a "begin idling" or similar
237 // operation. In that state we've already processed the
238 // timeout, so we intentionally no-op here.
239 if (DEBUG) {
240 Slog.w(TAG, "Belated op-complete of " + token);
241 }
242 }
243 break;
244 }
245
246 case MSG_IDLE_FINISHED: {
247 if (DEBUG) {
248 Slog.i(TAG, "MSG_IDLE_FINISHED of " + token);
249 }
250 ActiveTask task = mPendingOperations.get(token);
251 if (task != null) {
252 if (DEBUG) {
253 Slog.i(TAG, "... removing task " + token);
254 }
255 mPendingOperations.remove(token);
256 removeMessages(MSG_TIMEOUT);
257
258 handleIdleFinishedTm(task);
259 } else {
260 // Can happen "legitimately" from an app explicitly calling
261 // idleFinished() after already having been told that its slice
262 // has ended.
263 if (DEBUG) {
264 Slog.w(TAG, "Belated idle-finished of " + token);
265 }
266 }
267 break;
268 }
269
270 case MSG_TIMEOUT: {
271 if (DEBUG) {
272 Slog.i(TAG, "MSG_TIMEOUT of " + token);
273 }
274 ActiveTask task = mPendingOperations.get(token);
275 if (task != null) {
276 mPendingOperations.remove(token);
277 removeMessages(MSG_OP_COMPLETE);
278
279 handleTimeoutTm(task);
280 } else {
281 // This one should not happen; we flushed timeout messages
282 // whenever we entered a state after which we have established
283 // that they are not appropriate.
284 Slog.w(TAG, "Unexpected timeout of " + token);
285 }
286 break;
287 }
288
289 default:
290 Slog.w(TAG, "Unknown message: " + msg.what);
291 }
292 }
293 }
294
295 void handleTimeoutTm(ActiveTask task) {
296 switch (task.verb) {
297 case VERB_BINDING: {
298 // We were trying to bind to this service, but it wedged or otherwise
299 // failed to respond in time. Let it stay in the queue for the next
300 // time around, but just give up on it for now and go on to the next.
301 startNextIdleServiceTm();
302 break;
303 }
304 case VERB_IDLING: {
305 // The service has reached the end of its designated idle timeslice.
306 // This is not considered an error.
307 if (DEBUG) {
308 Slog.i(TAG, "Idler reached end of timeslice: " + task.who);
309 }
310 sendEndIdleTm(task.who);
311 break;
312 }
313 case VERB_ENDING: {
314 if (mCurrentIdler == task.who) {
315 if (DEBUG) {
316 Slog.i(TAG, "Task timed out when ending; unbind needed");
317 }
318 handleIdleFinishedTm(task);
319 } else {
320 if (DEBUG) {
321 Slog.w(TAG, "Ending timeout for non-current idle service!");
322 }
323 }
324 break;
325 }
326 default: {
327 Slog.w(TAG, "Unknown timeout state " + task.verb);
328 break;
329 }
330 }
331 }
332
333 void handleOpCompleteTm(ActiveTask task, int result) {
334 if (DEBUG) {
335 Slog.i(TAG, "handleOpComplete : task=" + task + " result=" + result);
336 }
337 if (task.verb == VERB_IDLING) {
338 // If the service was told to begin idling and responded positively, then
339 // it has begun idling and will eventually either explicitly finish, or
340 // reach the end of its allotted timeslice. It's running free now, so we
341 // just schedule the idle-expiration timeout under the token it's already been
342 // given and let it keep going.
343 if (result != 0) {
344 scheduleOpTimeoutTm(task);
345 } else {
346 // The idle service has indicated that it does not, in fact,
347 // need to run at present, so we immediately indicate that it's
348 // to finish idling, and go on to the next idler.
349 if (DEBUG) {
350 Slog.i(TAG, "Idler declined idling; moving along");
351 }
352 sendEndIdleTm(task.who);
353 }
354 } else {
355 // In the idling case, the task will be cleared either as the result of a timeout
356 // or of an explicit idleFinished(). For all other operations (binding, ending) we
357 // are done with the task as such, so we remove it from our bookkeeping.
358 if (DEBUG) {
359 Slog.i(TAG, "Clearing task " + task);
360 }
361 mPendingOperations.remove(task.token);
362 if (task.verb == VERB_ENDING) {
363 // The last bit of handshaking around idle cessation for this target
364 handleIdleFinishedTm(task);
365 }
366 }
367 }
368
369 void handleIdleFinishedTm(ActiveTask task) {
370 final IdleServiceInfo who = task.who;
371 if (who == mCurrentIdler) {
372 if (DEBUG) {
373 Slog.i(TAG, "Current idler has finished: " + who);
374 Slog.i(TAG, "Attributing wakelock to system work source");
375 }
376 mContext.unbindService(mConnection);
377 startNextIdleServiceTm();
378 } else {
379 Slog.w(TAG, "finish from non-current idle service? " + who);
380 }
381 }
382
383 void updateIdleServiceQueueTm() {
384 if (DEBUG) {
385 Slog.i(TAG, "Updating idle service queue");
386 }
387 PackageManager pm = mContext.getPackageManager();
388 Intent idleIntent = new Intent(IdleService.SERVICE_INTERFACE);
389 List<ResolveInfo> services = pm.queryIntentServices(idleIntent, 0);
390 for (ResolveInfo info : services) {
391 if (info.serviceInfo != null) {
392 if (IdleService.PERMISSION_BIND.equals(info.serviceInfo.permission)) {
393 final ComponentName componentName = new ComponentName(
394 info.serviceInfo.packageName,
395 info.serviceInfo.name);
396 if (DEBUG) {
397 Slog.i(TAG, " - " + componentName);
398 }
399 if (!mIdleServices.containsKey(componentName)) {
400 if (DEBUG) {
401 Slog.i(TAG, " + not known; adding");
402 }
403 IdleServiceInfo serviceInfo = new IdleServiceInfo(info, componentName);
404 mIdleServices.put(componentName, serviceInfo);
405 mIdleServiceQueue.add(serviceInfo);
406 }
407 } else {
408 if (DEBUG) {
409 Slog.i(TAG, "Idle service " + info.serviceInfo
410 + " does not have required permission; ignoring");
411 }
412 }
413 }
414 }
415 }
416
417 void startNextIdleServiceTm() {
418 mWakeLock.setWorkSource(mSystemWorkSource);
419
420 if (mLastIdler == null) {
421 // we've run the queue; nothing more to do until the next idle interval.
422 if (DEBUG) {
423 Slog.i(TAG, "Queue already drained; nothing more to do");
424 }
425 return;
426 }
427
428 if (DEBUG) {
429 Slog.i(TAG, "startNextIdleService : last=" + mLastIdler + " cur=" + mCurrentIdler);
430 if (mIdleServiceQueue.size() > 0) {
431 int i = 0;
432 Slog.i(TAG, "Queue (" + mIdleServiceQueue.size() + "):");
433 for (IdleServiceInfo info : mIdleServiceQueue) {
434 Slog.i(TAG, " " + i + " : " + info);
435 i++;
436 }
437 }
438 }
439 if (mCurrentIdler != mLastIdler) {
440 if (mIdleServiceQueue.size() > 0) {
441 IdleServiceInfo target = mIdleServiceQueue.pop();
442 if (DEBUG) {
443 Slog.i(TAG, "starting next idle service " + target);
444 }
445 Intent idleIntent = new Intent(IdleService.SERVICE_INTERFACE);
446 idleIntent.setComponent(target.componentName);
447 mCurrentIdler = target;
448 ActiveTask task = new ActiveTask(target, VERB_BINDING);
449 scheduleOpTimeoutTm(task);
450 boolean bindOk = mContext.bindServiceAsUser(idleIntent, mConnection,
451 Context.BIND_AUTO_CREATE | Context.BIND_WAIVE_PRIORITY, UserHandle.OWNER);
452 if (!bindOk) {
453 if (DEBUG) {
454 Slog.w(TAG, "bindService() to " + target.componentName
455 + " failed");
456 }
457 } else {
458 mIdleServiceQueue.add(target); // at the end for next time
459 if (DEBUG) { Slog.i(TAG, "Attributing wakelock to target uid " + target.uid); }
460 mWakeLock.setWorkSource(new WorkSource(target.uid));
461 }
462 } else {
463 // Queue is empty but mLastIdler is non-null -- eeep. Clear *everything*
464 // and wind up until the next time around.
465 Slog.e(TAG, "Queue unexpectedly empty; resetting. last="
466 + mLastIdler + " cur=" + mCurrentIdler);
467 mHandler.removeMessages(MSG_TIMEOUT);
468 mPendingOperations.clear();
469 stopIdleMaintenanceTm();
470 }
471 } else {
472 // we've reached the place we started, so mark the queue as drained
473 if (DEBUG) {
474 Slog.i(TAG, "Reached end of queue.");
475 }
476 stopIdleMaintenanceTm();
477 }
478 }
479
480 void sendStartIdleTm(IdleServiceInfo who) {
481 ActiveTask task = new ActiveTask(who, VERB_IDLING);
482 scheduleOpTimeoutTm(task);
483 try {
484 who.service.startIdleMaintenance(mCallback, task.token);
485 } catch (RemoteException e) {
486 // We bound to it, but now we can't reach it. Bail and go on to the
487 // next service.
488 mContext.unbindService(mConnection);
489 if (DEBUG) { Slog.i(TAG, "Attributing wakelock to system work source"); }
490 mHandler.removeMessages(MSG_TIMEOUT);
491 startNextIdleServiceTm();
492 }
493 }
494
495 void sendEndIdleTm(IdleServiceInfo who) {
496 ActiveTask task = new ActiveTask(who, VERB_ENDING);
497 scheduleOpTimeoutTm(task);
498 if (DEBUG) {
499 Slog.i(TAG, "Sending end-idle to " + who);
500 }
501 try {
502 who.service.stopIdleMaintenance(mCallback, task.token);
503 } catch (RemoteException e) {
504 // We bound to it, but now we can't reach it. Bail and go on to the
505 // next service.
506 mContext.unbindService(mConnection);
507 if (DEBUG) { Slog.i(TAG, "Attributing wakelock to system work source"); }
508 mHandler.removeMessages(MSG_TIMEOUT);
509 startNextIdleServiceTm();
510 }
511 }
512
513 ServiceConnection mConnection = new ServiceConnection() {
514 @Override
515 public void onServiceConnected(ComponentName name, IBinder service) {
516 if (DEBUG) {
517 Slog.i(TAG, "onServiceConnected(" + name + ")");
518 }
519 IdleServiceInfo info = mIdleServices.get(name);
520 if (info != null) {
521 // Bound! Cancel the bind timeout
522 mHandler.removeMessages(MSG_TIMEOUT);
523 // Now tell it to start its idle work
524 info.service = IIdleService.Stub.asInterface(service);
525 sendStartIdleTm(info);
526 } else {
527 // We bound to a service we don't know about. That's ungood.
528 Slog.e(TAG, "Connected to unexpected component " + name);
529 mContext.unbindService(this);
530 }
531 }
532
533 @Override
534 public void onServiceDisconnected(ComponentName name) {
535 if (DEBUG) {
536 Slog.i(TAG, "onServiceDisconnected(" + name + ")");
537 }
538 IdleServiceInfo who = mIdleServices.get(name);
539 if (who == mCurrentIdler) {
540 // Hm, okay; they didn't tell us they were finished but they
541 // went away. Crashed, probably. Oh well. They're gone, so
542 // we can't finish them cleanly; just force things along.
543 Slog.w(TAG, "Idler unexpectedly vanished: " + mCurrentIdler);
544 mContext.unbindService(this);
545 mHandler.removeMessages(MSG_TIMEOUT);
546 startNextIdleServiceTm();
547 } else {
548 // Not the current idler, so we don't interrupt our process...
549 if (DEBUG) {
550 Slog.w(TAG, "Disconnect of abandoned or unexpected service " + name);
551 }
552 }
553 }
554 };
555
556 // Schedules a timeout / end-of-work based on the task verb
557 void scheduleOpTimeoutTm(ActiveTask task) {
558 final long timeoutMillis = (task.verb == VERB_IDLING) ? IDLE_TIMESLICE : OP_TIMEOUT;
559 if (DEBUG) {
560 Slog.i(TAG, "Scheduling timeout (token " + task.token
561 + " : verb " + task.verb + ") for " + task + " in " + timeoutMillis);
562 }
563 mPendingOperations.put(task.token, task);
564 mHandler.removeMessages(MSG_TIMEOUT);
565 final Message msg = mHandler.obtainMessage(MSG_TIMEOUT, 0, task.token);
566 mHandler.sendMessageDelayed(msg, timeoutMillis);
567 }
568
569 // -------------------------------------------------------------------------------
Svetoslav6a08a122013-05-03 11:24:26 -0700570 public IdleMaintenanceService(Context context, BatteryService batteryService) {
Svetoslavb3038ec2013-02-13 14:39:30 -0800571 mContext = context;
Svetoslav6a08a122013-05-03 11:24:26 -0700572 mBatteryService = batteryService;
573
574 mAlarmService = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
Svetoslavb3038ec2013-02-13 14:39:30 -0800575
576 PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
Christopher Tated417d622013-08-19 16:14:25 -0700577 mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
Svetoslavb3038ec2013-02-13 14:39:30 -0800578
Christopher Tated417d622013-08-19 16:14:25 -0700579 mHandler = new IdleHandler(mContext.getMainLooper());
580 mCallback = new IdleCallback();
Svetoslavb3038ec2013-02-13 14:39:30 -0800581
Svetoslav6a08a122013-05-03 11:24:26 -0700582 Intent intent = new Intent(ACTION_UPDATE_IDLE_MAINTENANCE_STATE);
583 intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
584 mUpdateIdleMaintenanceStatePendingIntent = PendingIntent.getBroadcast(mContext, 0,
585 intent, PendingIntent.FLAG_UPDATE_CURRENT);
586
Dianne Hackborn35f72be2013-09-16 10:57:39 -0700587 register(mHandler);
Svetoslavb3038ec2013-02-13 14:39:30 -0800588 }
589
Dianne Hackborn35f72be2013-09-16 10:57:39 -0700590 public void register(Handler handler) {
Svetoslavb3038ec2013-02-13 14:39:30 -0800591 IntentFilter intentFilter = new IntentFilter();
592
Svetoslav6a08a122013-05-03 11:24:26 -0700593 // Alarm actions.
594 intentFilter.addAction(ACTION_UPDATE_IDLE_MAINTENANCE_STATE);
595
Svetoslavb3038ec2013-02-13 14:39:30 -0800596 // Battery actions.
597 intentFilter.addAction(Intent.ACTION_BATTERY_CHANGED);
598
599 // Screen actions.
600 intentFilter.addAction(Intent.ACTION_SCREEN_ON);
601 intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
602
603 // Dream actions.
604 intentFilter.addAction(Intent.ACTION_DREAMING_STARTED);
605 intentFilter.addAction(Intent.ACTION_DREAMING_STOPPED);
606
607 mContext.registerReceiverAsUser(this, UserHandle.ALL,
Dianne Hackborn35f72be2013-09-16 10:57:39 -0700608 intentFilter, null, mHandler);
609
610 intentFilter = new IntentFilter();
611 intentFilter.addAction(ACTION_FORCE_IDLE_MAINTENANCE);
612 mContext.registerReceiverAsUser(this, UserHandle.ALL,
613 intentFilter, android.Manifest.permission.SET_ACTIVITY_WATCHER, mHandler);
Svetoslavb3038ec2013-02-13 14:39:30 -0800614 }
615
Svetoslav6a08a122013-05-03 11:24:26 -0700616 private void scheduleUpdateIdleMaintenanceState(long delayMillis) {
617 final long triggetRealTimeMillis = SystemClock.elapsedRealtime() + delayMillis;
618 mAlarmService.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggetRealTimeMillis,
619 mUpdateIdleMaintenanceStatePendingIntent);
620 }
621
622 private void unscheduleUpdateIdleMaintenanceState() {
623 mAlarmService.cancel(mUpdateIdleMaintenanceStatePendingIntent);
624 }
625
Christopher Tated417d622013-08-19 16:14:25 -0700626 private void updateIdleMaintenanceStateTm(boolean noisy) {
Svetoslavb3038ec2013-02-13 14:39:30 -0800627 if (mIdleMaintenanceStarted) {
Svetoslav6a08a122013-05-03 11:24:26 -0700628 // Idle maintenance can be interrupted by user activity, or duration
629 // time out, or low battery.
Christopher Tated417d622013-08-19 16:14:25 -0700630 final boolean batteryOk
631 = batteryLevelAndMaintenanceTimeoutPermitsIdleMaintenanceRunning();
632 if (!lastUserActivityPermitsIdleMaintenanceRunning() || !batteryOk) {
Svetoslav6a08a122013-05-03 11:24:26 -0700633 unscheduleUpdateIdleMaintenanceState();
Svetoslavb3038ec2013-02-13 14:39:30 -0800634 mIdleMaintenanceStarted = false;
Svetoslav6a08a122013-05-03 11:24:26 -0700635 // We stopped since we don't have enough battery or timed out but the
636 // user is not using the device, so we should be able to run maintenance
637 // in the next maintenance window since the battery may be charged
638 // without interaction and the min interval between maintenances passed.
Christopher Tated417d622013-08-19 16:14:25 -0700639 if (!batteryOk) {
Svetoslav6a08a122013-05-03 11:24:26 -0700640 scheduleUpdateIdleMaintenanceState(
641 getNextIdleMaintenanceIntervalStartFromNow());
642 }
Christopher Tated417d622013-08-19 16:14:25 -0700643
644 EventLogTags.writeIdleMaintenanceWindowFinish(SystemClock.elapsedRealtime(),
645 mLastUserActivityElapsedTimeMillis, mBatteryService.getBatteryLevel(),
646 isBatteryCharging() ? 1 : 0);
647 scheduleIdleFinishTm();
Svetoslavb3038ec2013-02-13 14:39:30 -0800648 }
Dianne Hackborn35f72be2013-09-16 10:57:39 -0700649 } else if (deviceStatePermitsIdleMaintenanceStart(noisy)
650 && lastUserActivityPermitsIdleMaintenanceStart(noisy)
651 && lastRunPermitsIdleMaintenanceStart(noisy)) {
Svetoslav6a08a122013-05-03 11:24:26 -0700652 // Now that we started idle maintenance, we should schedule another
653 // update for the moment when the idle maintenance times out.
654 scheduleUpdateIdleMaintenanceState(MAX_IDLE_MAINTENANCE_DURATION);
Svetoslavb3038ec2013-02-13 14:39:30 -0800655 mIdleMaintenanceStarted = true;
Svetoslavf23b64d2013-04-25 14:45:54 -0700656 EventLogTags.writeIdleMaintenanceWindowStart(SystemClock.elapsedRealtime(),
Svetoslav6a08a122013-05-03 11:24:26 -0700657 mLastUserActivityElapsedTimeMillis, mBatteryService.getBatteryLevel(),
658 isBatteryCharging() ? 1 : 0);
Svetoslavf23b64d2013-04-25 14:45:54 -0700659 mLastIdleMaintenanceStartTimeMillis = SystemClock.elapsedRealtime();
Christopher Tated417d622013-08-19 16:14:25 -0700660 startIdleMaintenanceTm();
Dianne Hackborn35f72be2013-09-16 10:57:39 -0700661 } else if (lastUserActivityPermitsIdleMaintenanceStart(noisy)) {
662 if (lastRunPermitsIdleMaintenanceStart(noisy)) {
Svetoslav6a08a122013-05-03 11:24:26 -0700663 // The user does not use the device and we did not run maintenance in more
664 // than the min interval between runs, so schedule an update - maybe the
665 // battery will be charged latter.
666 scheduleUpdateIdleMaintenanceState(MIN_USER_INACTIVITY_IDLE_MAINTENANCE_START);
667 } else {
668 // The user does not use the device but we have run maintenance in the min
669 // interval between runs, so schedule an update after the min interval ends.
670 scheduleUpdateIdleMaintenanceState(
671 getNextIdleMaintenanceIntervalStartFromNow());
672 }
Svetoslavb3038ec2013-02-13 14:39:30 -0800673 }
674 }
675
Christopher Tated417d622013-08-19 16:14:25 -0700676 void startIdleMaintenanceTm() {
677 if (DEBUG) {
678 Slog.i(TAG, "*** Starting idle maintenance ***");
679 }
680 if (DEBUG) { Slog.i(TAG, "Attributing wakelock to system work source"); }
681 mWakeLock.setWorkSource(mSystemWorkSource);
682 mWakeLock.acquire();
683 updateIdleServiceQueueTm();
684 mCurrentIdler = null;
685 mLastIdler = (mIdleServiceQueue.size() > 0) ? mIdleServiceQueue.peekLast() : null;
686 startNextIdleServiceTm();
687 }
688
689 // Start a graceful wind-down of the idle maintenance state: end the current idler
690 // and pretend that we've finished running the queue. If there's no current idler,
691 // this is a no-op.
692 void scheduleIdleFinishTm() {
693 if (mCurrentIdler != null) {
694 if (DEBUG) {
695 Slog.i(TAG, "*** Finishing idle maintenance ***");
696 }
697 mLastIdler = mCurrentIdler;
698 sendEndIdleTm(mCurrentIdler);
699 } else {
700 if (DEBUG) {
701 Slog.w(TAG, "Asked to finish idle maintenance but we're done already");
702 }
703 }
704 }
705
706 // Actual finalization of the idle maintenance sequence
707 void stopIdleMaintenanceTm() {
708 if (mLastIdler != null) {
709 if (DEBUG) {
710 Slog.i(TAG, "*** Idle maintenance shutdown ***");
711 }
712 mWakeLock.setWorkSource(mSystemWorkSource);
713 mLastIdler = mCurrentIdler = null;
714 updateIdleMaintenanceStateTm(false); // resets 'started' and schedules next window
715 mWakeLock.release();
716 } else {
717 Slog.e(TAG, "ERROR: idle shutdown but invariants not held. last=" + mLastIdler
718 + " cur=" + mCurrentIdler + " size=" + mIdleServiceQueue.size());
719 }
720 }
721
Svetoslav6a08a122013-05-03 11:24:26 -0700722 private long getNextIdleMaintenanceIntervalStartFromNow() {
723 return mLastIdleMaintenanceStartTimeMillis + MIN_IDLE_MAINTENANCE_INTERVAL_MILLIS
724 - SystemClock.elapsedRealtime();
725 }
726
Dianne Hackborn35f72be2013-09-16 10:57:39 -0700727 private boolean deviceStatePermitsIdleMaintenanceStart(boolean noisy) {
Svetoslav6a08a122013-05-03 11:24:26 -0700728 final int minBatteryLevel = isBatteryCharging()
Svetoslavf23b64d2013-04-25 14:45:54 -0700729 ? MIN_BATTERY_LEVEL_IDLE_MAINTENANCE_START_CHARGING
730 : MIN_BATTERY_LEVEL_IDLE_MAINTENANCE_START_NOT_CHARGING;
Dianne Hackborn35f72be2013-09-16 10:57:39 -0700731 boolean allowed = (mLastUserActivityElapsedTimeMillis != LAST_USER_ACTIVITY_TIME_INVALID
Svetoslav6a08a122013-05-03 11:24:26 -0700732 && mBatteryService.getBatteryLevel() > minBatteryLevel);
Dianne Hackborn35f72be2013-09-16 10:57:39 -0700733 if (!allowed && noisy) {
734 Slog.i("IdleMaintenance", "Idle maintenance not allowed due to power");
735 }
736 return allowed;
Svetoslavb3038ec2013-02-13 14:39:30 -0800737 }
738
Dianne Hackborn35f72be2013-09-16 10:57:39 -0700739 private boolean lastUserActivityPermitsIdleMaintenanceStart(boolean noisy) {
Svetoslav6a08a122013-05-03 11:24:26 -0700740 // The last time the user poked the device is above the threshold.
Dianne Hackborn35f72be2013-09-16 10:57:39 -0700741 boolean allowed = (mLastUserActivityElapsedTimeMillis != LAST_USER_ACTIVITY_TIME_INVALID
Svetoslav6a08a122013-05-03 11:24:26 -0700742 && SystemClock.elapsedRealtime() - mLastUserActivityElapsedTimeMillis
743 > MIN_USER_INACTIVITY_IDLE_MAINTENANCE_START);
Dianne Hackborn35f72be2013-09-16 10:57:39 -0700744 if (!allowed && noisy) {
745 Slog.i("IdleMaintenance", "Idle maintenance not allowed due to last user activity");
746 }
747 return allowed;
Svetoslavb3038ec2013-02-13 14:39:30 -0800748 }
749
Dianne Hackborn35f72be2013-09-16 10:57:39 -0700750 private boolean lastRunPermitsIdleMaintenanceStart(boolean noisy) {
Svetoslav6a08a122013-05-03 11:24:26 -0700751 // Enough time passed since the last maintenance run.
Dianne Hackborn35f72be2013-09-16 10:57:39 -0700752 boolean allowed = SystemClock.elapsedRealtime() - mLastIdleMaintenanceStartTimeMillis
Svetoslav6a08a122013-05-03 11:24:26 -0700753 > MIN_IDLE_MAINTENANCE_INTERVAL_MILLIS;
Dianne Hackborn35f72be2013-09-16 10:57:39 -0700754 if (!allowed && noisy) {
755 Slog.i("IdleMaintenance", "Idle maintenance not allowed due time since last");
756 }
757 return allowed;
Svetoslav6a08a122013-05-03 11:24:26 -0700758 }
759
760 private boolean lastUserActivityPermitsIdleMaintenanceRunning() {
761 // The user is not using the device.
762 return (mLastUserActivityElapsedTimeMillis != LAST_USER_ACTIVITY_TIME_INVALID);
763 }
764
765 private boolean batteryLevelAndMaintenanceTimeoutPermitsIdleMaintenanceRunning() {
766 // Battery not too low and the maintenance duration did not timeout.
767 return (mBatteryService.getBatteryLevel() > MIN_BATTERY_LEVEL_IDLE_MAINTENANCE_RUNNING
768 && mLastIdleMaintenanceStartTimeMillis + MAX_IDLE_MAINTENANCE_DURATION
769 > SystemClock.elapsedRealtime());
770 }
771
772 private boolean isBatteryCharging() {
773 return mBatteryService.getPlugType() > 0
774 && mBatteryService.getInvalidCharger() == 0;
Svetoslavb3038ec2013-02-13 14:39:30 -0800775 }
776
777 @Override
778 public void onReceive(Context context, Intent intent) {
779 if (DEBUG) {
Christopher Tated417d622013-08-19 16:14:25 -0700780 Log.i(TAG, intent.getAction());
Svetoslavb3038ec2013-02-13 14:39:30 -0800781 }
782 String action = intent.getAction();
783 if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
Svetoslav6a08a122013-05-03 11:24:26 -0700784 // We care about battery only if maintenance is in progress so we can
785 // stop it if battery is too low. Note that here we assume that the
786 // maintenance clients are properly holding a wake lock. We will
787 // refactor the maintenance to use services instead of intents for the
788 // next release. The only client for this for now is internal an holds
789 // a wake lock correctly.
790 if (mIdleMaintenanceStarted) {
Christopher Tated417d622013-08-19 16:14:25 -0700791 updateIdleMaintenanceStateTm(false);
Svetoslav6a08a122013-05-03 11:24:26 -0700792 }
Svetoslavb3038ec2013-02-13 14:39:30 -0800793 } else if (Intent.ACTION_SCREEN_ON.equals(action)
794 || Intent.ACTION_DREAMING_STOPPED.equals(action)) {
795 mLastUserActivityElapsedTimeMillis = LAST_USER_ACTIVITY_TIME_INVALID;
Svetoslav6a08a122013-05-03 11:24:26 -0700796 // Unschedule any future updates since we already know that maintenance
797 // cannot be performed since the user is back.
798 unscheduleUpdateIdleMaintenanceState();
799 // If the screen went on/stopped dreaming, we know the user is using the
800 // device which means that idle maintenance should be stopped if running.
Christopher Tated417d622013-08-19 16:14:25 -0700801 updateIdleMaintenanceStateTm(false);
Svetoslavb3038ec2013-02-13 14:39:30 -0800802 } else if (Intent.ACTION_SCREEN_OFF.equals(action)
803 || Intent.ACTION_DREAMING_STARTED.equals(action)) {
804 mLastUserActivityElapsedTimeMillis = SystemClock.elapsedRealtime();
Svetoslav6a08a122013-05-03 11:24:26 -0700805 // If screen went off/started dreaming, we may be able to start idle maintenance
806 // after the minimal user inactivity elapses. We schedule an alarm for when
807 // this timeout elapses since the device may go to sleep by then.
808 scheduleUpdateIdleMaintenanceState(MIN_USER_INACTIVITY_IDLE_MAINTENANCE_START);
809 } else if (ACTION_UPDATE_IDLE_MAINTENANCE_STATE.equals(action)) {
Christopher Tated417d622013-08-19 16:14:25 -0700810 updateIdleMaintenanceStateTm(false);
Dianne Hackborn35f72be2013-09-16 10:57:39 -0700811 } else if (ACTION_FORCE_IDLE_MAINTENANCE.equals(action)) {
812 long now = SystemClock.elapsedRealtime() - 1;
813 mLastUserActivityElapsedTimeMillis = now - MIN_USER_INACTIVITY_IDLE_MAINTENANCE_START;
814 mLastIdleMaintenanceStartTimeMillis = now - MIN_IDLE_MAINTENANCE_INTERVAL_MILLIS;
Christopher Tated417d622013-08-19 16:14:25 -0700815 updateIdleMaintenanceStateTm(true);
Svetoslavb3038ec2013-02-13 14:39:30 -0800816 }
Svetoslavb3038ec2013-02-13 14:39:30 -0800817 }
818}