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