blob: e088417a40d56304e48a6f95a3d0fcc88c52ea47 [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
Dianne Hackborn21f1bd12010-02-19 17:02:21 -080019import android.app.Activity;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080020import android.app.ActivityManagerNative;
21import android.app.AlarmManager;
22import android.app.IAlarmManager;
23import android.app.PendingIntent;
24import android.content.BroadcastReceiver;
25import android.content.Context;
26import android.content.Intent;
27import android.content.IntentFilter;
28import android.content.pm.PackageManager;
29import android.net.Uri;
30import android.os.Binder;
31import android.os.Bundle;
32import android.os.Handler;
33import android.os.Message;
34import android.os.PowerManager;
35import android.os.SystemClock;
36import android.os.SystemProperties;
37import android.text.TextUtils;
38import android.text.format.Time;
39import android.util.EventLog;
Joe Onorato8a9b2202010-02-26 18:56:32 -080040import android.util.Slog;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080041
42import java.io.FileDescriptor;
43import java.io.PrintWriter;
44import java.util.ArrayList;
45import java.util.Calendar;
46import java.util.Collections;
47import java.util.Comparator;
Mike Lockwood1f7b4132009-11-20 15:12:51 -050048import java.util.Date;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080049import java.util.HashMap;
50import java.util.Iterator;
51import java.util.Map;
52import java.util.TimeZone;
53
54class AlarmManagerService extends IAlarmManager.Stub {
55 // The threshold for how long an alarm can be late before we print a
56 // warning message. The time duration is in milliseconds.
57 private static final long LATE_ALARM_THRESHOLD = 10 * 1000;
58
59 private static final int RTC_WAKEUP_MASK = 1 << AlarmManager.RTC_WAKEUP;
60 private static final int RTC_MASK = 1 << AlarmManager.RTC;
61 private static final int ELAPSED_REALTIME_WAKEUP_MASK = 1 << AlarmManager.ELAPSED_REALTIME_WAKEUP;
62 private static final int ELAPSED_REALTIME_MASK = 1 << AlarmManager.ELAPSED_REALTIME;
63 private static final int TIME_CHANGED_MASK = 1 << 16;
64
65 private static final String TAG = "AlarmManager";
66 private static final String ClockReceiver_TAG = "ClockReceiver";
67 private static final boolean localLOGV = false;
68 private static final int ALARM_EVENT = 1;
69 private static final String TIMEZONE_PROPERTY = "persist.sys.timezone";
70
71 private static final Intent mBackgroundIntent
72 = new Intent().addFlags(Intent.FLAG_FROM_BACKGROUND);
73
74 private final Context mContext;
75
76 private Object mLock = new Object();
77
78 private final ArrayList<Alarm> mRtcWakeupAlarms = new ArrayList<Alarm>();
79 private final ArrayList<Alarm> mRtcAlarms = new ArrayList<Alarm>();
80 private final ArrayList<Alarm> mElapsedRealtimeWakeupAlarms = new ArrayList<Alarm>();
81 private final ArrayList<Alarm> mElapsedRealtimeAlarms = new ArrayList<Alarm>();
82 private final IncreasingTimeOrder mIncreasingTimeOrder = new IncreasingTimeOrder();
83
84 // slots corresponding with the inexact-repeat interval buckets,
85 // ordered from shortest to longest
86 private static final long sInexactSlotIntervals[] = {
87 AlarmManager.INTERVAL_FIFTEEN_MINUTES,
88 AlarmManager.INTERVAL_HALF_HOUR,
89 AlarmManager.INTERVAL_HOUR,
90 AlarmManager.INTERVAL_HALF_DAY,
91 AlarmManager.INTERVAL_DAY
92 };
93 private long mInexactDeliveryTimes[] = { 0, 0, 0, 0, 0};
94
95 private int mDescriptor;
96 private int mBroadcastRefCount = 0;
97 private PowerManager.WakeLock mWakeLock;
98 private final AlarmThread mWaitThread = new AlarmThread();
99 private final AlarmHandler mHandler = new AlarmHandler();
100 private ClockReceiver mClockReceiver;
101 private UninstallReceiver mUninstallReceiver;
102 private final ResultReceiver mResultReceiver = new ResultReceiver();
103 private final PendingIntent mTimeTickSender;
104 private final PendingIntent mDateChangeSender;
105
106 private static final class FilterStats {
107 int count;
108 }
109
110 private static final class BroadcastStats {
111 long aggregateTime;
112 int numWakeup;
113 long startTime;
114 int nesting;
115 HashMap<Intent.FilterComparison, FilterStats> filterStats
116 = new HashMap<Intent.FilterComparison, FilterStats>();
117 }
118
119 private final HashMap<String, BroadcastStats> mBroadcastStats
120 = new HashMap<String, BroadcastStats>();
121
122 public AlarmManagerService(Context context) {
123 mContext = context;
124 mDescriptor = init();
125 PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
126 mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
127
128 mTimeTickSender = PendingIntent.getBroadcast(context, 0,
129 new Intent(Intent.ACTION_TIME_TICK).addFlags(
130 Intent.FLAG_RECEIVER_REGISTERED_ONLY), 0);
Dianne Hackborn1c633fc2009-12-08 19:45:14 -0800131 Intent intent = new Intent(Intent.ACTION_DATE_CHANGED);
132 intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
133 mDateChangeSender = PendingIntent.getBroadcast(context, 0, intent, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800134
135 // now that we have initied the driver schedule the alarm
136 mClockReceiver= new ClockReceiver();
137 mClockReceiver.scheduleTimeTickEvent();
138 mClockReceiver.scheduleDateChangedEvent();
139 mUninstallReceiver = new UninstallReceiver();
140
141 if (mDescriptor != -1) {
142 mWaitThread.start();
143 } else {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800144 Slog.w(TAG, "Failed to open alarm driver. Falling back to a handler.");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800145 }
146 }
147
148 protected void finalize() throws Throwable {
149 try {
150 close(mDescriptor);
151 } finally {
152 super.finalize();
153 }
154 }
155
156 public void set(int type, long triggerAtTime, PendingIntent operation) {
157 setRepeating(type, triggerAtTime, 0, operation);
158 }
159
160 public void setRepeating(int type, long triggerAtTime, long interval,
161 PendingIntent operation) {
162 if (operation == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800163 Slog.w(TAG, "set/setRepeating ignored because there is no intent");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800164 return;
165 }
166 synchronized (mLock) {
167 Alarm alarm = new Alarm();
168 alarm.type = type;
169 alarm.when = triggerAtTime;
170 alarm.repeatInterval = interval;
171 alarm.operation = operation;
172
173 // Remove this alarm if already scheduled.
174 removeLocked(operation);
175
Joe Onorato8a9b2202010-02-26 18:56:32 -0800176 if (localLOGV) Slog.v(TAG, "set: " + alarm);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800177
178 int index = addAlarmLocked(alarm);
179 if (index == 0) {
180 setLocked(alarm);
181 }
182 }
183 }
184
185 public void setInexactRepeating(int type, long triggerAtTime, long interval,
186 PendingIntent operation) {
187 if (operation == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800188 Slog.w(TAG, "setInexactRepeating ignored because there is no intent");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800189 return;
190 }
191
192 // find the slot in the delivery-times array that we will use
193 int intervalSlot;
194 for (intervalSlot = 0; intervalSlot < sInexactSlotIntervals.length; intervalSlot++) {
195 if (sInexactSlotIntervals[intervalSlot] == interval) {
196 break;
197 }
198 }
199
200 // Non-bucket intervals just fall back to the less-efficient
201 // unbucketed recurring alarm implementation
202 if (intervalSlot >= sInexactSlotIntervals.length) {
203 setRepeating(type, triggerAtTime, interval, operation);
204 return;
205 }
206
207 // Align bucketed alarm deliveries by trying to match
208 // the shortest-interval bucket already scheduled
209 long bucketTime = 0;
210 for (int slot = 0; slot < mInexactDeliveryTimes.length; slot++) {
211 if (mInexactDeliveryTimes[slot] > 0) {
212 bucketTime = mInexactDeliveryTimes[slot];
213 break;
214 }
215 }
216
217 if (bucketTime == 0) {
218 // If nothing is scheduled yet, just start at the requested time
219 bucketTime = triggerAtTime;
220 } else {
221 // Align the new alarm with the existing bucketed sequence. To achieve
222 // alignment, we slide the start time around by min{interval, slot interval}
223 long adjustment = (interval <= sInexactSlotIntervals[intervalSlot])
224 ? interval : sInexactSlotIntervals[intervalSlot];
225
226 // The bucket may have started in the past; adjust
227 while (bucketTime < triggerAtTime) {
228 bucketTime += adjustment;
229 }
230
231 // Or the bucket may be set to start more than an interval beyond
232 // our requested trigger time; pull it back to meet our needs
233 while (bucketTime > triggerAtTime + adjustment) {
234 bucketTime -= adjustment;
235 }
236 }
237
238 // Remember where this bucket started (reducing the amount of later
239 // fixup required) and set the alarm with the new, bucketed start time.
Joe Onorato8a9b2202010-02-26 18:56:32 -0800240 if (localLOGV) Slog.v(TAG, "setInexactRepeating: interval=" + interval
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800241 + " bucketTime=" + bucketTime);
242 mInexactDeliveryTimes[intervalSlot] = bucketTime;
243 setRepeating(type, bucketTime, interval, operation);
244 }
245
Dan Egnor97e44942010-02-04 20:27:47 -0800246 public void setTime(long millis) {
247 mContext.enforceCallingOrSelfPermission(
248 "android.permission.SET_TIME",
249 "setTime");
250
251 SystemClock.setCurrentTimeMillis(millis);
252 }
253
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800254 public void setTimeZone(String tz) {
255 mContext.enforceCallingOrSelfPermission(
256 "android.permission.SET_TIME_ZONE",
257 "setTimeZone");
258
259 if (TextUtils.isEmpty(tz)) return;
260 TimeZone zone = TimeZone.getTimeZone(tz);
261 // Prevent reentrant calls from stepping on each other when writing
262 // the time zone property
263 boolean timeZoneWasChanged = false;
264 synchronized (this) {
265 String current = SystemProperties.get(TIMEZONE_PROPERTY);
266 if (current == null || !current.equals(zone.getID())) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800267 if (localLOGV) Slog.v(TAG, "timezone changed: " + current + ", new=" + zone.getID());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800268 timeZoneWasChanged = true;
269 SystemProperties.set(TIMEZONE_PROPERTY, zone.getID());
270 }
271
272 // Update the kernel timezone information
273 // Kernel tracks time offsets as 'minutes west of GMT'
Mike Lockwood1f7b4132009-11-20 15:12:51 -0500274 int gmtOffset = zone.getRawOffset();
275 if (zone.inDaylightTime(new Date(System.currentTimeMillis()))) {
276 gmtOffset += zone.getDSTSavings();
277 }
278 setKernelTimezone(mDescriptor, -(gmtOffset / 60000));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800279 }
280
281 TimeZone.setDefault(null);
282
283 if (timeZoneWasChanged) {
284 Intent intent = new Intent(Intent.ACTION_TIMEZONE_CHANGED);
Dianne Hackborn1c633fc2009-12-08 19:45:14 -0800285 intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800286 intent.putExtra("time-zone", zone.getID());
287 mContext.sendBroadcast(intent);
288 }
289 }
290
291 public void remove(PendingIntent operation) {
292 if (operation == null) {
293 return;
294 }
295 synchronized (mLock) {
296 removeLocked(operation);
297 }
298 }
299
300 public void removeLocked(PendingIntent operation) {
301 removeLocked(mRtcWakeupAlarms, operation);
302 removeLocked(mRtcAlarms, operation);
303 removeLocked(mElapsedRealtimeWakeupAlarms, operation);
304 removeLocked(mElapsedRealtimeAlarms, operation);
305 }
306
307 private void removeLocked(ArrayList<Alarm> alarmList,
308 PendingIntent operation) {
309 if (alarmList.size() <= 0) {
310 return;
311 }
312
313 // iterator over the list removing any it where the intent match
314 Iterator<Alarm> it = alarmList.iterator();
315
316 while (it.hasNext()) {
317 Alarm alarm = it.next();
318 if (alarm.operation.equals(operation)) {
319 it.remove();
320 }
321 }
322 }
323
324 public void removeLocked(String packageName) {
325 removeLocked(mRtcWakeupAlarms, packageName);
326 removeLocked(mRtcAlarms, packageName);
327 removeLocked(mElapsedRealtimeWakeupAlarms, packageName);
328 removeLocked(mElapsedRealtimeAlarms, packageName);
329 }
330
331 private void removeLocked(ArrayList<Alarm> alarmList,
332 String packageName) {
333 if (alarmList.size() <= 0) {
334 return;
335 }
336
337 // iterator over the list removing any it where the intent match
338 Iterator<Alarm> it = alarmList.iterator();
339
340 while (it.hasNext()) {
341 Alarm alarm = it.next();
342 if (alarm.operation.getTargetPackage().equals(packageName)) {
343 it.remove();
344 }
345 }
346 }
347
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800348 public boolean lookForPackageLocked(String packageName) {
349 return lookForPackageLocked(mRtcWakeupAlarms, packageName)
350 || lookForPackageLocked(mRtcAlarms, packageName)
351 || lookForPackageLocked(mElapsedRealtimeWakeupAlarms, packageName)
352 || lookForPackageLocked(mElapsedRealtimeAlarms, packageName);
353 }
354
355 private boolean lookForPackageLocked(ArrayList<Alarm> alarmList, String packageName) {
356 for (int i=alarmList.size()-1; i>=0; i--) {
357 if (alarmList.get(i).operation.getTargetPackage().equals(packageName)) {
358 return true;
359 }
360 }
361 return false;
362 }
363
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800364 private ArrayList<Alarm> getAlarmList(int type) {
365 switch (type) {
366 case AlarmManager.RTC_WAKEUP: return mRtcWakeupAlarms;
367 case AlarmManager.RTC: return mRtcAlarms;
368 case AlarmManager.ELAPSED_REALTIME_WAKEUP: return mElapsedRealtimeWakeupAlarms;
369 case AlarmManager.ELAPSED_REALTIME: return mElapsedRealtimeAlarms;
370 }
371
372 return null;
373 }
374
375 private int addAlarmLocked(Alarm alarm) {
376 ArrayList<Alarm> alarmList = getAlarmList(alarm.type);
377
378 int index = Collections.binarySearch(alarmList, alarm, mIncreasingTimeOrder);
379 if (index < 0) {
380 index = 0 - index - 1;
381 }
Joe Onorato8a9b2202010-02-26 18:56:32 -0800382 if (localLOGV) Slog.v(TAG, "Adding alarm " + alarm + " at " + index);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800383 alarmList.add(index, alarm);
384
385 if (localLOGV) {
386 // Display the list of alarms for this alarm type
Joe Onorato8a9b2202010-02-26 18:56:32 -0800387 Slog.v(TAG, "alarms: " + alarmList.size() + " type: " + alarm.type);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800388 int position = 0;
389 for (Alarm a : alarmList) {
390 Time time = new Time();
391 time.set(a.when);
392 String timeStr = time.format("%b %d %I:%M:%S %p");
Joe Onorato8a9b2202010-02-26 18:56:32 -0800393 Slog.v(TAG, position + ": " + timeStr
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800394 + " " + a.operation.getTargetPackage());
395 position += 1;
396 }
397 }
398
399 return index;
400 }
401
402 public long timeToNextAlarm() {
Jeff Brown11c5f1a2010-03-31 15:29:40 -0700403 long nextAlarm = Long.MAX_VALUE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800404 synchronized (mLock) {
405 for (int i=AlarmManager.RTC_WAKEUP;
406 i<=AlarmManager.ELAPSED_REALTIME; i++) {
407 ArrayList<Alarm> alarmList = getAlarmList(i);
408 if (alarmList.size() > 0) {
409 Alarm a = alarmList.get(0);
410 if (a.when < nextAlarm) {
411 nextAlarm = a.when;
412 }
413 }
414 }
415 }
416 return nextAlarm;
417 }
418
419 private void setLocked(Alarm alarm)
420 {
421 if (mDescriptor != -1)
422 {
Jeff Brown11c5f1a2010-03-31 15:29:40 -0700423 // The kernel never triggers alarms with negative wakeup times
424 // so we ensure they are positive.
425 long alarmSeconds, alarmNanoseconds;
426 if (alarm.when < 0) {
427 alarmSeconds = 0;
428 alarmNanoseconds = 0;
429 } else {
430 alarmSeconds = alarm.when / 1000;
431 alarmNanoseconds = (alarm.when % 1000) * 1000 * 1000;
432 }
433
434 set(mDescriptor, alarm.type, alarmSeconds, alarmNanoseconds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800435 }
436 else
437 {
438 Message msg = Message.obtain();
439 msg.what = ALARM_EVENT;
440
441 mHandler.removeMessages(ALARM_EVENT);
442 mHandler.sendMessageAtTime(msg, alarm.when);
443 }
444 }
445
446 @Override
447 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
448 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
449 != PackageManager.PERMISSION_GRANTED) {
450 pw.println("Permission Denial: can't dump AlarmManager from from pid="
451 + Binder.getCallingPid()
452 + ", uid=" + Binder.getCallingUid());
453 return;
454 }
455
456 synchronized (mLock) {
457 pw.println("Current Alarm Manager state:");
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700458 if (mRtcWakeupAlarms.size() > 0 || mRtcAlarms.size() > 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800459 pw.println(" ");
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700460 pw.print(" Realtime wakeup (now=");
461 pw.print(System.currentTimeMillis()); pw.println("):");
462 if (mRtcWakeupAlarms.size() > 0) {
463 dumpAlarmList(pw, mRtcWakeupAlarms, " ", "RTC_WAKEUP");
464 }
465 if (mRtcAlarms.size() > 0) {
466 dumpAlarmList(pw, mRtcAlarms, " ", "RTC");
467 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800468 }
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700469 if (mElapsedRealtimeWakeupAlarms.size() > 0 || mElapsedRealtimeAlarms.size() > 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800470 pw.println(" ");
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700471 pw.print(" Elapsed realtime wakeup (now=");
472 pw.print(SystemClock.elapsedRealtime()); pw.println("):");
473 if (mElapsedRealtimeWakeupAlarms.size() > 0) {
474 dumpAlarmList(pw, mElapsedRealtimeWakeupAlarms, " ", "ELAPSED_WAKEUP");
475 }
476 if (mElapsedRealtimeAlarms.size() > 0) {
477 dumpAlarmList(pw, mElapsedRealtimeAlarms, " ", "ELAPSED");
478 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800479 }
480
481 pw.println(" ");
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700482 pw.print(" Broadcast ref count: "); pw.println(mBroadcastRefCount);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800483
484 pw.println(" ");
485 pw.println(" Alarm Stats:");
486 for (Map.Entry<String, BroadcastStats> be : mBroadcastStats.entrySet()) {
487 BroadcastStats bs = be.getValue();
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700488 pw.print(" "); pw.println(be.getKey());
489 pw.print(" "); pw.print(bs.aggregateTime);
490 pw.print("ms running, "); pw.print(bs.numWakeup);
491 pw.println(" wakeups");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800492 for (Map.Entry<Intent.FilterComparison, FilterStats> fe
493 : bs.filterStats.entrySet()) {
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700494 pw.print(" "); pw.print(fe.getValue().count);
495 pw.print(" alarms: ");
496 pw.println(fe.getKey().getIntent().toShortString(true, false));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800497 }
498 }
499 }
500 }
501
502 private static final void dumpAlarmList(PrintWriter pw, ArrayList<Alarm> list, String prefix, String label) {
503 for (int i=list.size()-1; i>=0; i--) {
504 Alarm a = list.get(i);
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700505 pw.print(prefix); pw.print(label); pw.print(" #"); pw.print(i);
506 pw.print(": "); pw.println(a);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800507 a.dump(pw, prefix + " ");
508 }
509 }
510
511 private native int init();
512 private native void close(int fd);
Jeff Brown11c5f1a2010-03-31 15:29:40 -0700513 private native void set(int fd, int type, long seconds, long nanoseconds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800514 private native int waitForAlarm(int fd);
515 private native int setKernelTimezone(int fd, int minuteswest);
516
517 private void triggerAlarmsLocked(ArrayList<Alarm> alarmList,
518 ArrayList<Alarm> triggerList,
519 long now)
520 {
521 Iterator<Alarm> it = alarmList.iterator();
522 ArrayList<Alarm> repeats = new ArrayList<Alarm>();
523
524 while (it.hasNext())
525 {
526 Alarm alarm = it.next();
527
Joe Onorato8a9b2202010-02-26 18:56:32 -0800528 if (localLOGV) Slog.v(TAG, "Checking active alarm when=" + alarm.when + " " + alarm);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800529
530 if (alarm.when > now) {
531 // don't fire alarms in the future
532 break;
533 }
534
535 // If the alarm is late, then print a warning message.
536 // Note that this can happen if the user creates a new event on
537 // the Calendar app with a reminder that is in the past. In that
538 // case, the reminder alarm will fire immediately.
539 if (localLOGV && now - alarm.when > LATE_ALARM_THRESHOLD) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800540 Slog.v(TAG, "alarm is late! alarm time: " + alarm.when
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800541 + " now: " + now + " delay (in seconds): "
542 + (now - alarm.when) / 1000);
543 }
544
545 // Recurring alarms may have passed several alarm intervals while the
546 // phone was asleep or off, so pass a trigger count when sending them.
Joe Onorato8a9b2202010-02-26 18:56:32 -0800547 if (localLOGV) Slog.v(TAG, "Alarm triggering: " + alarm);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800548 alarm.count = 1;
549 if (alarm.repeatInterval > 0) {
550 // this adjustment will be zero if we're late by
551 // less than one full repeat interval
552 alarm.count += (now - alarm.when) / alarm.repeatInterval;
553 }
554 triggerList.add(alarm);
555
556 // remove the alarm from the list
557 it.remove();
558
559 // if it repeats queue it up to be read-added to the list
560 if (alarm.repeatInterval > 0) {
561 repeats.add(alarm);
562 }
563 }
564
565 // reset any repeating alarms.
566 it = repeats.iterator();
567 while (it.hasNext()) {
568 Alarm alarm = it.next();
569 alarm.when += alarm.count * alarm.repeatInterval;
570 addAlarmLocked(alarm);
571 }
572
573 if (alarmList.size() > 0) {
574 setLocked(alarmList.get(0));
575 }
576 }
577
578 /**
579 * This Comparator sorts Alarms into increasing time order.
580 */
581 public static class IncreasingTimeOrder implements Comparator<Alarm> {
582 public int compare(Alarm a1, Alarm a2) {
583 long when1 = a1.when;
584 long when2 = a2.when;
585 if (when1 - when2 > 0) {
586 return 1;
587 }
588 if (when1 - when2 < 0) {
589 return -1;
590 }
591 return 0;
592 }
593 }
594
595 private static class Alarm {
596 public int type;
597 public int count;
598 public long when;
599 public long repeatInterval;
600 public PendingIntent operation;
601
602 public Alarm() {
603 when = 0;
604 repeatInterval = 0;
605 operation = null;
606 }
607
608 @Override
609 public String toString()
610 {
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700611 StringBuilder sb = new StringBuilder(128);
612 sb.append("Alarm{");
613 sb.append(Integer.toHexString(System.identityHashCode(this)));
614 sb.append(" type ");
615 sb.append(type);
616 sb.append(" ");
617 sb.append(operation.getTargetPackage());
618 sb.append('}');
619 return sb.toString();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800620 }
621
622 public void dump(PrintWriter pw, String prefix)
623 {
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700624 pw.print(prefix); pw.print("type="); pw.print(type);
625 pw.print(" when="); pw.print(when);
626 pw.print(" repeatInterval="); pw.print(repeatInterval);
627 pw.print(" count="); pw.println(count);
628 pw.print(prefix); pw.print("operation="); pw.println(operation);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800629 }
630 }
631
632 private class AlarmThread extends Thread
633 {
634 public AlarmThread()
635 {
636 super("AlarmManager");
637 }
638
639 public void run()
640 {
641 while (true)
642 {
643 int result = waitForAlarm(mDescriptor);
644
645 ArrayList<Alarm> triggerList = new ArrayList<Alarm>();
646
647 if ((result & TIME_CHANGED_MASK) != 0) {
648 remove(mTimeTickSender);
649 mClockReceiver.scheduleTimeTickEvent();
Dianne Hackborn1c633fc2009-12-08 19:45:14 -0800650 Intent intent = new Intent(Intent.ACTION_TIME_CHANGED);
651 intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
652 mContext.sendBroadcast(intent);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800653 }
654
655 synchronized (mLock) {
656 final long nowRTC = System.currentTimeMillis();
657 final long nowELAPSED = SystemClock.elapsedRealtime();
Joe Onorato8a9b2202010-02-26 18:56:32 -0800658 if (localLOGV) Slog.v(
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800659 TAG, "Checking for alarms... rtc=" + nowRTC
660 + ", elapsed=" + nowELAPSED);
661
662 if ((result & RTC_WAKEUP_MASK) != 0)
663 triggerAlarmsLocked(mRtcWakeupAlarms, triggerList, nowRTC);
664
665 if ((result & RTC_MASK) != 0)
666 triggerAlarmsLocked(mRtcAlarms, triggerList, nowRTC);
667
668 if ((result & ELAPSED_REALTIME_WAKEUP_MASK) != 0)
669 triggerAlarmsLocked(mElapsedRealtimeWakeupAlarms, triggerList, nowELAPSED);
670
671 if ((result & ELAPSED_REALTIME_MASK) != 0)
672 triggerAlarmsLocked(mElapsedRealtimeAlarms, triggerList, nowELAPSED);
673
674 // now trigger the alarms
675 Iterator<Alarm> it = triggerList.iterator();
676 while (it.hasNext()) {
677 Alarm alarm = it.next();
678 try {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800679 if (localLOGV) Slog.v(TAG, "sending alarm " + alarm);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800680 alarm.operation.send(mContext, 0,
681 mBackgroundIntent.putExtra(
682 Intent.EXTRA_ALARM_COUNT, alarm.count),
683 mResultReceiver, mHandler);
684
685 // we have an active broadcast so stay awake.
686 if (mBroadcastRefCount == 0) {
687 mWakeLock.acquire();
688 }
689 mBroadcastRefCount++;
690
691 BroadcastStats bs = getStatsLocked(alarm.operation);
692 if (bs.nesting == 0) {
693 bs.startTime = nowELAPSED;
694 } else {
695 bs.nesting++;
696 }
697 if (alarm.type == AlarmManager.ELAPSED_REALTIME_WAKEUP
698 || alarm.type == AlarmManager.RTC_WAKEUP) {
699 bs.numWakeup++;
700 ActivityManagerNative.noteWakeupAlarm(
701 alarm.operation);
702 }
703 } catch (PendingIntent.CanceledException e) {
704 if (alarm.repeatInterval > 0) {
705 // This IntentSender is no longer valid, but this
706 // is a repeating alarm, so toss the hoser.
707 remove(alarm.operation);
708 }
709 } catch (RuntimeException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800710 Slog.w(TAG, "Failure sending alarm.", e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800711 }
712 }
713 }
714 }
715 }
716 }
717
718 private class AlarmHandler extends Handler {
719 public static final int ALARM_EVENT = 1;
720 public static final int MINUTE_CHANGE_EVENT = 2;
721 public static final int DATE_CHANGE_EVENT = 3;
722
723 public AlarmHandler() {
724 }
725
726 public void handleMessage(Message msg) {
727 if (msg.what == ALARM_EVENT) {
728 ArrayList<Alarm> triggerList = new ArrayList<Alarm>();
729 synchronized (mLock) {
730 final long nowRTC = System.currentTimeMillis();
731 triggerAlarmsLocked(mRtcWakeupAlarms, triggerList, nowRTC);
732 triggerAlarmsLocked(mRtcAlarms, triggerList, nowRTC);
733 triggerAlarmsLocked(mElapsedRealtimeWakeupAlarms, triggerList, nowRTC);
734 triggerAlarmsLocked(mElapsedRealtimeAlarms, triggerList, nowRTC);
735 }
736
737 // now trigger the alarms without the lock held
738 Iterator<Alarm> it = triggerList.iterator();
739 while (it.hasNext())
740 {
741 Alarm alarm = it.next();
742 try {
743 alarm.operation.send();
744 } catch (PendingIntent.CanceledException e) {
745 if (alarm.repeatInterval > 0) {
746 // This IntentSender is no longer valid, but this
747 // is a repeating alarm, so toss the hoser.
748 remove(alarm.operation);
749 }
750 }
751 }
752 }
753 }
754 }
755
756 class ClockReceiver extends BroadcastReceiver {
757 public ClockReceiver() {
758 IntentFilter filter = new IntentFilter();
759 filter.addAction(Intent.ACTION_TIME_TICK);
760 filter.addAction(Intent.ACTION_DATE_CHANGED);
761 mContext.registerReceiver(this, filter);
762 }
763
764 @Override
765 public void onReceive(Context context, Intent intent) {
766 if (intent.getAction().equals(Intent.ACTION_TIME_TICK)) {
767 scheduleTimeTickEvent();
768 } else if (intent.getAction().equals(Intent.ACTION_DATE_CHANGED)) {
769 // Since the kernel does not keep track of DST, we need to
770 // reset the TZ information at the beginning of each day
771 // based off of the current Zone gmt offset + userspace tracked
772 // daylight savings information.
773 TimeZone zone = TimeZone.getTimeZone(SystemProperties.get(TIMEZONE_PROPERTY));
774 int gmtOffset = (zone.getRawOffset() + zone.getDSTSavings()) / 60000;
775
776 setKernelTimezone(mDescriptor, -(gmtOffset));
777 scheduleDateChangedEvent();
778 }
779 }
780
781 public void scheduleTimeTickEvent() {
782 Calendar calendar = Calendar.getInstance();
783 calendar.setTimeInMillis(System.currentTimeMillis());
784 calendar.add(Calendar.MINUTE, 1);
785 calendar.set(Calendar.SECOND, 0);
786 calendar.set(Calendar.MILLISECOND, 0);
787
788 set(AlarmManager.RTC, calendar.getTimeInMillis(), mTimeTickSender);
789 }
790
791 public void scheduleDateChangedEvent() {
792 Calendar calendar = Calendar.getInstance();
793 calendar.setTimeInMillis(System.currentTimeMillis());
794 calendar.set(Calendar.HOUR, 0);
795 calendar.set(Calendar.MINUTE, 0);
796 calendar.set(Calendar.SECOND, 0);
797 calendar.set(Calendar.MILLISECOND, 0);
798 calendar.add(Calendar.DAY_OF_MONTH, 1);
799
800 set(AlarmManager.RTC, calendar.getTimeInMillis(), mDateChangeSender);
801 }
802 }
803
804 class UninstallReceiver extends BroadcastReceiver {
805 public UninstallReceiver() {
806 IntentFilter filter = new IntentFilter();
807 filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
808 filter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800809 filter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800810 filter.addDataScheme("package");
811 mContext.registerReceiver(this, filter);
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800812 // Register for events related to sdcard installation.
813 IntentFilter sdFilter = new IntentFilter();
Suchi Amalapurapub56ae202010-02-04 22:51:07 -0800814 sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800815 mContext.registerReceiver(this, sdFilter);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800816 }
817
818 @Override
819 public void onReceive(Context context, Intent intent) {
820 synchronized (mLock) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800821 String action = intent.getAction();
822 String pkgList[] = null;
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800823 if (Intent.ACTION_QUERY_PACKAGE_RESTART.equals(action)) {
824 pkgList = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);
825 for (String packageName : pkgList) {
826 if (lookForPackageLocked(packageName)) {
827 setResultCode(Activity.RESULT_OK);
828 return;
829 }
830 }
831 return;
832 } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800833 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
834 } else {
Dianne Hackborn409578f2010-03-10 17:23:43 -0800835 if (Intent.ACTION_PACKAGE_REMOVED.equals(action)
836 && intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
837 // This package is being updated; don't kill its alarms.
838 return;
839 }
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800840 Uri data = intent.getData();
841 if (data != null) {
842 String pkg = data.getSchemeSpecificPart();
843 if (pkg != null) {
844 pkgList = new String[]{pkg};
845 }
846 }
847 }
848 if (pkgList != null && (pkgList.length > 0)) {
849 for (String pkg : pkgList) {
850 removeLocked(pkg);
851 mBroadcastStats.remove(pkg);
852 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800853 }
854 }
855 }
856 }
857
858 private final BroadcastStats getStatsLocked(PendingIntent pi) {
859 String pkg = pi.getTargetPackage();
860 BroadcastStats bs = mBroadcastStats.get(pkg);
861 if (bs == null) {
862 bs = new BroadcastStats();
863 mBroadcastStats.put(pkg, bs);
864 }
865 return bs;
866 }
867
868 class ResultReceiver implements PendingIntent.OnFinished {
869 public void onSendFinished(PendingIntent pi, Intent intent, int resultCode,
870 String resultData, Bundle resultExtras) {
871 synchronized (mLock) {
872 BroadcastStats bs = getStatsLocked(pi);
873 if (bs != null) {
874 bs.nesting--;
875 if (bs.nesting <= 0) {
876 bs.nesting = 0;
877 bs.aggregateTime += SystemClock.elapsedRealtime()
878 - bs.startTime;
879 Intent.FilterComparison fc = new Intent.FilterComparison(intent);
880 FilterStats fs = bs.filterStats.get(fc);
881 if (fs == null) {
882 fs = new FilterStats();
883 bs.filterStats.put(fc, fs);
884 }
885 fs.count++;
886 }
887 }
888 mBroadcastRefCount--;
889 if (mBroadcastRefCount == 0) {
890 mWakeLock.release();
891 }
892 }
893 }
894 }
895}