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