blob: 44cc0bbd3916aa9192b3a060dbbfd27062840f8b [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2006 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.ActivityManagerNative;
20import android.app.AlarmManager;
21import android.app.IAlarmManager;
22import android.app.PendingIntent;
23import android.content.BroadcastReceiver;
24import android.content.Context;
25import android.content.Intent;
26import android.content.IntentFilter;
27import android.content.pm.PackageManager;
28import android.net.Uri;
29import android.os.Binder;
30import android.os.Bundle;
31import android.os.Handler;
32import android.os.Message;
33import android.os.PowerManager;
34import android.os.SystemClock;
35import android.os.SystemProperties;
36import android.text.TextUtils;
37import android.text.format.Time;
38import android.util.EventLog;
39import android.util.Log;
40
41import java.io.FileDescriptor;
42import java.io.PrintWriter;
43import java.util.ArrayList;
44import java.util.Calendar;
45import java.util.Collections;
46import java.util.Comparator;
Mike Lockwood1f7b4132009-11-20 15:12:51 -050047import java.util.Date;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080048import java.util.HashMap;
49import java.util.Iterator;
50import java.util.Map;
51import java.util.TimeZone;
52
53class AlarmManagerService extends IAlarmManager.Stub {
54 // The threshold for how long an alarm can be late before we print a
55 // warning message. The time duration is in milliseconds.
56 private static final long LATE_ALARM_THRESHOLD = 10 * 1000;
57
58 private static final int RTC_WAKEUP_MASK = 1 << AlarmManager.RTC_WAKEUP;
59 private static final int RTC_MASK = 1 << AlarmManager.RTC;
60 private static final int ELAPSED_REALTIME_WAKEUP_MASK = 1 << AlarmManager.ELAPSED_REALTIME_WAKEUP;
61 private static final int ELAPSED_REALTIME_MASK = 1 << AlarmManager.ELAPSED_REALTIME;
62 private static final int TIME_CHANGED_MASK = 1 << 16;
63
64 private static final String TAG = "AlarmManager";
65 private static final String ClockReceiver_TAG = "ClockReceiver";
66 private static final boolean localLOGV = false;
67 private static final int ALARM_EVENT = 1;
68 private static final String TIMEZONE_PROPERTY = "persist.sys.timezone";
69
70 private static final Intent mBackgroundIntent
71 = new Intent().addFlags(Intent.FLAG_FROM_BACKGROUND);
72
73 private final Context mContext;
74
75 private Object mLock = new Object();
76
77 private final ArrayList<Alarm> mRtcWakeupAlarms = new ArrayList<Alarm>();
78 private final ArrayList<Alarm> mRtcAlarms = new ArrayList<Alarm>();
79 private final ArrayList<Alarm> mElapsedRealtimeWakeupAlarms = new ArrayList<Alarm>();
80 private final ArrayList<Alarm> mElapsedRealtimeAlarms = new ArrayList<Alarm>();
81 private final IncreasingTimeOrder mIncreasingTimeOrder = new IncreasingTimeOrder();
82
83 // slots corresponding with the inexact-repeat interval buckets,
84 // ordered from shortest to longest
85 private static final long sInexactSlotIntervals[] = {
86 AlarmManager.INTERVAL_FIFTEEN_MINUTES,
87 AlarmManager.INTERVAL_HALF_HOUR,
88 AlarmManager.INTERVAL_HOUR,
89 AlarmManager.INTERVAL_HALF_DAY,
90 AlarmManager.INTERVAL_DAY
91 };
92 private long mInexactDeliveryTimes[] = { 0, 0, 0, 0, 0};
93
94 private int mDescriptor;
95 private int mBroadcastRefCount = 0;
96 private PowerManager.WakeLock mWakeLock;
97 private final AlarmThread mWaitThread = new AlarmThread();
98 private final AlarmHandler mHandler = new AlarmHandler();
99 private ClockReceiver mClockReceiver;
100 private UninstallReceiver mUninstallReceiver;
101 private final ResultReceiver mResultReceiver = new ResultReceiver();
102 private final PendingIntent mTimeTickSender;
103 private final PendingIntent mDateChangeSender;
104
105 private static final class FilterStats {
106 int count;
107 }
108
109 private static final class BroadcastStats {
110 long aggregateTime;
111 int numWakeup;
112 long startTime;
113 int nesting;
114 HashMap<Intent.FilterComparison, FilterStats> filterStats
115 = new HashMap<Intent.FilterComparison, FilterStats>();
116 }
117
118 private final HashMap<String, BroadcastStats> mBroadcastStats
119 = new HashMap<String, BroadcastStats>();
120
121 public AlarmManagerService(Context context) {
122 mContext = context;
123 mDescriptor = init();
124 PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
125 mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
126
127 mTimeTickSender = PendingIntent.getBroadcast(context, 0,
128 new Intent(Intent.ACTION_TIME_TICK).addFlags(
129 Intent.FLAG_RECEIVER_REGISTERED_ONLY), 0);
Dianne Hackborn1c633fc2009-12-08 19:45:14 -0800130 Intent intent = new Intent(Intent.ACTION_DATE_CHANGED);
131 intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
132 mDateChangeSender = PendingIntent.getBroadcast(context, 0, intent, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800133
134 // now that we have initied the driver schedule the alarm
135 mClockReceiver= new ClockReceiver();
136 mClockReceiver.scheduleTimeTickEvent();
137 mClockReceiver.scheduleDateChangedEvent();
138 mUninstallReceiver = new UninstallReceiver();
139
140 if (mDescriptor != -1) {
141 mWaitThread.start();
142 } else {
143 Log.w(TAG, "Failed to open alarm driver. Falling back to a handler.");
144 }
145 }
146
147 protected void finalize() throws Throwable {
148 try {
149 close(mDescriptor);
150 } finally {
151 super.finalize();
152 }
153 }
154
155 public void set(int type, long triggerAtTime, PendingIntent operation) {
156 setRepeating(type, triggerAtTime, 0, operation);
157 }
158
159 public void setRepeating(int type, long triggerAtTime, long interval,
160 PendingIntent operation) {
161 if (operation == null) {
162 Log.w(TAG, "set/setRepeating ignored because there is no intent");
163 return;
164 }
165 synchronized (mLock) {
166 Alarm alarm = new Alarm();
167 alarm.type = type;
168 alarm.when = triggerAtTime;
169 alarm.repeatInterval = interval;
170 alarm.operation = operation;
171
172 // Remove this alarm if already scheduled.
173 removeLocked(operation);
174
175 if (localLOGV) Log.v(TAG, "set: " + alarm);
176
177 int index = addAlarmLocked(alarm);
178 if (index == 0) {
179 setLocked(alarm);
180 }
181 }
182 }
183
184 public void setInexactRepeating(int type, long triggerAtTime, long interval,
185 PendingIntent operation) {
186 if (operation == null) {
187 Log.w(TAG, "setInexactRepeating ignored because there is no intent");
188 return;
189 }
190
191 // find the slot in the delivery-times array that we will use
192 int intervalSlot;
193 for (intervalSlot = 0; intervalSlot < sInexactSlotIntervals.length; intervalSlot++) {
194 if (sInexactSlotIntervals[intervalSlot] == interval) {
195 break;
196 }
197 }
198
199 // Non-bucket intervals just fall back to the less-efficient
200 // unbucketed recurring alarm implementation
201 if (intervalSlot >= sInexactSlotIntervals.length) {
202 setRepeating(type, triggerAtTime, interval, operation);
203 return;
204 }
205
206 // Align bucketed alarm deliveries by trying to match
207 // the shortest-interval bucket already scheduled
208 long bucketTime = 0;
209 for (int slot = 0; slot < mInexactDeliveryTimes.length; slot++) {
210 if (mInexactDeliveryTimes[slot] > 0) {
211 bucketTime = mInexactDeliveryTimes[slot];
212 break;
213 }
214 }
215
216 if (bucketTime == 0) {
217 // If nothing is scheduled yet, just start at the requested time
218 bucketTime = triggerAtTime;
219 } else {
220 // Align the new alarm with the existing bucketed sequence. To achieve
221 // alignment, we slide the start time around by min{interval, slot interval}
222 long adjustment = (interval <= sInexactSlotIntervals[intervalSlot])
223 ? interval : sInexactSlotIntervals[intervalSlot];
224
225 // The bucket may have started in the past; adjust
226 while (bucketTime < triggerAtTime) {
227 bucketTime += adjustment;
228 }
229
230 // Or the bucket may be set to start more than an interval beyond
231 // our requested trigger time; pull it back to meet our needs
232 while (bucketTime > triggerAtTime + adjustment) {
233 bucketTime -= adjustment;
234 }
235 }
236
237 // Remember where this bucket started (reducing the amount of later
238 // fixup required) and set the alarm with the new, bucketed start time.
239 if (localLOGV) Log.v(TAG, "setInexactRepeating: interval=" + interval
240 + " bucketTime=" + bucketTime);
241 mInexactDeliveryTimes[intervalSlot] = bucketTime;
242 setRepeating(type, bucketTime, interval, operation);
243 }
244
Dan Egnor97e44942010-02-04 20:27:47 -0800245 public void setTime(long millis) {
246 mContext.enforceCallingOrSelfPermission(
247 "android.permission.SET_TIME",
248 "setTime");
249
250 SystemClock.setCurrentTimeMillis(millis);
251 }
252
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800253 public void setTimeZone(String tz) {
254 mContext.enforceCallingOrSelfPermission(
255 "android.permission.SET_TIME_ZONE",
256 "setTimeZone");
257
258 if (TextUtils.isEmpty(tz)) return;
259 TimeZone zone = TimeZone.getTimeZone(tz);
260 // Prevent reentrant calls from stepping on each other when writing
261 // the time zone property
262 boolean timeZoneWasChanged = false;
263 synchronized (this) {
264 String current = SystemProperties.get(TIMEZONE_PROPERTY);
265 if (current == null || !current.equals(zone.getID())) {
266 if (localLOGV) Log.v(TAG, "timezone changed: " + current + ", new=" + zone.getID());
267 timeZoneWasChanged = true;
268 SystemProperties.set(TIMEZONE_PROPERTY, zone.getID());
269 }
270
271 // Update the kernel timezone information
272 // Kernel tracks time offsets as 'minutes west of GMT'
Mike Lockwood1f7b4132009-11-20 15:12:51 -0500273 int gmtOffset = zone.getRawOffset();
274 if (zone.inDaylightTime(new Date(System.currentTimeMillis()))) {
275 gmtOffset += zone.getDSTSavings();
276 }
277 setKernelTimezone(mDescriptor, -(gmtOffset / 60000));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800278 }
279
280 TimeZone.setDefault(null);
281
282 if (timeZoneWasChanged) {
283 Intent intent = new Intent(Intent.ACTION_TIMEZONE_CHANGED);
Dianne Hackborn1c633fc2009-12-08 19:45:14 -0800284 intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800285 intent.putExtra("time-zone", zone.getID());
286 mContext.sendBroadcast(intent);
287 }
288 }
289
290 public void remove(PendingIntent operation) {
291 if (operation == null) {
292 return;
293 }
294 synchronized (mLock) {
295 removeLocked(operation);
296 }
297 }
298
299 public void removeLocked(PendingIntent operation) {
300 removeLocked(mRtcWakeupAlarms, operation);
301 removeLocked(mRtcAlarms, operation);
302 removeLocked(mElapsedRealtimeWakeupAlarms, operation);
303 removeLocked(mElapsedRealtimeAlarms, operation);
304 }
305
306 private void removeLocked(ArrayList<Alarm> alarmList,
307 PendingIntent operation) {
308 if (alarmList.size() <= 0) {
309 return;
310 }
311
312 // iterator over the list removing any it where the intent match
313 Iterator<Alarm> it = alarmList.iterator();
314
315 while (it.hasNext()) {
316 Alarm alarm = it.next();
317 if (alarm.operation.equals(operation)) {
318 it.remove();
319 }
320 }
321 }
322
323 public void removeLocked(String packageName) {
324 removeLocked(mRtcWakeupAlarms, packageName);
325 removeLocked(mRtcAlarms, packageName);
326 removeLocked(mElapsedRealtimeWakeupAlarms, packageName);
327 removeLocked(mElapsedRealtimeAlarms, packageName);
328 }
329
330 private void removeLocked(ArrayList<Alarm> alarmList,
331 String packageName) {
332 if (alarmList.size() <= 0) {
333 return;
334 }
335
336 // iterator over the list removing any it where the intent match
337 Iterator<Alarm> it = alarmList.iterator();
338
339 while (it.hasNext()) {
340 Alarm alarm = it.next();
341 if (alarm.operation.getTargetPackage().equals(packageName)) {
342 it.remove();
343 }
344 }
345 }
346
347 private ArrayList<Alarm> getAlarmList(int type) {
348 switch (type) {
349 case AlarmManager.RTC_WAKEUP: return mRtcWakeupAlarms;
350 case AlarmManager.RTC: return mRtcAlarms;
351 case AlarmManager.ELAPSED_REALTIME_WAKEUP: return mElapsedRealtimeWakeupAlarms;
352 case AlarmManager.ELAPSED_REALTIME: return mElapsedRealtimeAlarms;
353 }
354
355 return null;
356 }
357
358 private int addAlarmLocked(Alarm alarm) {
359 ArrayList<Alarm> alarmList = getAlarmList(alarm.type);
360
361 int index = Collections.binarySearch(alarmList, alarm, mIncreasingTimeOrder);
362 if (index < 0) {
363 index = 0 - index - 1;
364 }
365 if (localLOGV) Log.v(TAG, "Adding alarm " + alarm + " at " + index);
366 alarmList.add(index, alarm);
367
368 if (localLOGV) {
369 // Display the list of alarms for this alarm type
370 Log.v(TAG, "alarms: " + alarmList.size() + " type: " + alarm.type);
371 int position = 0;
372 for (Alarm a : alarmList) {
373 Time time = new Time();
374 time.set(a.when);
375 String timeStr = time.format("%b %d %I:%M:%S %p");
376 Log.v(TAG, position + ": " + timeStr
377 + " " + a.operation.getTargetPackage());
378 position += 1;
379 }
380 }
381
382 return index;
383 }
384
385 public long timeToNextAlarm() {
386 long nextAlarm = 0xfffffffffffffffl;
387 synchronized (mLock) {
388 for (int i=AlarmManager.RTC_WAKEUP;
389 i<=AlarmManager.ELAPSED_REALTIME; i++) {
390 ArrayList<Alarm> alarmList = getAlarmList(i);
391 if (alarmList.size() > 0) {
392 Alarm a = alarmList.get(0);
393 if (a.when < nextAlarm) {
394 nextAlarm = a.when;
395 }
396 }
397 }
398 }
399 return nextAlarm;
400 }
401
402 private void setLocked(Alarm alarm)
403 {
404 if (mDescriptor != -1)
405 {
406 set(mDescriptor, alarm.type, (alarm.when * 1000 * 1000));
407 }
408 else
409 {
410 Message msg = Message.obtain();
411 msg.what = ALARM_EVENT;
412
413 mHandler.removeMessages(ALARM_EVENT);
414 mHandler.sendMessageAtTime(msg, alarm.when);
415 }
416 }
417
418 @Override
419 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
420 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
421 != PackageManager.PERMISSION_GRANTED) {
422 pw.println("Permission Denial: can't dump AlarmManager from from pid="
423 + Binder.getCallingPid()
424 + ", uid=" + Binder.getCallingUid());
425 return;
426 }
427
428 synchronized (mLock) {
429 pw.println("Current Alarm Manager state:");
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700430 if (mRtcWakeupAlarms.size() > 0 || mRtcAlarms.size() > 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800431 pw.println(" ");
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700432 pw.print(" Realtime wakeup (now=");
433 pw.print(System.currentTimeMillis()); pw.println("):");
434 if (mRtcWakeupAlarms.size() > 0) {
435 dumpAlarmList(pw, mRtcWakeupAlarms, " ", "RTC_WAKEUP");
436 }
437 if (mRtcAlarms.size() > 0) {
438 dumpAlarmList(pw, mRtcAlarms, " ", "RTC");
439 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800440 }
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700441 if (mElapsedRealtimeWakeupAlarms.size() > 0 || mElapsedRealtimeAlarms.size() > 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800442 pw.println(" ");
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700443 pw.print(" Elapsed realtime wakeup (now=");
444 pw.print(SystemClock.elapsedRealtime()); pw.println("):");
445 if (mElapsedRealtimeWakeupAlarms.size() > 0) {
446 dumpAlarmList(pw, mElapsedRealtimeWakeupAlarms, " ", "ELAPSED_WAKEUP");
447 }
448 if (mElapsedRealtimeAlarms.size() > 0) {
449 dumpAlarmList(pw, mElapsedRealtimeAlarms, " ", "ELAPSED");
450 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800451 }
452
453 pw.println(" ");
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700454 pw.print(" Broadcast ref count: "); pw.println(mBroadcastRefCount);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800455
456 pw.println(" ");
457 pw.println(" Alarm Stats:");
458 for (Map.Entry<String, BroadcastStats> be : mBroadcastStats.entrySet()) {
459 BroadcastStats bs = be.getValue();
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700460 pw.print(" "); pw.println(be.getKey());
461 pw.print(" "); pw.print(bs.aggregateTime);
462 pw.print("ms running, "); pw.print(bs.numWakeup);
463 pw.println(" wakeups");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800464 for (Map.Entry<Intent.FilterComparison, FilterStats> fe
465 : bs.filterStats.entrySet()) {
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700466 pw.print(" "); pw.print(fe.getValue().count);
467 pw.print(" alarms: ");
468 pw.println(fe.getKey().getIntent().toShortString(true, false));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800469 }
470 }
471 }
472 }
473
474 private static final void dumpAlarmList(PrintWriter pw, ArrayList<Alarm> list, String prefix, String label) {
475 for (int i=list.size()-1; i>=0; i--) {
476 Alarm a = list.get(i);
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700477 pw.print(prefix); pw.print(label); pw.print(" #"); pw.print(i);
478 pw.print(": "); pw.println(a);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800479 a.dump(pw, prefix + " ");
480 }
481 }
482
483 private native int init();
484 private native void close(int fd);
485 private native void set(int fd, int type, long nanoseconds);
486 private native int waitForAlarm(int fd);
487 private native int setKernelTimezone(int fd, int minuteswest);
488
489 private void triggerAlarmsLocked(ArrayList<Alarm> alarmList,
490 ArrayList<Alarm> triggerList,
491 long now)
492 {
493 Iterator<Alarm> it = alarmList.iterator();
494 ArrayList<Alarm> repeats = new ArrayList<Alarm>();
495
496 while (it.hasNext())
497 {
498 Alarm alarm = it.next();
499
500 if (localLOGV) Log.v(TAG, "Checking active alarm when=" + alarm.when + " " + alarm);
501
502 if (alarm.when > now) {
503 // don't fire alarms in the future
504 break;
505 }
506
507 // If the alarm is late, then print a warning message.
508 // Note that this can happen if the user creates a new event on
509 // the Calendar app with a reminder that is in the past. In that
510 // case, the reminder alarm will fire immediately.
511 if (localLOGV && now - alarm.when > LATE_ALARM_THRESHOLD) {
512 Log.v(TAG, "alarm is late! alarm time: " + alarm.when
513 + " now: " + now + " delay (in seconds): "
514 + (now - alarm.when) / 1000);
515 }
516
517 // Recurring alarms may have passed several alarm intervals while the
518 // phone was asleep or off, so pass a trigger count when sending them.
519 if (localLOGV) Log.v(TAG, "Alarm triggering: " + alarm);
520 alarm.count = 1;
521 if (alarm.repeatInterval > 0) {
522 // this adjustment will be zero if we're late by
523 // less than one full repeat interval
524 alarm.count += (now - alarm.when) / alarm.repeatInterval;
525 }
526 triggerList.add(alarm);
527
528 // remove the alarm from the list
529 it.remove();
530
531 // if it repeats queue it up to be read-added to the list
532 if (alarm.repeatInterval > 0) {
533 repeats.add(alarm);
534 }
535 }
536
537 // reset any repeating alarms.
538 it = repeats.iterator();
539 while (it.hasNext()) {
540 Alarm alarm = it.next();
541 alarm.when += alarm.count * alarm.repeatInterval;
542 addAlarmLocked(alarm);
543 }
544
545 if (alarmList.size() > 0) {
546 setLocked(alarmList.get(0));
547 }
548 }
549
550 /**
551 * This Comparator sorts Alarms into increasing time order.
552 */
553 public static class IncreasingTimeOrder implements Comparator<Alarm> {
554 public int compare(Alarm a1, Alarm a2) {
555 long when1 = a1.when;
556 long when2 = a2.when;
557 if (when1 - when2 > 0) {
558 return 1;
559 }
560 if (when1 - when2 < 0) {
561 return -1;
562 }
563 return 0;
564 }
565 }
566
567 private static class Alarm {
568 public int type;
569 public int count;
570 public long when;
571 public long repeatInterval;
572 public PendingIntent operation;
573
574 public Alarm() {
575 when = 0;
576 repeatInterval = 0;
577 operation = null;
578 }
579
580 @Override
581 public String toString()
582 {
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700583 StringBuilder sb = new StringBuilder(128);
584 sb.append("Alarm{");
585 sb.append(Integer.toHexString(System.identityHashCode(this)));
586 sb.append(" type ");
587 sb.append(type);
588 sb.append(" ");
589 sb.append(operation.getTargetPackage());
590 sb.append('}');
591 return sb.toString();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800592 }
593
594 public void dump(PrintWriter pw, String prefix)
595 {
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700596 pw.print(prefix); pw.print("type="); pw.print(type);
597 pw.print(" when="); pw.print(when);
598 pw.print(" repeatInterval="); pw.print(repeatInterval);
599 pw.print(" count="); pw.println(count);
600 pw.print(prefix); pw.print("operation="); pw.println(operation);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800601 }
602 }
603
604 private class AlarmThread extends Thread
605 {
606 public AlarmThread()
607 {
608 super("AlarmManager");
609 }
610
611 public void run()
612 {
613 while (true)
614 {
615 int result = waitForAlarm(mDescriptor);
616
617 ArrayList<Alarm> triggerList = new ArrayList<Alarm>();
618
619 if ((result & TIME_CHANGED_MASK) != 0) {
620 remove(mTimeTickSender);
621 mClockReceiver.scheduleTimeTickEvent();
Dianne Hackborn1c633fc2009-12-08 19:45:14 -0800622 Intent intent = new Intent(Intent.ACTION_TIME_CHANGED);
623 intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
624 mContext.sendBroadcast(intent);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800625 }
626
627 synchronized (mLock) {
628 final long nowRTC = System.currentTimeMillis();
629 final long nowELAPSED = SystemClock.elapsedRealtime();
630 if (localLOGV) Log.v(
631 TAG, "Checking for alarms... rtc=" + nowRTC
632 + ", elapsed=" + nowELAPSED);
633
634 if ((result & RTC_WAKEUP_MASK) != 0)
635 triggerAlarmsLocked(mRtcWakeupAlarms, triggerList, nowRTC);
636
637 if ((result & RTC_MASK) != 0)
638 triggerAlarmsLocked(mRtcAlarms, triggerList, nowRTC);
639
640 if ((result & ELAPSED_REALTIME_WAKEUP_MASK) != 0)
641 triggerAlarmsLocked(mElapsedRealtimeWakeupAlarms, triggerList, nowELAPSED);
642
643 if ((result & ELAPSED_REALTIME_MASK) != 0)
644 triggerAlarmsLocked(mElapsedRealtimeAlarms, triggerList, nowELAPSED);
645
646 // now trigger the alarms
647 Iterator<Alarm> it = triggerList.iterator();
648 while (it.hasNext()) {
649 Alarm alarm = it.next();
650 try {
651 if (localLOGV) Log.v(TAG, "sending alarm " + alarm);
652 alarm.operation.send(mContext, 0,
653 mBackgroundIntent.putExtra(
654 Intent.EXTRA_ALARM_COUNT, alarm.count),
655 mResultReceiver, mHandler);
656
657 // we have an active broadcast so stay awake.
658 if (mBroadcastRefCount == 0) {
659 mWakeLock.acquire();
660 }
661 mBroadcastRefCount++;
662
663 BroadcastStats bs = getStatsLocked(alarm.operation);
664 if (bs.nesting == 0) {
665 bs.startTime = nowELAPSED;
666 } else {
667 bs.nesting++;
668 }
669 if (alarm.type == AlarmManager.ELAPSED_REALTIME_WAKEUP
670 || alarm.type == AlarmManager.RTC_WAKEUP) {
671 bs.numWakeup++;
672 ActivityManagerNative.noteWakeupAlarm(
673 alarm.operation);
674 }
675 } catch (PendingIntent.CanceledException e) {
676 if (alarm.repeatInterval > 0) {
677 // This IntentSender is no longer valid, but this
678 // is a repeating alarm, so toss the hoser.
679 remove(alarm.operation);
680 }
681 } catch (RuntimeException e) {
682 Log.w(TAG, "Failure sending alarm.", e);
683 }
684 }
685 }
686 }
687 }
688 }
689
690 private class AlarmHandler extends Handler {
691 public static final int ALARM_EVENT = 1;
692 public static final int MINUTE_CHANGE_EVENT = 2;
693 public static final int DATE_CHANGE_EVENT = 3;
694
695 public AlarmHandler() {
696 }
697
698 public void handleMessage(Message msg) {
699 if (msg.what == ALARM_EVENT) {
700 ArrayList<Alarm> triggerList = new ArrayList<Alarm>();
701 synchronized (mLock) {
702 final long nowRTC = System.currentTimeMillis();
703 triggerAlarmsLocked(mRtcWakeupAlarms, triggerList, nowRTC);
704 triggerAlarmsLocked(mRtcAlarms, triggerList, nowRTC);
705 triggerAlarmsLocked(mElapsedRealtimeWakeupAlarms, triggerList, nowRTC);
706 triggerAlarmsLocked(mElapsedRealtimeAlarms, triggerList, nowRTC);
707 }
708
709 // now trigger the alarms without the lock held
710 Iterator<Alarm> it = triggerList.iterator();
711 while (it.hasNext())
712 {
713 Alarm alarm = it.next();
714 try {
715 alarm.operation.send();
716 } catch (PendingIntent.CanceledException e) {
717 if (alarm.repeatInterval > 0) {
718 // This IntentSender is no longer valid, but this
719 // is a repeating alarm, so toss the hoser.
720 remove(alarm.operation);
721 }
722 }
723 }
724 }
725 }
726 }
727
728 class ClockReceiver extends BroadcastReceiver {
729 public ClockReceiver() {
730 IntentFilter filter = new IntentFilter();
731 filter.addAction(Intent.ACTION_TIME_TICK);
732 filter.addAction(Intent.ACTION_DATE_CHANGED);
733 mContext.registerReceiver(this, filter);
734 }
735
736 @Override
737 public void onReceive(Context context, Intent intent) {
738 if (intent.getAction().equals(Intent.ACTION_TIME_TICK)) {
739 scheduleTimeTickEvent();
740 } else if (intent.getAction().equals(Intent.ACTION_DATE_CHANGED)) {
741 // Since the kernel does not keep track of DST, we need to
742 // reset the TZ information at the beginning of each day
743 // based off of the current Zone gmt offset + userspace tracked
744 // daylight savings information.
745 TimeZone zone = TimeZone.getTimeZone(SystemProperties.get(TIMEZONE_PROPERTY));
746 int gmtOffset = (zone.getRawOffset() + zone.getDSTSavings()) / 60000;
747
748 setKernelTimezone(mDescriptor, -(gmtOffset));
749 scheduleDateChangedEvent();
750 }
751 }
752
753 public void scheduleTimeTickEvent() {
754 Calendar calendar = Calendar.getInstance();
755 calendar.setTimeInMillis(System.currentTimeMillis());
756 calendar.add(Calendar.MINUTE, 1);
757 calendar.set(Calendar.SECOND, 0);
758 calendar.set(Calendar.MILLISECOND, 0);
759
760 set(AlarmManager.RTC, calendar.getTimeInMillis(), mTimeTickSender);
761 }
762
763 public void scheduleDateChangedEvent() {
764 Calendar calendar = Calendar.getInstance();
765 calendar.setTimeInMillis(System.currentTimeMillis());
766 calendar.set(Calendar.HOUR, 0);
767 calendar.set(Calendar.MINUTE, 0);
768 calendar.set(Calendar.SECOND, 0);
769 calendar.set(Calendar.MILLISECOND, 0);
770 calendar.add(Calendar.DAY_OF_MONTH, 1);
771
772 set(AlarmManager.RTC, calendar.getTimeInMillis(), mDateChangeSender);
773 }
774 }
775
776 class UninstallReceiver extends BroadcastReceiver {
777 public UninstallReceiver() {
778 IntentFilter filter = new IntentFilter();
779 filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
780 filter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
781 filter.addDataScheme("package");
782 mContext.registerReceiver(this, filter);
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800783 // Register for events related to sdcard installation.
784 IntentFilter sdFilter = new IntentFilter();
Suchi Amalapurapub56ae202010-02-04 22:51:07 -0800785 sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800786 mContext.registerReceiver(this, sdFilter);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800787 }
788
789 @Override
790 public void onReceive(Context context, Intent intent) {
791 synchronized (mLock) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800792 String action = intent.getAction();
793 String pkgList[] = null;
Suchi Amalapurapub56ae202010-02-04 22:51:07 -0800794 if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800795 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
796 } else {
797 Uri data = intent.getData();
798 if (data != null) {
799 String pkg = data.getSchemeSpecificPart();
800 if (pkg != null) {
801 pkgList = new String[]{pkg};
802 }
803 }
804 }
805 if (pkgList != null && (pkgList.length > 0)) {
806 for (String pkg : pkgList) {
807 removeLocked(pkg);
808 mBroadcastStats.remove(pkg);
809 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800810 }
811 }
812 }
813 }
814
815 private final BroadcastStats getStatsLocked(PendingIntent pi) {
816 String pkg = pi.getTargetPackage();
817 BroadcastStats bs = mBroadcastStats.get(pkg);
818 if (bs == null) {
819 bs = new BroadcastStats();
820 mBroadcastStats.put(pkg, bs);
821 }
822 return bs;
823 }
824
825 class ResultReceiver implements PendingIntent.OnFinished {
826 public void onSendFinished(PendingIntent pi, Intent intent, int resultCode,
827 String resultData, Bundle resultExtras) {
828 synchronized (mLock) {
829 BroadcastStats bs = getStatsLocked(pi);
830 if (bs != null) {
831 bs.nesting--;
832 if (bs.nesting <= 0) {
833 bs.nesting = 0;
834 bs.aggregateTime += SystemClock.elapsedRealtime()
835 - bs.startTime;
836 Intent.FilterComparison fc = new Intent.FilterComparison(intent);
837 FilterStats fs = bs.filterStats.get(fc);
838 if (fs == null) {
839 fs = new FilterStats();
840 bs.filterStats.put(fc, fs);
841 }
842 fs.count++;
843 }
844 }
845 mBroadcastRefCount--;
846 if (mBroadcastRefCount == 0) {
847 mWakeLock.release();
848 }
849 }
850 }
851 }
852}