blob: 5a36417ba9db9b9ed13143d93d24451f3cd162ec [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;
Dianne Hackborn043fcd92010-10-06 14:27:34 -070041import android.util.TimeUtils;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080042
43import java.io.FileDescriptor;
44import java.io.PrintWriter;
Dianne Hackborn043fcd92010-10-06 14:27:34 -070045import java.text.SimpleDateFormat;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080046import java.util.ArrayList;
47import java.util.Calendar;
48import java.util.Collections;
49import java.util.Comparator;
Mike Lockwood1f7b4132009-11-20 15:12:51 -050050import java.util.Date;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080051import java.util.HashMap;
52import java.util.Iterator;
53import java.util.Map;
54import java.util.TimeZone;
55
56class AlarmManagerService extends IAlarmManager.Stub {
57 // The threshold for how long an alarm can be late before we print a
58 // warning message. The time duration is in milliseconds.
59 private static final long LATE_ALARM_THRESHOLD = 10 * 1000;
60
61 private static final int RTC_WAKEUP_MASK = 1 << AlarmManager.RTC_WAKEUP;
62 private static final int RTC_MASK = 1 << AlarmManager.RTC;
63 private static final int ELAPSED_REALTIME_WAKEUP_MASK = 1 << AlarmManager.ELAPSED_REALTIME_WAKEUP;
64 private static final int ELAPSED_REALTIME_MASK = 1 << AlarmManager.ELAPSED_REALTIME;
65 private static final int TIME_CHANGED_MASK = 1 << 16;
66
67 private static final String TAG = "AlarmManager";
68 private static final String ClockReceiver_TAG = "ClockReceiver";
69 private static final boolean localLOGV = false;
70 private static final int ALARM_EVENT = 1;
71 private static final String TIMEZONE_PROPERTY = "persist.sys.timezone";
72
73 private static final Intent mBackgroundIntent
74 = new Intent().addFlags(Intent.FLAG_FROM_BACKGROUND);
75
76 private final Context mContext;
77
78 private Object mLock = new Object();
79
80 private final ArrayList<Alarm> mRtcWakeupAlarms = new ArrayList<Alarm>();
81 private final ArrayList<Alarm> mRtcAlarms = new ArrayList<Alarm>();
82 private final ArrayList<Alarm> mElapsedRealtimeWakeupAlarms = new ArrayList<Alarm>();
83 private final ArrayList<Alarm> mElapsedRealtimeAlarms = new ArrayList<Alarm>();
84 private final IncreasingTimeOrder mIncreasingTimeOrder = new IncreasingTimeOrder();
85
86 // slots corresponding with the inexact-repeat interval buckets,
87 // ordered from shortest to longest
88 private static final long sInexactSlotIntervals[] = {
89 AlarmManager.INTERVAL_FIFTEEN_MINUTES,
90 AlarmManager.INTERVAL_HALF_HOUR,
91 AlarmManager.INTERVAL_HOUR,
92 AlarmManager.INTERVAL_HALF_DAY,
93 AlarmManager.INTERVAL_DAY
94 };
95 private long mInexactDeliveryTimes[] = { 0, 0, 0, 0, 0};
96
97 private int mDescriptor;
98 private int mBroadcastRefCount = 0;
99 private PowerManager.WakeLock mWakeLock;
100 private final AlarmThread mWaitThread = new AlarmThread();
101 private final AlarmHandler mHandler = new AlarmHandler();
102 private ClockReceiver mClockReceiver;
103 private UninstallReceiver mUninstallReceiver;
104 private final ResultReceiver mResultReceiver = new ResultReceiver();
105 private final PendingIntent mTimeTickSender;
106 private final PendingIntent mDateChangeSender;
107
108 private static final class FilterStats {
109 int count;
110 }
111
112 private static final class BroadcastStats {
113 long aggregateTime;
114 int numWakeup;
115 long startTime;
116 int nesting;
117 HashMap<Intent.FilterComparison, FilterStats> filterStats
118 = new HashMap<Intent.FilterComparison, FilterStats>();
119 }
120
121 private final HashMap<String, BroadcastStats> mBroadcastStats
122 = new HashMap<String, BroadcastStats>();
123
124 public AlarmManagerService(Context context) {
125 mContext = context;
126 mDescriptor = init();
Robert CH Chou64ba8e42009-11-04 21:38:49 +0800127
128 // We have to set current TimeZone info to kernel
129 // because kernel doesn't keep this after reboot
130 String tz = SystemProperties.get(TIMEZONE_PROPERTY);
131 if (tz != null) {
132 setTimeZone(tz);
133 }
134
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800135 PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
136 mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
137
138 mTimeTickSender = PendingIntent.getBroadcast(context, 0,
139 new Intent(Intent.ACTION_TIME_TICK).addFlags(
140 Intent.FLAG_RECEIVER_REGISTERED_ONLY), 0);
Dianne Hackborn1c633fc2009-12-08 19:45:14 -0800141 Intent intent = new Intent(Intent.ACTION_DATE_CHANGED);
142 intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
143 mDateChangeSender = PendingIntent.getBroadcast(context, 0, intent, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800144
145 // now that we have initied the driver schedule the alarm
146 mClockReceiver= new ClockReceiver();
147 mClockReceiver.scheduleTimeTickEvent();
148 mClockReceiver.scheduleDateChangedEvent();
149 mUninstallReceiver = new UninstallReceiver();
150
151 if (mDescriptor != -1) {
152 mWaitThread.start();
153 } else {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800154 Slog.w(TAG, "Failed to open alarm driver. Falling back to a handler.");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800155 }
156 }
157
158 protected void finalize() throws Throwable {
159 try {
160 close(mDescriptor);
161 } finally {
162 super.finalize();
163 }
164 }
165
166 public void set(int type, long triggerAtTime, PendingIntent operation) {
167 setRepeating(type, triggerAtTime, 0, operation);
168 }
169
170 public void setRepeating(int type, long triggerAtTime, long interval,
171 PendingIntent operation) {
172 if (operation == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800173 Slog.w(TAG, "set/setRepeating ignored because there is no intent");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800174 return;
175 }
176 synchronized (mLock) {
177 Alarm alarm = new Alarm();
178 alarm.type = type;
179 alarm.when = triggerAtTime;
180 alarm.repeatInterval = interval;
181 alarm.operation = operation;
182
183 // Remove this alarm if already scheduled.
184 removeLocked(operation);
185
Joe Onorato8a9b2202010-02-26 18:56:32 -0800186 if (localLOGV) Slog.v(TAG, "set: " + alarm);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800187
188 int index = addAlarmLocked(alarm);
189 if (index == 0) {
190 setLocked(alarm);
191 }
192 }
193 }
194
195 public void setInexactRepeating(int type, long triggerAtTime, long interval,
196 PendingIntent operation) {
197 if (operation == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800198 Slog.w(TAG, "setInexactRepeating ignored because there is no intent");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800199 return;
200 }
201
202 // find the slot in the delivery-times array that we will use
203 int intervalSlot;
204 for (intervalSlot = 0; intervalSlot < sInexactSlotIntervals.length; intervalSlot++) {
205 if (sInexactSlotIntervals[intervalSlot] == interval) {
206 break;
207 }
208 }
209
210 // Non-bucket intervals just fall back to the less-efficient
211 // unbucketed recurring alarm implementation
212 if (intervalSlot >= sInexactSlotIntervals.length) {
213 setRepeating(type, triggerAtTime, interval, operation);
214 return;
215 }
216
217 // Align bucketed alarm deliveries by trying to match
218 // the shortest-interval bucket already scheduled
219 long bucketTime = 0;
220 for (int slot = 0; slot < mInexactDeliveryTimes.length; slot++) {
221 if (mInexactDeliveryTimes[slot] > 0) {
222 bucketTime = mInexactDeliveryTimes[slot];
223 break;
224 }
225 }
226
227 if (bucketTime == 0) {
228 // If nothing is scheduled yet, just start at the requested time
229 bucketTime = triggerAtTime;
230 } else {
231 // Align the new alarm with the existing bucketed sequence. To achieve
232 // alignment, we slide the start time around by min{interval, slot interval}
233 long adjustment = (interval <= sInexactSlotIntervals[intervalSlot])
234 ? interval : sInexactSlotIntervals[intervalSlot];
235
236 // The bucket may have started in the past; adjust
237 while (bucketTime < triggerAtTime) {
238 bucketTime += adjustment;
239 }
240
241 // Or the bucket may be set to start more than an interval beyond
242 // our requested trigger time; pull it back to meet our needs
243 while (bucketTime > triggerAtTime + adjustment) {
244 bucketTime -= adjustment;
245 }
246 }
247
248 // Remember where this bucket started (reducing the amount of later
249 // fixup required) and set the alarm with the new, bucketed start time.
Joe Onorato8a9b2202010-02-26 18:56:32 -0800250 if (localLOGV) Slog.v(TAG, "setInexactRepeating: interval=" + interval
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800251 + " bucketTime=" + bucketTime);
252 mInexactDeliveryTimes[intervalSlot] = bucketTime;
253 setRepeating(type, bucketTime, interval, operation);
254 }
255
Dan Egnor97e44942010-02-04 20:27:47 -0800256 public void setTime(long millis) {
257 mContext.enforceCallingOrSelfPermission(
258 "android.permission.SET_TIME",
259 "setTime");
260
261 SystemClock.setCurrentTimeMillis(millis);
262 }
263
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800264 public void setTimeZone(String tz) {
265 mContext.enforceCallingOrSelfPermission(
266 "android.permission.SET_TIME_ZONE",
267 "setTimeZone");
268
269 if (TextUtils.isEmpty(tz)) return;
270 TimeZone zone = TimeZone.getTimeZone(tz);
271 // Prevent reentrant calls from stepping on each other when writing
272 // the time zone property
273 boolean timeZoneWasChanged = false;
274 synchronized (this) {
275 String current = SystemProperties.get(TIMEZONE_PROPERTY);
276 if (current == null || !current.equals(zone.getID())) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800277 if (localLOGV) Slog.v(TAG, "timezone changed: " + current + ", new=" + zone.getID());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800278 timeZoneWasChanged = true;
279 SystemProperties.set(TIMEZONE_PROPERTY, zone.getID());
280 }
281
282 // Update the kernel timezone information
283 // Kernel tracks time offsets as 'minutes west of GMT'
Mike Lockwood1f7b4132009-11-20 15:12:51 -0500284 int gmtOffset = zone.getRawOffset();
285 if (zone.inDaylightTime(new Date(System.currentTimeMillis()))) {
286 gmtOffset += zone.getDSTSavings();
287 }
288 setKernelTimezone(mDescriptor, -(gmtOffset / 60000));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800289 }
290
291 TimeZone.setDefault(null);
292
293 if (timeZoneWasChanged) {
294 Intent intent = new Intent(Intent.ACTION_TIMEZONE_CHANGED);
Dianne Hackborn1c633fc2009-12-08 19:45:14 -0800295 intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800296 intent.putExtra("time-zone", zone.getID());
297 mContext.sendBroadcast(intent);
298 }
299 }
300
301 public void remove(PendingIntent operation) {
302 if (operation == null) {
303 return;
304 }
305 synchronized (mLock) {
306 removeLocked(operation);
307 }
308 }
309
310 public void removeLocked(PendingIntent operation) {
311 removeLocked(mRtcWakeupAlarms, operation);
312 removeLocked(mRtcAlarms, operation);
313 removeLocked(mElapsedRealtimeWakeupAlarms, operation);
314 removeLocked(mElapsedRealtimeAlarms, operation);
315 }
316
317 private void removeLocked(ArrayList<Alarm> alarmList,
318 PendingIntent operation) {
319 if (alarmList.size() <= 0) {
320 return;
321 }
322
323 // iterator over the list removing any it where the intent match
324 Iterator<Alarm> it = alarmList.iterator();
325
326 while (it.hasNext()) {
327 Alarm alarm = it.next();
328 if (alarm.operation.equals(operation)) {
329 it.remove();
330 }
331 }
332 }
333
334 public void removeLocked(String packageName) {
335 removeLocked(mRtcWakeupAlarms, packageName);
336 removeLocked(mRtcAlarms, packageName);
337 removeLocked(mElapsedRealtimeWakeupAlarms, packageName);
338 removeLocked(mElapsedRealtimeAlarms, packageName);
339 }
340
341 private void removeLocked(ArrayList<Alarm> alarmList,
342 String packageName) {
343 if (alarmList.size() <= 0) {
344 return;
345 }
346
347 // iterator over the list removing any it where the intent match
348 Iterator<Alarm> it = alarmList.iterator();
349
350 while (it.hasNext()) {
351 Alarm alarm = it.next();
352 if (alarm.operation.getTargetPackage().equals(packageName)) {
353 it.remove();
354 }
355 }
356 }
357
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800358 public boolean lookForPackageLocked(String packageName) {
359 return lookForPackageLocked(mRtcWakeupAlarms, packageName)
360 || lookForPackageLocked(mRtcAlarms, packageName)
361 || lookForPackageLocked(mElapsedRealtimeWakeupAlarms, packageName)
362 || lookForPackageLocked(mElapsedRealtimeAlarms, packageName);
363 }
364
365 private boolean lookForPackageLocked(ArrayList<Alarm> alarmList, String packageName) {
366 for (int i=alarmList.size()-1; i>=0; i--) {
367 if (alarmList.get(i).operation.getTargetPackage().equals(packageName)) {
368 return true;
369 }
370 }
371 return false;
372 }
373
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800374 private ArrayList<Alarm> getAlarmList(int type) {
375 switch (type) {
376 case AlarmManager.RTC_WAKEUP: return mRtcWakeupAlarms;
377 case AlarmManager.RTC: return mRtcAlarms;
378 case AlarmManager.ELAPSED_REALTIME_WAKEUP: return mElapsedRealtimeWakeupAlarms;
379 case AlarmManager.ELAPSED_REALTIME: return mElapsedRealtimeAlarms;
380 }
381
382 return null;
383 }
384
385 private int addAlarmLocked(Alarm alarm) {
386 ArrayList<Alarm> alarmList = getAlarmList(alarm.type);
387
388 int index = Collections.binarySearch(alarmList, alarm, mIncreasingTimeOrder);
389 if (index < 0) {
390 index = 0 - index - 1;
391 }
Joe Onorato8a9b2202010-02-26 18:56:32 -0800392 if (localLOGV) Slog.v(TAG, "Adding alarm " + alarm + " at " + index);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800393 alarmList.add(index, alarm);
394
395 if (localLOGV) {
396 // Display the list of alarms for this alarm type
Joe Onorato8a9b2202010-02-26 18:56:32 -0800397 Slog.v(TAG, "alarms: " + alarmList.size() + " type: " + alarm.type);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800398 int position = 0;
399 for (Alarm a : alarmList) {
400 Time time = new Time();
401 time.set(a.when);
402 String timeStr = time.format("%b %d %I:%M:%S %p");
Joe Onorato8a9b2202010-02-26 18:56:32 -0800403 Slog.v(TAG, position + ": " + timeStr
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800404 + " " + a.operation.getTargetPackage());
405 position += 1;
406 }
407 }
408
409 return index;
410 }
411
412 public long timeToNextAlarm() {
Jeff Brown11c5f1a2010-03-31 15:29:40 -0700413 long nextAlarm = Long.MAX_VALUE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800414 synchronized (mLock) {
415 for (int i=AlarmManager.RTC_WAKEUP;
416 i<=AlarmManager.ELAPSED_REALTIME; i++) {
417 ArrayList<Alarm> alarmList = getAlarmList(i);
418 if (alarmList.size() > 0) {
419 Alarm a = alarmList.get(0);
420 if (a.when < nextAlarm) {
421 nextAlarm = a.when;
422 }
423 }
424 }
425 }
426 return nextAlarm;
427 }
428
429 private void setLocked(Alarm alarm)
430 {
431 if (mDescriptor != -1)
432 {
Jeff Brown11c5f1a2010-03-31 15:29:40 -0700433 // The kernel never triggers alarms with negative wakeup times
434 // so we ensure they are positive.
435 long alarmSeconds, alarmNanoseconds;
436 if (alarm.when < 0) {
437 alarmSeconds = 0;
438 alarmNanoseconds = 0;
439 } else {
440 alarmSeconds = alarm.when / 1000;
441 alarmNanoseconds = (alarm.when % 1000) * 1000 * 1000;
442 }
443
444 set(mDescriptor, alarm.type, alarmSeconds, alarmNanoseconds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800445 }
446 else
447 {
448 Message msg = Message.obtain();
449 msg.what = ALARM_EVENT;
450
451 mHandler.removeMessages(ALARM_EVENT);
452 mHandler.sendMessageAtTime(msg, alarm.when);
453 }
454 }
455
456 @Override
457 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
458 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
459 != PackageManager.PERMISSION_GRANTED) {
460 pw.println("Permission Denial: can't dump AlarmManager from from pid="
461 + Binder.getCallingPid()
462 + ", uid=" + Binder.getCallingUid());
463 return;
464 }
465
466 synchronized (mLock) {
467 pw.println("Current Alarm Manager state:");
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700468 if (mRtcWakeupAlarms.size() > 0 || mRtcAlarms.size() > 0) {
Dianne Hackborn043fcd92010-10-06 14:27:34 -0700469 final long now = System.currentTimeMillis();
470 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800471 pw.println(" ");
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700472 pw.print(" Realtime wakeup (now=");
Dianne Hackborn043fcd92010-10-06 14:27:34 -0700473 pw.print(sdf.format(new Date(now))); pw.println("):");
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700474 if (mRtcWakeupAlarms.size() > 0) {
Dianne Hackborn043fcd92010-10-06 14:27:34 -0700475 dumpAlarmList(pw, mRtcWakeupAlarms, " ", "RTC_WAKEUP", now);
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700476 }
477 if (mRtcAlarms.size() > 0) {
Dianne Hackborn043fcd92010-10-06 14:27:34 -0700478 dumpAlarmList(pw, mRtcAlarms, " ", "RTC", now);
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700479 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800480 }
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700481 if (mElapsedRealtimeWakeupAlarms.size() > 0 || mElapsedRealtimeAlarms.size() > 0) {
Dianne Hackborn043fcd92010-10-06 14:27:34 -0700482 final long now = SystemClock.elapsedRealtime();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800483 pw.println(" ");
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700484 pw.print(" Elapsed realtime wakeup (now=");
Dianne Hackborn043fcd92010-10-06 14:27:34 -0700485 TimeUtils.formatDuration(now, pw); pw.println("):");
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700486 if (mElapsedRealtimeWakeupAlarms.size() > 0) {
Dianne Hackborn043fcd92010-10-06 14:27:34 -0700487 dumpAlarmList(pw, mElapsedRealtimeWakeupAlarms, " ", "ELAPSED_WAKEUP", now);
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700488 }
489 if (mElapsedRealtimeAlarms.size() > 0) {
Dianne Hackborn043fcd92010-10-06 14:27:34 -0700490 dumpAlarmList(pw, mElapsedRealtimeAlarms, " ", "ELAPSED", now);
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700491 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800492 }
493
494 pw.println(" ");
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700495 pw.print(" Broadcast ref count: "); pw.println(mBroadcastRefCount);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800496
497 pw.println(" ");
498 pw.println(" Alarm Stats:");
499 for (Map.Entry<String, BroadcastStats> be : mBroadcastStats.entrySet()) {
500 BroadcastStats bs = be.getValue();
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700501 pw.print(" "); pw.println(be.getKey());
502 pw.print(" "); pw.print(bs.aggregateTime);
503 pw.print("ms running, "); pw.print(bs.numWakeup);
504 pw.println(" wakeups");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800505 for (Map.Entry<Intent.FilterComparison, FilterStats> fe
506 : bs.filterStats.entrySet()) {
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700507 pw.print(" "); pw.print(fe.getValue().count);
508 pw.print(" alarms: ");
509 pw.println(fe.getKey().getIntent().toShortString(true, false));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800510 }
511 }
512 }
513 }
514
Dianne Hackborn043fcd92010-10-06 14:27:34 -0700515 private static final void dumpAlarmList(PrintWriter pw, ArrayList<Alarm> list,
516 String prefix, String label, long now) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800517 for (int i=list.size()-1; i>=0; i--) {
518 Alarm a = list.get(i);
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700519 pw.print(prefix); pw.print(label); pw.print(" #"); pw.print(i);
520 pw.print(": "); pw.println(a);
Dianne Hackborn043fcd92010-10-06 14:27:34 -0700521 a.dump(pw, prefix + " ", now);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800522 }
523 }
524
525 private native int init();
526 private native void close(int fd);
Jeff Brown11c5f1a2010-03-31 15:29:40 -0700527 private native void set(int fd, int type, long seconds, long nanoseconds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800528 private native int waitForAlarm(int fd);
529 private native int setKernelTimezone(int fd, int minuteswest);
530
531 private void triggerAlarmsLocked(ArrayList<Alarm> alarmList,
532 ArrayList<Alarm> triggerList,
533 long now)
534 {
535 Iterator<Alarm> it = alarmList.iterator();
536 ArrayList<Alarm> repeats = new ArrayList<Alarm>();
537
538 while (it.hasNext())
539 {
540 Alarm alarm = it.next();
541
Joe Onorato8a9b2202010-02-26 18:56:32 -0800542 if (localLOGV) Slog.v(TAG, "Checking active alarm when=" + alarm.when + " " + alarm);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800543
544 if (alarm.when > now) {
545 // don't fire alarms in the future
546 break;
547 }
548
549 // If the alarm is late, then print a warning message.
550 // Note that this can happen if the user creates a new event on
551 // the Calendar app with a reminder that is in the past. In that
552 // case, the reminder alarm will fire immediately.
553 if (localLOGV && now - alarm.when > LATE_ALARM_THRESHOLD) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800554 Slog.v(TAG, "alarm is late! alarm time: " + alarm.when
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800555 + " now: " + now + " delay (in seconds): "
556 + (now - alarm.when) / 1000);
557 }
558
559 // Recurring alarms may have passed several alarm intervals while the
560 // phone was asleep or off, so pass a trigger count when sending them.
Joe Onorato8a9b2202010-02-26 18:56:32 -0800561 if (localLOGV) Slog.v(TAG, "Alarm triggering: " + alarm);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800562 alarm.count = 1;
563 if (alarm.repeatInterval > 0) {
564 // this adjustment will be zero if we're late by
565 // less than one full repeat interval
566 alarm.count += (now - alarm.when) / alarm.repeatInterval;
567 }
568 triggerList.add(alarm);
569
570 // remove the alarm from the list
571 it.remove();
572
573 // if it repeats queue it up to be read-added to the list
574 if (alarm.repeatInterval > 0) {
575 repeats.add(alarm);
576 }
577 }
578
579 // reset any repeating alarms.
580 it = repeats.iterator();
581 while (it.hasNext()) {
582 Alarm alarm = it.next();
583 alarm.when += alarm.count * alarm.repeatInterval;
584 addAlarmLocked(alarm);
585 }
586
587 if (alarmList.size() > 0) {
588 setLocked(alarmList.get(0));
589 }
590 }
591
592 /**
593 * This Comparator sorts Alarms into increasing time order.
594 */
595 public static class IncreasingTimeOrder implements Comparator<Alarm> {
596 public int compare(Alarm a1, Alarm a2) {
597 long when1 = a1.when;
598 long when2 = a2.when;
599 if (when1 - when2 > 0) {
600 return 1;
601 }
602 if (when1 - when2 < 0) {
603 return -1;
604 }
605 return 0;
606 }
607 }
608
609 private static class Alarm {
610 public int type;
611 public int count;
612 public long when;
613 public long repeatInterval;
614 public PendingIntent operation;
615
616 public Alarm() {
617 when = 0;
618 repeatInterval = 0;
619 operation = null;
620 }
621
622 @Override
623 public String toString()
624 {
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700625 StringBuilder sb = new StringBuilder(128);
626 sb.append("Alarm{");
627 sb.append(Integer.toHexString(System.identityHashCode(this)));
628 sb.append(" type ");
629 sb.append(type);
630 sb.append(" ");
631 sb.append(operation.getTargetPackage());
632 sb.append('}');
633 return sb.toString();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800634 }
635
Dianne Hackborn043fcd92010-10-06 14:27:34 -0700636 public void dump(PrintWriter pw, String prefix, long now) {
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700637 pw.print(prefix); pw.print("type="); pw.print(type);
Dianne Hackborn043fcd92010-10-06 14:27:34 -0700638 pw.print(" when="); TimeUtils.formatDuration(when, now, pw);
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700639 pw.print(" repeatInterval="); pw.print(repeatInterval);
640 pw.print(" count="); pw.println(count);
641 pw.print(prefix); pw.print("operation="); pw.println(operation);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800642 }
643 }
644
645 private class AlarmThread extends Thread
646 {
647 public AlarmThread()
648 {
649 super("AlarmManager");
650 }
651
652 public void run()
653 {
654 while (true)
655 {
656 int result = waitForAlarm(mDescriptor);
657
658 ArrayList<Alarm> triggerList = new ArrayList<Alarm>();
659
660 if ((result & TIME_CHANGED_MASK) != 0) {
661 remove(mTimeTickSender);
662 mClockReceiver.scheduleTimeTickEvent();
Dianne Hackborn1c633fc2009-12-08 19:45:14 -0800663 Intent intent = new Intent(Intent.ACTION_TIME_CHANGED);
Dianne Hackborn89ba6752011-01-23 16:51:16 -0800664 intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING
665 | Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
Dianne Hackborn1c633fc2009-12-08 19:45:14 -0800666 mContext.sendBroadcast(intent);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800667 }
668
669 synchronized (mLock) {
670 final long nowRTC = System.currentTimeMillis();
671 final long nowELAPSED = SystemClock.elapsedRealtime();
Joe Onorato8a9b2202010-02-26 18:56:32 -0800672 if (localLOGV) Slog.v(
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800673 TAG, "Checking for alarms... rtc=" + nowRTC
674 + ", elapsed=" + nowELAPSED);
675
676 if ((result & RTC_WAKEUP_MASK) != 0)
677 triggerAlarmsLocked(mRtcWakeupAlarms, triggerList, nowRTC);
678
679 if ((result & RTC_MASK) != 0)
680 triggerAlarmsLocked(mRtcAlarms, triggerList, nowRTC);
681
682 if ((result & ELAPSED_REALTIME_WAKEUP_MASK) != 0)
683 triggerAlarmsLocked(mElapsedRealtimeWakeupAlarms, triggerList, nowELAPSED);
684
685 if ((result & ELAPSED_REALTIME_MASK) != 0)
686 triggerAlarmsLocked(mElapsedRealtimeAlarms, triggerList, nowELAPSED);
687
688 // now trigger the alarms
689 Iterator<Alarm> it = triggerList.iterator();
690 while (it.hasNext()) {
691 Alarm alarm = it.next();
692 try {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800693 if (localLOGV) Slog.v(TAG, "sending alarm " + alarm);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800694 alarm.operation.send(mContext, 0,
695 mBackgroundIntent.putExtra(
696 Intent.EXTRA_ALARM_COUNT, alarm.count),
697 mResultReceiver, mHandler);
698
699 // we have an active broadcast so stay awake.
700 if (mBroadcastRefCount == 0) {
701 mWakeLock.acquire();
702 }
703 mBroadcastRefCount++;
704
705 BroadcastStats bs = getStatsLocked(alarm.operation);
706 if (bs.nesting == 0) {
707 bs.startTime = nowELAPSED;
708 } else {
709 bs.nesting++;
710 }
711 if (alarm.type == AlarmManager.ELAPSED_REALTIME_WAKEUP
712 || alarm.type == AlarmManager.RTC_WAKEUP) {
713 bs.numWakeup++;
714 ActivityManagerNative.noteWakeupAlarm(
715 alarm.operation);
716 }
717 } catch (PendingIntent.CanceledException e) {
718 if (alarm.repeatInterval > 0) {
719 // This IntentSender is no longer valid, but this
720 // is a repeating alarm, so toss the hoser.
721 remove(alarm.operation);
722 }
723 } catch (RuntimeException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800724 Slog.w(TAG, "Failure sending alarm.", e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800725 }
726 }
727 }
728 }
729 }
730 }
731
732 private class AlarmHandler extends Handler {
733 public static final int ALARM_EVENT = 1;
734 public static final int MINUTE_CHANGE_EVENT = 2;
735 public static final int DATE_CHANGE_EVENT = 3;
736
737 public AlarmHandler() {
738 }
739
740 public void handleMessage(Message msg) {
741 if (msg.what == ALARM_EVENT) {
742 ArrayList<Alarm> triggerList = new ArrayList<Alarm>();
743 synchronized (mLock) {
744 final long nowRTC = System.currentTimeMillis();
745 triggerAlarmsLocked(mRtcWakeupAlarms, triggerList, nowRTC);
746 triggerAlarmsLocked(mRtcAlarms, triggerList, nowRTC);
747 triggerAlarmsLocked(mElapsedRealtimeWakeupAlarms, triggerList, nowRTC);
748 triggerAlarmsLocked(mElapsedRealtimeAlarms, triggerList, nowRTC);
749 }
750
751 // now trigger the alarms without the lock held
752 Iterator<Alarm> it = triggerList.iterator();
753 while (it.hasNext())
754 {
755 Alarm alarm = it.next();
756 try {
757 alarm.operation.send();
758 } catch (PendingIntent.CanceledException e) {
759 if (alarm.repeatInterval > 0) {
760 // This IntentSender is no longer valid, but this
761 // is a repeating alarm, so toss the hoser.
762 remove(alarm.operation);
763 }
764 }
765 }
766 }
767 }
768 }
769
770 class ClockReceiver extends BroadcastReceiver {
771 public ClockReceiver() {
772 IntentFilter filter = new IntentFilter();
773 filter.addAction(Intent.ACTION_TIME_TICK);
774 filter.addAction(Intent.ACTION_DATE_CHANGED);
775 mContext.registerReceiver(this, filter);
776 }
777
778 @Override
779 public void onReceive(Context context, Intent intent) {
780 if (intent.getAction().equals(Intent.ACTION_TIME_TICK)) {
781 scheduleTimeTickEvent();
782 } else if (intent.getAction().equals(Intent.ACTION_DATE_CHANGED)) {
783 // Since the kernel does not keep track of DST, we need to
784 // reset the TZ information at the beginning of each day
785 // based off of the current Zone gmt offset + userspace tracked
786 // daylight savings information.
787 TimeZone zone = TimeZone.getTimeZone(SystemProperties.get(TIMEZONE_PROPERTY));
788 int gmtOffset = (zone.getRawOffset() + zone.getDSTSavings()) / 60000;
789
790 setKernelTimezone(mDescriptor, -(gmtOffset));
791 scheduleDateChangedEvent();
792 }
793 }
794
795 public void scheduleTimeTickEvent() {
796 Calendar calendar = Calendar.getInstance();
797 calendar.setTimeInMillis(System.currentTimeMillis());
798 calendar.add(Calendar.MINUTE, 1);
799 calendar.set(Calendar.SECOND, 0);
800 calendar.set(Calendar.MILLISECOND, 0);
801
802 set(AlarmManager.RTC, calendar.getTimeInMillis(), mTimeTickSender);
803 }
804
805 public void scheduleDateChangedEvent() {
806 Calendar calendar = Calendar.getInstance();
807 calendar.setTimeInMillis(System.currentTimeMillis());
808 calendar.set(Calendar.HOUR, 0);
809 calendar.set(Calendar.MINUTE, 0);
810 calendar.set(Calendar.SECOND, 0);
811 calendar.set(Calendar.MILLISECOND, 0);
812 calendar.add(Calendar.DAY_OF_MONTH, 1);
813
814 set(AlarmManager.RTC, calendar.getTimeInMillis(), mDateChangeSender);
815 }
816 }
817
818 class UninstallReceiver extends BroadcastReceiver {
819 public UninstallReceiver() {
820 IntentFilter filter = new IntentFilter();
821 filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
822 filter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800823 filter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800824 filter.addDataScheme("package");
825 mContext.registerReceiver(this, filter);
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800826 // Register for events related to sdcard installation.
827 IntentFilter sdFilter = new IntentFilter();
Suchi Amalapurapub56ae202010-02-04 22:51:07 -0800828 sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800829 mContext.registerReceiver(this, sdFilter);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800830 }
831
832 @Override
833 public void onReceive(Context context, Intent intent) {
834 synchronized (mLock) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800835 String action = intent.getAction();
836 String pkgList[] = null;
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800837 if (Intent.ACTION_QUERY_PACKAGE_RESTART.equals(action)) {
838 pkgList = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);
839 for (String packageName : pkgList) {
840 if (lookForPackageLocked(packageName)) {
841 setResultCode(Activity.RESULT_OK);
842 return;
843 }
844 }
845 return;
846 } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800847 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
848 } else {
Dianne Hackborn409578f2010-03-10 17:23:43 -0800849 if (Intent.ACTION_PACKAGE_REMOVED.equals(action)
850 && intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
851 // This package is being updated; don't kill its alarms.
852 return;
853 }
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800854 Uri data = intent.getData();
855 if (data != null) {
856 String pkg = data.getSchemeSpecificPart();
857 if (pkg != null) {
858 pkgList = new String[]{pkg};
859 }
860 }
861 }
862 if (pkgList != null && (pkgList.length > 0)) {
863 for (String pkg : pkgList) {
864 removeLocked(pkg);
865 mBroadcastStats.remove(pkg);
866 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800867 }
868 }
869 }
870 }
871
872 private final BroadcastStats getStatsLocked(PendingIntent pi) {
873 String pkg = pi.getTargetPackage();
874 BroadcastStats bs = mBroadcastStats.get(pkg);
875 if (bs == null) {
876 bs = new BroadcastStats();
877 mBroadcastStats.put(pkg, bs);
878 }
879 return bs;
880 }
881
882 class ResultReceiver implements PendingIntent.OnFinished {
883 public void onSendFinished(PendingIntent pi, Intent intent, int resultCode,
884 String resultData, Bundle resultExtras) {
885 synchronized (mLock) {
886 BroadcastStats bs = getStatsLocked(pi);
887 if (bs != null) {
888 bs.nesting--;
889 if (bs.nesting <= 0) {
890 bs.nesting = 0;
891 bs.aggregateTime += SystemClock.elapsedRealtime()
892 - bs.startTime;
893 Intent.FilterComparison fc = new Intent.FilterComparison(intent);
894 FilterStats fs = bs.filterStats.get(fc);
895 if (fs == null) {
896 fs = new FilterStats();
897 bs.filterStats.put(fc, fs);
898 }
899 fs.count++;
900 }
901 }
902 mBroadcastRefCount--;
903 if (mBroadcastRefCount == 0) {
904 mWakeLock.release();
905 }
906 }
907 }
908 }
909}