blob: 8852cc3664068bbd1b193f3c0af19d35882f6966 [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();
Robert CH Chou64ba8e42009-11-04 21:38:49 +0800125
126 // We have to set current TimeZone info to kernel
127 // because kernel doesn't keep this after reboot
128 String tz = SystemProperties.get(TIMEZONE_PROPERTY);
129 if (tz != null) {
130 setTimeZone(tz);
131 }
132
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800133 PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
134 mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
135
136 mTimeTickSender = PendingIntent.getBroadcast(context, 0,
137 new Intent(Intent.ACTION_TIME_TICK).addFlags(
138 Intent.FLAG_RECEIVER_REGISTERED_ONLY), 0);
Dianne Hackborn1c633fc2009-12-08 19:45:14 -0800139 Intent intent = new Intent(Intent.ACTION_DATE_CHANGED);
140 intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
141 mDateChangeSender = PendingIntent.getBroadcast(context, 0, intent, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800142
143 // now that we have initied the driver schedule the alarm
144 mClockReceiver= new ClockReceiver();
145 mClockReceiver.scheduleTimeTickEvent();
146 mClockReceiver.scheduleDateChangedEvent();
147 mUninstallReceiver = new UninstallReceiver();
148
149 if (mDescriptor != -1) {
150 mWaitThread.start();
151 } else {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800152 Slog.w(TAG, "Failed to open alarm driver. Falling back to a handler.");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800153 }
154 }
155
156 protected void finalize() throws Throwable {
157 try {
158 close(mDescriptor);
159 } finally {
160 super.finalize();
161 }
162 }
163
164 public void set(int type, long triggerAtTime, PendingIntent operation) {
165 setRepeating(type, triggerAtTime, 0, operation);
166 }
167
168 public void setRepeating(int type, long triggerAtTime, long interval,
169 PendingIntent operation) {
170 if (operation == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800171 Slog.w(TAG, "set/setRepeating ignored because there is no intent");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800172 return;
173 }
174 synchronized (mLock) {
175 Alarm alarm = new Alarm();
176 alarm.type = type;
177 alarm.when = triggerAtTime;
178 alarm.repeatInterval = interval;
179 alarm.operation = operation;
180
181 // Remove this alarm if already scheduled.
182 removeLocked(operation);
183
Joe Onorato8a9b2202010-02-26 18:56:32 -0800184 if (localLOGV) Slog.v(TAG, "set: " + alarm);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800185
186 int index = addAlarmLocked(alarm);
187 if (index == 0) {
188 setLocked(alarm);
189 }
190 }
191 }
192
193 public void setInexactRepeating(int type, long triggerAtTime, long interval,
194 PendingIntent operation) {
195 if (operation == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800196 Slog.w(TAG, "setInexactRepeating ignored because there is no intent");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800197 return;
198 }
199
200 // find the slot in the delivery-times array that we will use
201 int intervalSlot;
202 for (intervalSlot = 0; intervalSlot < sInexactSlotIntervals.length; intervalSlot++) {
203 if (sInexactSlotIntervals[intervalSlot] == interval) {
204 break;
205 }
206 }
207
208 // Non-bucket intervals just fall back to the less-efficient
209 // unbucketed recurring alarm implementation
210 if (intervalSlot >= sInexactSlotIntervals.length) {
211 setRepeating(type, triggerAtTime, interval, operation);
212 return;
213 }
214
215 // Align bucketed alarm deliveries by trying to match
216 // the shortest-interval bucket already scheduled
217 long bucketTime = 0;
218 for (int slot = 0; slot < mInexactDeliveryTimes.length; slot++) {
219 if (mInexactDeliveryTimes[slot] > 0) {
220 bucketTime = mInexactDeliveryTimes[slot];
221 break;
222 }
223 }
224
225 if (bucketTime == 0) {
226 // If nothing is scheduled yet, just start at the requested time
227 bucketTime = triggerAtTime;
228 } else {
229 // Align the new alarm with the existing bucketed sequence. To achieve
230 // alignment, we slide the start time around by min{interval, slot interval}
231 long adjustment = (interval <= sInexactSlotIntervals[intervalSlot])
232 ? interval : sInexactSlotIntervals[intervalSlot];
233
234 // The bucket may have started in the past; adjust
235 while (bucketTime < triggerAtTime) {
236 bucketTime += adjustment;
237 }
238
239 // Or the bucket may be set to start more than an interval beyond
240 // our requested trigger time; pull it back to meet our needs
241 while (bucketTime > triggerAtTime + adjustment) {
242 bucketTime -= adjustment;
243 }
244 }
245
246 // Remember where this bucket started (reducing the amount of later
247 // fixup required) and set the alarm with the new, bucketed start time.
Joe Onorato8a9b2202010-02-26 18:56:32 -0800248 if (localLOGV) Slog.v(TAG, "setInexactRepeating: interval=" + interval
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800249 + " bucketTime=" + bucketTime);
250 mInexactDeliveryTimes[intervalSlot] = bucketTime;
251 setRepeating(type, bucketTime, interval, operation);
252 }
253
Dan Egnor97e44942010-02-04 20:27:47 -0800254 public void setTime(long millis) {
255 mContext.enforceCallingOrSelfPermission(
256 "android.permission.SET_TIME",
257 "setTime");
258
259 SystemClock.setCurrentTimeMillis(millis);
260 }
261
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800262 public void setTimeZone(String tz) {
263 mContext.enforceCallingOrSelfPermission(
264 "android.permission.SET_TIME_ZONE",
265 "setTimeZone");
266
267 if (TextUtils.isEmpty(tz)) return;
268 TimeZone zone = TimeZone.getTimeZone(tz);
269 // Prevent reentrant calls from stepping on each other when writing
270 // the time zone property
271 boolean timeZoneWasChanged = false;
272 synchronized (this) {
273 String current = SystemProperties.get(TIMEZONE_PROPERTY);
274 if (current == null || !current.equals(zone.getID())) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800275 if (localLOGV) Slog.v(TAG, "timezone changed: " + current + ", new=" + zone.getID());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800276 timeZoneWasChanged = true;
277 SystemProperties.set(TIMEZONE_PROPERTY, zone.getID());
278 }
279
280 // Update the kernel timezone information
281 // Kernel tracks time offsets as 'minutes west of GMT'
Mike Lockwood1f7b4132009-11-20 15:12:51 -0500282 int gmtOffset = zone.getRawOffset();
283 if (zone.inDaylightTime(new Date(System.currentTimeMillis()))) {
284 gmtOffset += zone.getDSTSavings();
285 }
286 setKernelTimezone(mDescriptor, -(gmtOffset / 60000));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800287 }
288
289 TimeZone.setDefault(null);
290
291 if (timeZoneWasChanged) {
292 Intent intent = new Intent(Intent.ACTION_TIMEZONE_CHANGED);
Dianne Hackborn1c633fc2009-12-08 19:45:14 -0800293 intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800294 intent.putExtra("time-zone", zone.getID());
295 mContext.sendBroadcast(intent);
296 }
297 }
298
299 public void remove(PendingIntent operation) {
300 if (operation == null) {
301 return;
302 }
303 synchronized (mLock) {
304 removeLocked(operation);
305 }
306 }
307
308 public void removeLocked(PendingIntent operation) {
309 removeLocked(mRtcWakeupAlarms, operation);
310 removeLocked(mRtcAlarms, operation);
311 removeLocked(mElapsedRealtimeWakeupAlarms, operation);
312 removeLocked(mElapsedRealtimeAlarms, operation);
313 }
314
315 private void removeLocked(ArrayList<Alarm> alarmList,
316 PendingIntent operation) {
317 if (alarmList.size() <= 0) {
318 return;
319 }
320
321 // iterator over the list removing any it where the intent match
322 Iterator<Alarm> it = alarmList.iterator();
323
324 while (it.hasNext()) {
325 Alarm alarm = it.next();
326 if (alarm.operation.equals(operation)) {
327 it.remove();
328 }
329 }
330 }
331
332 public void removeLocked(String packageName) {
333 removeLocked(mRtcWakeupAlarms, packageName);
334 removeLocked(mRtcAlarms, packageName);
335 removeLocked(mElapsedRealtimeWakeupAlarms, packageName);
336 removeLocked(mElapsedRealtimeAlarms, packageName);
337 }
338
339 private void removeLocked(ArrayList<Alarm> alarmList,
340 String packageName) {
341 if (alarmList.size() <= 0) {
342 return;
343 }
344
345 // iterator over the list removing any it where the intent match
346 Iterator<Alarm> it = alarmList.iterator();
347
348 while (it.hasNext()) {
349 Alarm alarm = it.next();
350 if (alarm.operation.getTargetPackage().equals(packageName)) {
351 it.remove();
352 }
353 }
354 }
355
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800356 public boolean lookForPackageLocked(String packageName) {
357 return lookForPackageLocked(mRtcWakeupAlarms, packageName)
358 || lookForPackageLocked(mRtcAlarms, packageName)
359 || lookForPackageLocked(mElapsedRealtimeWakeupAlarms, packageName)
360 || lookForPackageLocked(mElapsedRealtimeAlarms, packageName);
361 }
362
363 private boolean lookForPackageLocked(ArrayList<Alarm> alarmList, String packageName) {
364 for (int i=alarmList.size()-1; i>=0; i--) {
365 if (alarmList.get(i).operation.getTargetPackage().equals(packageName)) {
366 return true;
367 }
368 }
369 return false;
370 }
371
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800372 private ArrayList<Alarm> getAlarmList(int type) {
373 switch (type) {
374 case AlarmManager.RTC_WAKEUP: return mRtcWakeupAlarms;
375 case AlarmManager.RTC: return mRtcAlarms;
376 case AlarmManager.ELAPSED_REALTIME_WAKEUP: return mElapsedRealtimeWakeupAlarms;
377 case AlarmManager.ELAPSED_REALTIME: return mElapsedRealtimeAlarms;
378 }
379
380 return null;
381 }
382
383 private int addAlarmLocked(Alarm alarm) {
384 ArrayList<Alarm> alarmList = getAlarmList(alarm.type);
385
386 int index = Collections.binarySearch(alarmList, alarm, mIncreasingTimeOrder);
387 if (index < 0) {
388 index = 0 - index - 1;
389 }
Joe Onorato8a9b2202010-02-26 18:56:32 -0800390 if (localLOGV) Slog.v(TAG, "Adding alarm " + alarm + " at " + index);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800391 alarmList.add(index, alarm);
392
393 if (localLOGV) {
394 // Display the list of alarms for this alarm type
Joe Onorato8a9b2202010-02-26 18:56:32 -0800395 Slog.v(TAG, "alarms: " + alarmList.size() + " type: " + alarm.type);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800396 int position = 0;
397 for (Alarm a : alarmList) {
398 Time time = new Time();
399 time.set(a.when);
400 String timeStr = time.format("%b %d %I:%M:%S %p");
Joe Onorato8a9b2202010-02-26 18:56:32 -0800401 Slog.v(TAG, position + ": " + timeStr
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800402 + " " + a.operation.getTargetPackage());
403 position += 1;
404 }
405 }
406
407 return index;
408 }
409
410 public long timeToNextAlarm() {
Jeff Brown11c5f1a2010-03-31 15:29:40 -0700411 long nextAlarm = Long.MAX_VALUE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800412 synchronized (mLock) {
413 for (int i=AlarmManager.RTC_WAKEUP;
414 i<=AlarmManager.ELAPSED_REALTIME; i++) {
415 ArrayList<Alarm> alarmList = getAlarmList(i);
416 if (alarmList.size() > 0) {
417 Alarm a = alarmList.get(0);
418 if (a.when < nextAlarm) {
419 nextAlarm = a.when;
420 }
421 }
422 }
423 }
424 return nextAlarm;
425 }
426
427 private void setLocked(Alarm alarm)
428 {
429 if (mDescriptor != -1)
430 {
Jeff Brown11c5f1a2010-03-31 15:29:40 -0700431 // The kernel never triggers alarms with negative wakeup times
432 // so we ensure they are positive.
433 long alarmSeconds, alarmNanoseconds;
434 if (alarm.when < 0) {
435 alarmSeconds = 0;
436 alarmNanoseconds = 0;
437 } else {
438 alarmSeconds = alarm.when / 1000;
439 alarmNanoseconds = (alarm.when % 1000) * 1000 * 1000;
440 }
441
442 set(mDescriptor, alarm.type, alarmSeconds, alarmNanoseconds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800443 }
444 else
445 {
446 Message msg = Message.obtain();
447 msg.what = ALARM_EVENT;
448
449 mHandler.removeMessages(ALARM_EVENT);
450 mHandler.sendMessageAtTime(msg, alarm.when);
451 }
452 }
453
454 @Override
455 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
456 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
457 != PackageManager.PERMISSION_GRANTED) {
458 pw.println("Permission Denial: can't dump AlarmManager from from pid="
459 + Binder.getCallingPid()
460 + ", uid=" + Binder.getCallingUid());
461 return;
462 }
463
464 synchronized (mLock) {
465 pw.println("Current Alarm Manager state:");
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700466 if (mRtcWakeupAlarms.size() > 0 || mRtcAlarms.size() > 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800467 pw.println(" ");
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700468 pw.print(" Realtime wakeup (now=");
469 pw.print(System.currentTimeMillis()); pw.println("):");
470 if (mRtcWakeupAlarms.size() > 0) {
471 dumpAlarmList(pw, mRtcWakeupAlarms, " ", "RTC_WAKEUP");
472 }
473 if (mRtcAlarms.size() > 0) {
474 dumpAlarmList(pw, mRtcAlarms, " ", "RTC");
475 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800476 }
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700477 if (mElapsedRealtimeWakeupAlarms.size() > 0 || mElapsedRealtimeAlarms.size() > 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800478 pw.println(" ");
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700479 pw.print(" Elapsed realtime wakeup (now=");
480 pw.print(SystemClock.elapsedRealtime()); pw.println("):");
481 if (mElapsedRealtimeWakeupAlarms.size() > 0) {
482 dumpAlarmList(pw, mElapsedRealtimeWakeupAlarms, " ", "ELAPSED_WAKEUP");
483 }
484 if (mElapsedRealtimeAlarms.size() > 0) {
485 dumpAlarmList(pw, mElapsedRealtimeAlarms, " ", "ELAPSED");
486 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800487 }
488
489 pw.println(" ");
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700490 pw.print(" Broadcast ref count: "); pw.println(mBroadcastRefCount);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800491
492 pw.println(" ");
493 pw.println(" Alarm Stats:");
494 for (Map.Entry<String, BroadcastStats> be : mBroadcastStats.entrySet()) {
495 BroadcastStats bs = be.getValue();
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700496 pw.print(" "); pw.println(be.getKey());
497 pw.print(" "); pw.print(bs.aggregateTime);
498 pw.print("ms running, "); pw.print(bs.numWakeup);
499 pw.println(" wakeups");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800500 for (Map.Entry<Intent.FilterComparison, FilterStats> fe
501 : bs.filterStats.entrySet()) {
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700502 pw.print(" "); pw.print(fe.getValue().count);
503 pw.print(" alarms: ");
504 pw.println(fe.getKey().getIntent().toShortString(true, false));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800505 }
506 }
507 }
508 }
509
510 private static final void dumpAlarmList(PrintWriter pw, ArrayList<Alarm> list, String prefix, String label) {
511 for (int i=list.size()-1; i>=0; i--) {
512 Alarm a = list.get(i);
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700513 pw.print(prefix); pw.print(label); pw.print(" #"); pw.print(i);
514 pw.print(": "); pw.println(a);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800515 a.dump(pw, prefix + " ");
516 }
517 }
518
519 private native int init();
520 private native void close(int fd);
Jeff Brown11c5f1a2010-03-31 15:29:40 -0700521 private native void set(int fd, int type, long seconds, long nanoseconds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800522 private native int waitForAlarm(int fd);
523 private native int setKernelTimezone(int fd, int minuteswest);
524
525 private void triggerAlarmsLocked(ArrayList<Alarm> alarmList,
526 ArrayList<Alarm> triggerList,
527 long now)
528 {
529 Iterator<Alarm> it = alarmList.iterator();
530 ArrayList<Alarm> repeats = new ArrayList<Alarm>();
531
532 while (it.hasNext())
533 {
534 Alarm alarm = it.next();
535
Joe Onorato8a9b2202010-02-26 18:56:32 -0800536 if (localLOGV) Slog.v(TAG, "Checking active alarm when=" + alarm.when + " " + alarm);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800537
538 if (alarm.when > now) {
539 // don't fire alarms in the future
540 break;
541 }
542
543 // If the alarm is late, then print a warning message.
544 // Note that this can happen if the user creates a new event on
545 // the Calendar app with a reminder that is in the past. In that
546 // case, the reminder alarm will fire immediately.
547 if (localLOGV && now - alarm.when > LATE_ALARM_THRESHOLD) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800548 Slog.v(TAG, "alarm is late! alarm time: " + alarm.when
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800549 + " now: " + now + " delay (in seconds): "
550 + (now - alarm.when) / 1000);
551 }
552
553 // Recurring alarms may have passed several alarm intervals while the
554 // phone was asleep or off, so pass a trigger count when sending them.
Joe Onorato8a9b2202010-02-26 18:56:32 -0800555 if (localLOGV) Slog.v(TAG, "Alarm triggering: " + alarm);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800556 alarm.count = 1;
557 if (alarm.repeatInterval > 0) {
558 // this adjustment will be zero if we're late by
559 // less than one full repeat interval
560 alarm.count += (now - alarm.when) / alarm.repeatInterval;
561 }
562 triggerList.add(alarm);
563
564 // remove the alarm from the list
565 it.remove();
566
567 // if it repeats queue it up to be read-added to the list
568 if (alarm.repeatInterval > 0) {
569 repeats.add(alarm);
570 }
571 }
572
573 // reset any repeating alarms.
574 it = repeats.iterator();
575 while (it.hasNext()) {
576 Alarm alarm = it.next();
577 alarm.when += alarm.count * alarm.repeatInterval;
578 addAlarmLocked(alarm);
579 }
580
581 if (alarmList.size() > 0) {
582 setLocked(alarmList.get(0));
583 }
584 }
585
586 /**
587 * This Comparator sorts Alarms into increasing time order.
588 */
589 public static class IncreasingTimeOrder implements Comparator<Alarm> {
590 public int compare(Alarm a1, Alarm a2) {
591 long when1 = a1.when;
592 long when2 = a2.when;
593 if (when1 - when2 > 0) {
594 return 1;
595 }
596 if (when1 - when2 < 0) {
597 return -1;
598 }
599 return 0;
600 }
601 }
602
603 private static class Alarm {
604 public int type;
605 public int count;
606 public long when;
607 public long repeatInterval;
608 public PendingIntent operation;
609
610 public Alarm() {
611 when = 0;
612 repeatInterval = 0;
613 operation = null;
614 }
615
616 @Override
617 public String toString()
618 {
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700619 StringBuilder sb = new StringBuilder(128);
620 sb.append("Alarm{");
621 sb.append(Integer.toHexString(System.identityHashCode(this)));
622 sb.append(" type ");
623 sb.append(type);
624 sb.append(" ");
625 sb.append(operation.getTargetPackage());
626 sb.append('}');
627 return sb.toString();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800628 }
629
630 public void dump(PrintWriter pw, String prefix)
631 {
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700632 pw.print(prefix); pw.print("type="); pw.print(type);
633 pw.print(" when="); pw.print(when);
634 pw.print(" repeatInterval="); pw.print(repeatInterval);
635 pw.print(" count="); pw.println(count);
636 pw.print(prefix); pw.print("operation="); pw.println(operation);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800637 }
638 }
639
640 private class AlarmThread extends Thread
641 {
642 public AlarmThread()
643 {
644 super("AlarmManager");
645 }
646
647 public void run()
648 {
649 while (true)
650 {
651 int result = waitForAlarm(mDescriptor);
652
653 ArrayList<Alarm> triggerList = new ArrayList<Alarm>();
654
655 if ((result & TIME_CHANGED_MASK) != 0) {
656 remove(mTimeTickSender);
657 mClockReceiver.scheduleTimeTickEvent();
Dianne Hackborn1c633fc2009-12-08 19:45:14 -0800658 Intent intent = new Intent(Intent.ACTION_TIME_CHANGED);
659 intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
660 mContext.sendBroadcast(intent);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800661 }
662
663 synchronized (mLock) {
664 final long nowRTC = System.currentTimeMillis();
665 final long nowELAPSED = SystemClock.elapsedRealtime();
Joe Onorato8a9b2202010-02-26 18:56:32 -0800666 if (localLOGV) Slog.v(
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800667 TAG, "Checking for alarms... rtc=" + nowRTC
668 + ", elapsed=" + nowELAPSED);
669
670 if ((result & RTC_WAKEUP_MASK) != 0)
671 triggerAlarmsLocked(mRtcWakeupAlarms, triggerList, nowRTC);
672
673 if ((result & RTC_MASK) != 0)
674 triggerAlarmsLocked(mRtcAlarms, triggerList, nowRTC);
675
676 if ((result & ELAPSED_REALTIME_WAKEUP_MASK) != 0)
677 triggerAlarmsLocked(mElapsedRealtimeWakeupAlarms, triggerList, nowELAPSED);
678
679 if ((result & ELAPSED_REALTIME_MASK) != 0)
680 triggerAlarmsLocked(mElapsedRealtimeAlarms, triggerList, nowELAPSED);
681
682 // now trigger the alarms
683 Iterator<Alarm> it = triggerList.iterator();
684 while (it.hasNext()) {
685 Alarm alarm = it.next();
686 try {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800687 if (localLOGV) Slog.v(TAG, "sending alarm " + alarm);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800688 alarm.operation.send(mContext, 0,
689 mBackgroundIntent.putExtra(
690 Intent.EXTRA_ALARM_COUNT, alarm.count),
691 mResultReceiver, mHandler);
692
693 // we have an active broadcast so stay awake.
694 if (mBroadcastRefCount == 0) {
695 mWakeLock.acquire();
696 }
697 mBroadcastRefCount++;
698
699 BroadcastStats bs = getStatsLocked(alarm.operation);
700 if (bs.nesting == 0) {
701 bs.startTime = nowELAPSED;
702 } else {
703 bs.nesting++;
704 }
705 if (alarm.type == AlarmManager.ELAPSED_REALTIME_WAKEUP
706 || alarm.type == AlarmManager.RTC_WAKEUP) {
707 bs.numWakeup++;
708 ActivityManagerNative.noteWakeupAlarm(
709 alarm.operation);
710 }
711 } catch (PendingIntent.CanceledException e) {
712 if (alarm.repeatInterval > 0) {
713 // This IntentSender is no longer valid, but this
714 // is a repeating alarm, so toss the hoser.
715 remove(alarm.operation);
716 }
717 } catch (RuntimeException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800718 Slog.w(TAG, "Failure sending alarm.", e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800719 }
720 }
721 }
722 }
723 }
724 }
725
726 private class AlarmHandler extends Handler {
727 public static final int ALARM_EVENT = 1;
728 public static final int MINUTE_CHANGE_EVENT = 2;
729 public static final int DATE_CHANGE_EVENT = 3;
730
731 public AlarmHandler() {
732 }
733
734 public void handleMessage(Message msg) {
735 if (msg.what == ALARM_EVENT) {
736 ArrayList<Alarm> triggerList = new ArrayList<Alarm>();
737 synchronized (mLock) {
738 final long nowRTC = System.currentTimeMillis();
739 triggerAlarmsLocked(mRtcWakeupAlarms, triggerList, nowRTC);
740 triggerAlarmsLocked(mRtcAlarms, triggerList, nowRTC);
741 triggerAlarmsLocked(mElapsedRealtimeWakeupAlarms, triggerList, nowRTC);
742 triggerAlarmsLocked(mElapsedRealtimeAlarms, triggerList, nowRTC);
743 }
744
745 // now trigger the alarms without the lock held
746 Iterator<Alarm> it = triggerList.iterator();
747 while (it.hasNext())
748 {
749 Alarm alarm = it.next();
750 try {
751 alarm.operation.send();
752 } catch (PendingIntent.CanceledException e) {
753 if (alarm.repeatInterval > 0) {
754 // This IntentSender is no longer valid, but this
755 // is a repeating alarm, so toss the hoser.
756 remove(alarm.operation);
757 }
758 }
759 }
760 }
761 }
762 }
763
764 class ClockReceiver extends BroadcastReceiver {
765 public ClockReceiver() {
766 IntentFilter filter = new IntentFilter();
767 filter.addAction(Intent.ACTION_TIME_TICK);
768 filter.addAction(Intent.ACTION_DATE_CHANGED);
769 mContext.registerReceiver(this, filter);
770 }
771
772 @Override
773 public void onReceive(Context context, Intent intent) {
774 if (intent.getAction().equals(Intent.ACTION_TIME_TICK)) {
775 scheduleTimeTickEvent();
776 } else if (intent.getAction().equals(Intent.ACTION_DATE_CHANGED)) {
777 // Since the kernel does not keep track of DST, we need to
778 // reset the TZ information at the beginning of each day
779 // based off of the current Zone gmt offset + userspace tracked
780 // daylight savings information.
781 TimeZone zone = TimeZone.getTimeZone(SystemProperties.get(TIMEZONE_PROPERTY));
782 int gmtOffset = (zone.getRawOffset() + zone.getDSTSavings()) / 60000;
783
784 setKernelTimezone(mDescriptor, -(gmtOffset));
785 scheduleDateChangedEvent();
786 }
787 }
788
789 public void scheduleTimeTickEvent() {
790 Calendar calendar = Calendar.getInstance();
791 calendar.setTimeInMillis(System.currentTimeMillis());
792 calendar.add(Calendar.MINUTE, 1);
793 calendar.set(Calendar.SECOND, 0);
794 calendar.set(Calendar.MILLISECOND, 0);
795
796 set(AlarmManager.RTC, calendar.getTimeInMillis(), mTimeTickSender);
797 }
798
799 public void scheduleDateChangedEvent() {
800 Calendar calendar = Calendar.getInstance();
801 calendar.setTimeInMillis(System.currentTimeMillis());
802 calendar.set(Calendar.HOUR, 0);
803 calendar.set(Calendar.MINUTE, 0);
804 calendar.set(Calendar.SECOND, 0);
805 calendar.set(Calendar.MILLISECOND, 0);
806 calendar.add(Calendar.DAY_OF_MONTH, 1);
807
808 set(AlarmManager.RTC, calendar.getTimeInMillis(), mDateChangeSender);
809 }
810 }
811
812 class UninstallReceiver extends BroadcastReceiver {
813 public UninstallReceiver() {
814 IntentFilter filter = new IntentFilter();
815 filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
816 filter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800817 filter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800818 filter.addDataScheme("package");
819 mContext.registerReceiver(this, filter);
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800820 // Register for events related to sdcard installation.
821 IntentFilter sdFilter = new IntentFilter();
Suchi Amalapurapub56ae202010-02-04 22:51:07 -0800822 sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800823 mContext.registerReceiver(this, sdFilter);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800824 }
825
826 @Override
827 public void onReceive(Context context, Intent intent) {
828 synchronized (mLock) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800829 String action = intent.getAction();
830 String pkgList[] = null;
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800831 if (Intent.ACTION_QUERY_PACKAGE_RESTART.equals(action)) {
832 pkgList = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);
833 for (String packageName : pkgList) {
834 if (lookForPackageLocked(packageName)) {
835 setResultCode(Activity.RESULT_OK);
836 return;
837 }
838 }
839 return;
840 } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800841 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
842 } else {
Dianne Hackborn409578f2010-03-10 17:23:43 -0800843 if (Intent.ACTION_PACKAGE_REMOVED.equals(action)
844 && intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
845 // This package is being updated; don't kill its alarms.
846 return;
847 }
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800848 Uri data = intent.getData();
849 if (data != null) {
850 String pkg = data.getSchemeSpecificPart();
851 if (pkg != null) {
852 pkgList = new String[]{pkg};
853 }
854 }
855 }
856 if (pkgList != null && (pkgList.length > 0)) {
857 for (String pkg : pkgList) {
858 removeLocked(pkg);
859 mBroadcastStats.remove(pkg);
860 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800861 }
862 }
863 }
864 }
865
866 private final BroadcastStats getStatsLocked(PendingIntent pi) {
867 String pkg = pi.getTargetPackage();
868 BroadcastStats bs = mBroadcastStats.get(pkg);
869 if (bs == null) {
870 bs = new BroadcastStats();
871 mBroadcastStats.put(pkg, bs);
872 }
873 return bs;
874 }
875
876 class ResultReceiver implements PendingIntent.OnFinished {
877 public void onSendFinished(PendingIntent pi, Intent intent, int resultCode,
878 String resultData, Bundle resultExtras) {
879 synchronized (mLock) {
880 BroadcastStats bs = getStatsLocked(pi);
881 if (bs != null) {
882 bs.nesting--;
883 if (bs.nesting <= 0) {
884 bs.nesting = 0;
885 bs.aggregateTime += SystemClock.elapsedRealtime()
886 - bs.startTime;
887 Intent.FilterComparison fc = new Intent.FilterComparison(intent);
888 FilterStats fs = bs.filterStats.get(fc);
889 if (fs == null) {
890 fs = new FilterStats();
891 bs.filterStats.put(fc, fs);
892 }
893 fs.count++;
894 }
895 }
896 mBroadcastRefCount--;
897 if (mBroadcastRefCount == 0) {
898 mWakeLock.release();
899 }
900 }
901 }
902 }
903}