blob: 9c4ace1e8e4f19c85aa7e14a7acf94652075ded7 [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:");
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700416 if (mRtcWakeupAlarms.size() > 0 || mRtcAlarms.size() > 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800417 pw.println(" ");
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700418 pw.print(" Realtime wakeup (now=");
419 pw.print(System.currentTimeMillis()); pw.println("):");
420 if (mRtcWakeupAlarms.size() > 0) {
421 dumpAlarmList(pw, mRtcWakeupAlarms, " ", "RTC_WAKEUP");
422 }
423 if (mRtcAlarms.size() > 0) {
424 dumpAlarmList(pw, mRtcAlarms, " ", "RTC");
425 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800426 }
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700427 if (mElapsedRealtimeWakeupAlarms.size() > 0 || mElapsedRealtimeAlarms.size() > 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800428 pw.println(" ");
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700429 pw.print(" Elapsed realtime wakeup (now=");
430 pw.print(SystemClock.elapsedRealtime()); pw.println("):");
431 if (mElapsedRealtimeWakeupAlarms.size() > 0) {
432 dumpAlarmList(pw, mElapsedRealtimeWakeupAlarms, " ", "ELAPSED_WAKEUP");
433 }
434 if (mElapsedRealtimeAlarms.size() > 0) {
435 dumpAlarmList(pw, mElapsedRealtimeAlarms, " ", "ELAPSED");
436 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800437 }
438
439 pw.println(" ");
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700440 pw.print(" Broadcast ref count: "); pw.println(mBroadcastRefCount);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800441
442 pw.println(" ");
443 pw.println(" Alarm Stats:");
444 for (Map.Entry<String, BroadcastStats> be : mBroadcastStats.entrySet()) {
445 BroadcastStats bs = be.getValue();
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700446 pw.print(" "); pw.println(be.getKey());
447 pw.print(" "); pw.print(bs.aggregateTime);
448 pw.print("ms running, "); pw.print(bs.numWakeup);
449 pw.println(" wakeups");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800450 for (Map.Entry<Intent.FilterComparison, FilterStats> fe
451 : bs.filterStats.entrySet()) {
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700452 pw.print(" "); pw.print(fe.getValue().count);
453 pw.print(" alarms: ");
454 pw.println(fe.getKey().getIntent().toShortString(true, false));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800455 }
456 }
457 }
458 }
459
460 private static final void dumpAlarmList(PrintWriter pw, ArrayList<Alarm> list, String prefix, String label) {
461 for (int i=list.size()-1; i>=0; i--) {
462 Alarm a = list.get(i);
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700463 pw.print(prefix); pw.print(label); pw.print(" #"); pw.print(i);
464 pw.print(": "); pw.println(a);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800465 a.dump(pw, prefix + " ");
466 }
467 }
468
469 private native int init();
470 private native void close(int fd);
471 private native void set(int fd, int type, long nanoseconds);
472 private native int waitForAlarm(int fd);
473 private native int setKernelTimezone(int fd, int minuteswest);
474
475 private void triggerAlarmsLocked(ArrayList<Alarm> alarmList,
476 ArrayList<Alarm> triggerList,
477 long now)
478 {
479 Iterator<Alarm> it = alarmList.iterator();
480 ArrayList<Alarm> repeats = new ArrayList<Alarm>();
481
482 while (it.hasNext())
483 {
484 Alarm alarm = it.next();
485
486 if (localLOGV) Log.v(TAG, "Checking active alarm when=" + alarm.when + " " + alarm);
487
488 if (alarm.when > now) {
489 // don't fire alarms in the future
490 break;
491 }
492
493 // If the alarm is late, then print a warning message.
494 // Note that this can happen if the user creates a new event on
495 // the Calendar app with a reminder that is in the past. In that
496 // case, the reminder alarm will fire immediately.
497 if (localLOGV && now - alarm.when > LATE_ALARM_THRESHOLD) {
498 Log.v(TAG, "alarm is late! alarm time: " + alarm.when
499 + " now: " + now + " delay (in seconds): "
500 + (now - alarm.when) / 1000);
501 }
502
503 // Recurring alarms may have passed several alarm intervals while the
504 // phone was asleep or off, so pass a trigger count when sending them.
505 if (localLOGV) Log.v(TAG, "Alarm triggering: " + alarm);
506 alarm.count = 1;
507 if (alarm.repeatInterval > 0) {
508 // this adjustment will be zero if we're late by
509 // less than one full repeat interval
510 alarm.count += (now - alarm.when) / alarm.repeatInterval;
511 }
512 triggerList.add(alarm);
513
514 // remove the alarm from the list
515 it.remove();
516
517 // if it repeats queue it up to be read-added to the list
518 if (alarm.repeatInterval > 0) {
519 repeats.add(alarm);
520 }
521 }
522
523 // reset any repeating alarms.
524 it = repeats.iterator();
525 while (it.hasNext()) {
526 Alarm alarm = it.next();
527 alarm.when += alarm.count * alarm.repeatInterval;
528 addAlarmLocked(alarm);
529 }
530
531 if (alarmList.size() > 0) {
532 setLocked(alarmList.get(0));
533 }
534 }
535
536 /**
537 * This Comparator sorts Alarms into increasing time order.
538 */
539 public static class IncreasingTimeOrder implements Comparator<Alarm> {
540 public int compare(Alarm a1, Alarm a2) {
541 long when1 = a1.when;
542 long when2 = a2.when;
543 if (when1 - when2 > 0) {
544 return 1;
545 }
546 if (when1 - when2 < 0) {
547 return -1;
548 }
549 return 0;
550 }
551 }
552
553 private static class Alarm {
554 public int type;
555 public int count;
556 public long when;
557 public long repeatInterval;
558 public PendingIntent operation;
559
560 public Alarm() {
561 when = 0;
562 repeatInterval = 0;
563 operation = null;
564 }
565
566 @Override
567 public String toString()
568 {
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700569 StringBuilder sb = new StringBuilder(128);
570 sb.append("Alarm{");
571 sb.append(Integer.toHexString(System.identityHashCode(this)));
572 sb.append(" type ");
573 sb.append(type);
574 sb.append(" ");
575 sb.append(operation.getTargetPackage());
576 sb.append('}');
577 return sb.toString();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800578 }
579
580 public void dump(PrintWriter pw, String prefix)
581 {
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700582 pw.print(prefix); pw.print("type="); pw.print(type);
583 pw.print(" when="); pw.print(when);
584 pw.print(" repeatInterval="); pw.print(repeatInterval);
585 pw.print(" count="); pw.println(count);
586 pw.print(prefix); pw.print("operation="); pw.println(operation);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800587 }
588 }
589
590 private class AlarmThread extends Thread
591 {
592 public AlarmThread()
593 {
594 super("AlarmManager");
595 }
596
597 public void run()
598 {
599 while (true)
600 {
601 int result = waitForAlarm(mDescriptor);
602
603 ArrayList<Alarm> triggerList = new ArrayList<Alarm>();
604
605 if ((result & TIME_CHANGED_MASK) != 0) {
606 remove(mTimeTickSender);
607 mClockReceiver.scheduleTimeTickEvent();
608 mContext.sendBroadcast(new Intent(Intent.ACTION_TIME_CHANGED));
609 }
610
611 synchronized (mLock) {
612 final long nowRTC = System.currentTimeMillis();
613 final long nowELAPSED = SystemClock.elapsedRealtime();
614 if (localLOGV) Log.v(
615 TAG, "Checking for alarms... rtc=" + nowRTC
616 + ", elapsed=" + nowELAPSED);
617
618 if ((result & RTC_WAKEUP_MASK) != 0)
619 triggerAlarmsLocked(mRtcWakeupAlarms, triggerList, nowRTC);
620
621 if ((result & RTC_MASK) != 0)
622 triggerAlarmsLocked(mRtcAlarms, triggerList, nowRTC);
623
624 if ((result & ELAPSED_REALTIME_WAKEUP_MASK) != 0)
625 triggerAlarmsLocked(mElapsedRealtimeWakeupAlarms, triggerList, nowELAPSED);
626
627 if ((result & ELAPSED_REALTIME_MASK) != 0)
628 triggerAlarmsLocked(mElapsedRealtimeAlarms, triggerList, nowELAPSED);
629
630 // now trigger the alarms
631 Iterator<Alarm> it = triggerList.iterator();
632 while (it.hasNext()) {
633 Alarm alarm = it.next();
634 try {
635 if (localLOGV) Log.v(TAG, "sending alarm " + alarm);
636 alarm.operation.send(mContext, 0,
637 mBackgroundIntent.putExtra(
638 Intent.EXTRA_ALARM_COUNT, alarm.count),
639 mResultReceiver, mHandler);
640
641 // we have an active broadcast so stay awake.
642 if (mBroadcastRefCount == 0) {
643 mWakeLock.acquire();
644 }
645 mBroadcastRefCount++;
646
647 BroadcastStats bs = getStatsLocked(alarm.operation);
648 if (bs.nesting == 0) {
649 bs.startTime = nowELAPSED;
650 } else {
651 bs.nesting++;
652 }
653 if (alarm.type == AlarmManager.ELAPSED_REALTIME_WAKEUP
654 || alarm.type == AlarmManager.RTC_WAKEUP) {
655 bs.numWakeup++;
656 ActivityManagerNative.noteWakeupAlarm(
657 alarm.operation);
658 }
659 } catch (PendingIntent.CanceledException e) {
660 if (alarm.repeatInterval > 0) {
661 // This IntentSender is no longer valid, but this
662 // is a repeating alarm, so toss the hoser.
663 remove(alarm.operation);
664 }
665 } catch (RuntimeException e) {
666 Log.w(TAG, "Failure sending alarm.", e);
667 }
668 }
669 }
670 }
671 }
672 }
673
674 private class AlarmHandler extends Handler {
675 public static final int ALARM_EVENT = 1;
676 public static final int MINUTE_CHANGE_EVENT = 2;
677 public static final int DATE_CHANGE_EVENT = 3;
678
679 public AlarmHandler() {
680 }
681
682 public void handleMessage(Message msg) {
683 if (msg.what == ALARM_EVENT) {
684 ArrayList<Alarm> triggerList = new ArrayList<Alarm>();
685 synchronized (mLock) {
686 final long nowRTC = System.currentTimeMillis();
687 triggerAlarmsLocked(mRtcWakeupAlarms, triggerList, nowRTC);
688 triggerAlarmsLocked(mRtcAlarms, triggerList, nowRTC);
689 triggerAlarmsLocked(mElapsedRealtimeWakeupAlarms, triggerList, nowRTC);
690 triggerAlarmsLocked(mElapsedRealtimeAlarms, triggerList, nowRTC);
691 }
692
693 // now trigger the alarms without the lock held
694 Iterator<Alarm> it = triggerList.iterator();
695 while (it.hasNext())
696 {
697 Alarm alarm = it.next();
698 try {
699 alarm.operation.send();
700 } catch (PendingIntent.CanceledException e) {
701 if (alarm.repeatInterval > 0) {
702 // This IntentSender is no longer valid, but this
703 // is a repeating alarm, so toss the hoser.
704 remove(alarm.operation);
705 }
706 }
707 }
708 }
709 }
710 }
711
712 class ClockReceiver extends BroadcastReceiver {
713 public ClockReceiver() {
714 IntentFilter filter = new IntentFilter();
715 filter.addAction(Intent.ACTION_TIME_TICK);
716 filter.addAction(Intent.ACTION_DATE_CHANGED);
717 mContext.registerReceiver(this, filter);
718 }
719
720 @Override
721 public void onReceive(Context context, Intent intent) {
722 if (intent.getAction().equals(Intent.ACTION_TIME_TICK)) {
723 scheduleTimeTickEvent();
724 } else if (intent.getAction().equals(Intent.ACTION_DATE_CHANGED)) {
725 // Since the kernel does not keep track of DST, we need to
726 // reset the TZ information at the beginning of each day
727 // based off of the current Zone gmt offset + userspace tracked
728 // daylight savings information.
729 TimeZone zone = TimeZone.getTimeZone(SystemProperties.get(TIMEZONE_PROPERTY));
730 int gmtOffset = (zone.getRawOffset() + zone.getDSTSavings()) / 60000;
731
732 setKernelTimezone(mDescriptor, -(gmtOffset));
733 scheduleDateChangedEvent();
734 }
735 }
736
737 public void scheduleTimeTickEvent() {
738 Calendar calendar = Calendar.getInstance();
739 calendar.setTimeInMillis(System.currentTimeMillis());
740 calendar.add(Calendar.MINUTE, 1);
741 calendar.set(Calendar.SECOND, 0);
742 calendar.set(Calendar.MILLISECOND, 0);
743
744 set(AlarmManager.RTC, calendar.getTimeInMillis(), mTimeTickSender);
745 }
746
747 public void scheduleDateChangedEvent() {
748 Calendar calendar = Calendar.getInstance();
749 calendar.setTimeInMillis(System.currentTimeMillis());
750 calendar.set(Calendar.HOUR, 0);
751 calendar.set(Calendar.MINUTE, 0);
752 calendar.set(Calendar.SECOND, 0);
753 calendar.set(Calendar.MILLISECOND, 0);
754 calendar.add(Calendar.DAY_OF_MONTH, 1);
755
756 set(AlarmManager.RTC, calendar.getTimeInMillis(), mDateChangeSender);
757 }
758 }
759
760 class UninstallReceiver extends BroadcastReceiver {
761 public UninstallReceiver() {
762 IntentFilter filter = new IntentFilter();
763 filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
764 filter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
765 filter.addDataScheme("package");
766 mContext.registerReceiver(this, filter);
767 }
768
769 @Override
770 public void onReceive(Context context, Intent intent) {
771 synchronized (mLock) {
772 Uri data = intent.getData();
773 if (data != null) {
774 String pkg = data.getSchemeSpecificPart();
775 removeLocked(pkg);
776 mBroadcastStats.remove(pkg);
777 }
778 }
779 }
780 }
781
782 private final BroadcastStats getStatsLocked(PendingIntent pi) {
783 String pkg = pi.getTargetPackage();
784 BroadcastStats bs = mBroadcastStats.get(pkg);
785 if (bs == null) {
786 bs = new BroadcastStats();
787 mBroadcastStats.put(pkg, bs);
788 }
789 return bs;
790 }
791
792 class ResultReceiver implements PendingIntent.OnFinished {
793 public void onSendFinished(PendingIntent pi, Intent intent, int resultCode,
794 String resultData, Bundle resultExtras) {
795 synchronized (mLock) {
796 BroadcastStats bs = getStatsLocked(pi);
797 if (bs != null) {
798 bs.nesting--;
799 if (bs.nesting <= 0) {
800 bs.nesting = 0;
801 bs.aggregateTime += SystemClock.elapsedRealtime()
802 - bs.startTime;
803 Intent.FilterComparison fc = new Intent.FilterComparison(intent);
804 FilterStats fs = bs.filterStats.get(fc);
805 if (fs == null) {
806 fs = new FilterStats();
807 bs.filterStats.put(fc, fs);
808 }
809 fs.count++;
810 }
811 }
812 mBroadcastRefCount--;
813 if (mBroadcastRefCount == 0) {
814 mWakeLock.release();
815 }
816 }
817 }
818 }
819}