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