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