blob: 4e2f1e3a0a88c58a436542cb3acf52b41aae6532 [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();
127 PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
128 mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
129
130 mTimeTickSender = PendingIntent.getBroadcast(context, 0,
131 new Intent(Intent.ACTION_TIME_TICK).addFlags(
132 Intent.FLAG_RECEIVER_REGISTERED_ONLY), 0);
Dianne Hackborn1c633fc2009-12-08 19:45:14 -0800133 Intent intent = new Intent(Intent.ACTION_DATE_CHANGED);
134 intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
135 mDateChangeSender = PendingIntent.getBroadcast(context, 0, intent, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800136
137 // now that we have initied the driver schedule the alarm
138 mClockReceiver= new ClockReceiver();
139 mClockReceiver.scheduleTimeTickEvent();
140 mClockReceiver.scheduleDateChangedEvent();
141 mUninstallReceiver = new UninstallReceiver();
142
143 if (mDescriptor != -1) {
144 mWaitThread.start();
145 } else {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800146 Slog.w(TAG, "Failed to open alarm driver. Falling back to a handler.");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800147 }
148 }
149
150 protected void finalize() throws Throwable {
151 try {
152 close(mDescriptor);
153 } finally {
154 super.finalize();
155 }
156 }
157
158 public void set(int type, long triggerAtTime, PendingIntent operation) {
159 setRepeating(type, triggerAtTime, 0, operation);
160 }
161
162 public void setRepeating(int type, long triggerAtTime, long interval,
163 PendingIntent operation) {
164 if (operation == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800165 Slog.w(TAG, "set/setRepeating ignored because there is no intent");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800166 return;
167 }
168 synchronized (mLock) {
169 Alarm alarm = new Alarm();
170 alarm.type = type;
171 alarm.when = triggerAtTime;
172 alarm.repeatInterval = interval;
173 alarm.operation = operation;
174
175 // Remove this alarm if already scheduled.
176 removeLocked(operation);
177
Joe Onorato8a9b2202010-02-26 18:56:32 -0800178 if (localLOGV) Slog.v(TAG, "set: " + alarm);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800179
180 int index = addAlarmLocked(alarm);
181 if (index == 0) {
182 setLocked(alarm);
183 }
184 }
185 }
186
187 public void setInexactRepeating(int type, long triggerAtTime, long interval,
188 PendingIntent operation) {
189 if (operation == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800190 Slog.w(TAG, "setInexactRepeating ignored because there is no intent");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800191 return;
192 }
193
194 // find the slot in the delivery-times array that we will use
195 int intervalSlot;
196 for (intervalSlot = 0; intervalSlot < sInexactSlotIntervals.length; intervalSlot++) {
197 if (sInexactSlotIntervals[intervalSlot] == interval) {
198 break;
199 }
200 }
201
202 // Non-bucket intervals just fall back to the less-efficient
203 // unbucketed recurring alarm implementation
204 if (intervalSlot >= sInexactSlotIntervals.length) {
205 setRepeating(type, triggerAtTime, interval, operation);
206 return;
207 }
208
209 // Align bucketed alarm deliveries by trying to match
210 // the shortest-interval bucket already scheduled
211 long bucketTime = 0;
212 for (int slot = 0; slot < mInexactDeliveryTimes.length; slot++) {
213 if (mInexactDeliveryTimes[slot] > 0) {
214 bucketTime = mInexactDeliveryTimes[slot];
215 break;
216 }
217 }
218
219 if (bucketTime == 0) {
220 // If nothing is scheduled yet, just start at the requested time
221 bucketTime = triggerAtTime;
222 } else {
223 // Align the new alarm with the existing bucketed sequence. To achieve
224 // alignment, we slide the start time around by min{interval, slot interval}
225 long adjustment = (interval <= sInexactSlotIntervals[intervalSlot])
226 ? interval : sInexactSlotIntervals[intervalSlot];
227
228 // The bucket may have started in the past; adjust
229 while (bucketTime < triggerAtTime) {
230 bucketTime += adjustment;
231 }
232
233 // Or the bucket may be set to start more than an interval beyond
234 // our requested trigger time; pull it back to meet our needs
235 while (bucketTime > triggerAtTime + adjustment) {
236 bucketTime -= adjustment;
237 }
238 }
239
240 // Remember where this bucket started (reducing the amount of later
241 // fixup required) and set the alarm with the new, bucketed start time.
Joe Onorato8a9b2202010-02-26 18:56:32 -0800242 if (localLOGV) Slog.v(TAG, "setInexactRepeating: interval=" + interval
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800243 + " bucketTime=" + bucketTime);
244 mInexactDeliveryTimes[intervalSlot] = bucketTime;
245 setRepeating(type, bucketTime, interval, operation);
246 }
247
Dan Egnor97e44942010-02-04 20:27:47 -0800248 public void setTime(long millis) {
249 mContext.enforceCallingOrSelfPermission(
250 "android.permission.SET_TIME",
251 "setTime");
252
253 SystemClock.setCurrentTimeMillis(millis);
254 }
255
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800256 public void setTimeZone(String tz) {
257 mContext.enforceCallingOrSelfPermission(
258 "android.permission.SET_TIME_ZONE",
259 "setTimeZone");
260
261 if (TextUtils.isEmpty(tz)) return;
262 TimeZone zone = TimeZone.getTimeZone(tz);
263 // Prevent reentrant calls from stepping on each other when writing
264 // the time zone property
265 boolean timeZoneWasChanged = false;
266 synchronized (this) {
267 String current = SystemProperties.get(TIMEZONE_PROPERTY);
268 if (current == null || !current.equals(zone.getID())) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800269 if (localLOGV) Slog.v(TAG, "timezone changed: " + current + ", new=" + zone.getID());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800270 timeZoneWasChanged = true;
271 SystemProperties.set(TIMEZONE_PROPERTY, zone.getID());
272 }
273
274 // Update the kernel timezone information
275 // Kernel tracks time offsets as 'minutes west of GMT'
Mike Lockwood1f7b4132009-11-20 15:12:51 -0500276 int gmtOffset = zone.getRawOffset();
277 if (zone.inDaylightTime(new Date(System.currentTimeMillis()))) {
278 gmtOffset += zone.getDSTSavings();
279 }
280 setKernelTimezone(mDescriptor, -(gmtOffset / 60000));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800281 }
282
283 TimeZone.setDefault(null);
284
285 if (timeZoneWasChanged) {
286 Intent intent = new Intent(Intent.ACTION_TIMEZONE_CHANGED);
Dianne Hackborn1c633fc2009-12-08 19:45:14 -0800287 intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800288 intent.putExtra("time-zone", zone.getID());
289 mContext.sendBroadcast(intent);
290 }
291 }
292
293 public void remove(PendingIntent operation) {
294 if (operation == null) {
295 return;
296 }
297 synchronized (mLock) {
298 removeLocked(operation);
299 }
300 }
301
302 public void removeLocked(PendingIntent operation) {
303 removeLocked(mRtcWakeupAlarms, operation);
304 removeLocked(mRtcAlarms, operation);
305 removeLocked(mElapsedRealtimeWakeupAlarms, operation);
306 removeLocked(mElapsedRealtimeAlarms, operation);
307 }
308
309 private void removeLocked(ArrayList<Alarm> alarmList,
310 PendingIntent operation) {
311 if (alarmList.size() <= 0) {
312 return;
313 }
314
315 // iterator over the list removing any it where the intent match
316 Iterator<Alarm> it = alarmList.iterator();
317
318 while (it.hasNext()) {
319 Alarm alarm = it.next();
320 if (alarm.operation.equals(operation)) {
321 it.remove();
322 }
323 }
324 }
325
326 public void removeLocked(String packageName) {
327 removeLocked(mRtcWakeupAlarms, packageName);
328 removeLocked(mRtcAlarms, packageName);
329 removeLocked(mElapsedRealtimeWakeupAlarms, packageName);
330 removeLocked(mElapsedRealtimeAlarms, packageName);
331 }
332
333 private void removeLocked(ArrayList<Alarm> alarmList,
334 String packageName) {
335 if (alarmList.size() <= 0) {
336 return;
337 }
338
339 // iterator over the list removing any it where the intent match
340 Iterator<Alarm> it = alarmList.iterator();
341
342 while (it.hasNext()) {
343 Alarm alarm = it.next();
344 if (alarm.operation.getTargetPackage().equals(packageName)) {
345 it.remove();
346 }
347 }
348 }
349
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800350 public boolean lookForPackageLocked(String packageName) {
351 return lookForPackageLocked(mRtcWakeupAlarms, packageName)
352 || lookForPackageLocked(mRtcAlarms, packageName)
353 || lookForPackageLocked(mElapsedRealtimeWakeupAlarms, packageName)
354 || lookForPackageLocked(mElapsedRealtimeAlarms, packageName);
355 }
356
357 private boolean lookForPackageLocked(ArrayList<Alarm> alarmList, String packageName) {
358 for (int i=alarmList.size()-1; i>=0; i--) {
359 if (alarmList.get(i).operation.getTargetPackage().equals(packageName)) {
360 return true;
361 }
362 }
363 return false;
364 }
365
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800366 private ArrayList<Alarm> getAlarmList(int type) {
367 switch (type) {
368 case AlarmManager.RTC_WAKEUP: return mRtcWakeupAlarms;
369 case AlarmManager.RTC: return mRtcAlarms;
370 case AlarmManager.ELAPSED_REALTIME_WAKEUP: return mElapsedRealtimeWakeupAlarms;
371 case AlarmManager.ELAPSED_REALTIME: return mElapsedRealtimeAlarms;
372 }
373
374 return null;
375 }
376
377 private int addAlarmLocked(Alarm alarm) {
378 ArrayList<Alarm> alarmList = getAlarmList(alarm.type);
379
380 int index = Collections.binarySearch(alarmList, alarm, mIncreasingTimeOrder);
381 if (index < 0) {
382 index = 0 - index - 1;
383 }
Joe Onorato8a9b2202010-02-26 18:56:32 -0800384 if (localLOGV) Slog.v(TAG, "Adding alarm " + alarm + " at " + index);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800385 alarmList.add(index, alarm);
386
387 if (localLOGV) {
388 // Display the list of alarms for this alarm type
Joe Onorato8a9b2202010-02-26 18:56:32 -0800389 Slog.v(TAG, "alarms: " + alarmList.size() + " type: " + alarm.type);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800390 int position = 0;
391 for (Alarm a : alarmList) {
392 Time time = new Time();
393 time.set(a.when);
394 String timeStr = time.format("%b %d %I:%M:%S %p");
Joe Onorato8a9b2202010-02-26 18:56:32 -0800395 Slog.v(TAG, position + ": " + timeStr
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800396 + " " + a.operation.getTargetPackage());
397 position += 1;
398 }
399 }
400
401 return index;
402 }
403
404 public long timeToNextAlarm() {
Jeff Brown11c5f1a2010-03-31 15:29:40 -0700405 long nextAlarm = Long.MAX_VALUE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800406 synchronized (mLock) {
407 for (int i=AlarmManager.RTC_WAKEUP;
408 i<=AlarmManager.ELAPSED_REALTIME; i++) {
409 ArrayList<Alarm> alarmList = getAlarmList(i);
410 if (alarmList.size() > 0) {
411 Alarm a = alarmList.get(0);
412 if (a.when < nextAlarm) {
413 nextAlarm = a.when;
414 }
415 }
416 }
417 }
418 return nextAlarm;
419 }
420
421 private void setLocked(Alarm alarm)
422 {
423 if (mDescriptor != -1)
424 {
Jeff Brown11c5f1a2010-03-31 15:29:40 -0700425 // The kernel never triggers alarms with negative wakeup times
426 // so we ensure they are positive.
427 long alarmSeconds, alarmNanoseconds;
428 if (alarm.when < 0) {
429 alarmSeconds = 0;
430 alarmNanoseconds = 0;
431 } else {
432 alarmSeconds = alarm.when / 1000;
433 alarmNanoseconds = (alarm.when % 1000) * 1000 * 1000;
434 }
435
436 set(mDescriptor, alarm.type, alarmSeconds, alarmNanoseconds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800437 }
438 else
439 {
440 Message msg = Message.obtain();
441 msg.what = ALARM_EVENT;
442
443 mHandler.removeMessages(ALARM_EVENT);
444 mHandler.sendMessageAtTime(msg, alarm.when);
445 }
446 }
447
448 @Override
449 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
450 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
451 != PackageManager.PERMISSION_GRANTED) {
452 pw.println("Permission Denial: can't dump AlarmManager from from pid="
453 + Binder.getCallingPid()
454 + ", uid=" + Binder.getCallingUid());
455 return;
456 }
457
458 synchronized (mLock) {
459 pw.println("Current Alarm Manager state:");
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700460 if (mRtcWakeupAlarms.size() > 0 || mRtcAlarms.size() > 0) {
Dianne Hackborn043fcd92010-10-06 14:27:34 -0700461 final long now = System.currentTimeMillis();
462 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800463 pw.println(" ");
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700464 pw.print(" Realtime wakeup (now=");
Dianne Hackborn043fcd92010-10-06 14:27:34 -0700465 pw.print(sdf.format(new Date(now))); pw.println("):");
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700466 if (mRtcWakeupAlarms.size() > 0) {
Dianne Hackborn043fcd92010-10-06 14:27:34 -0700467 dumpAlarmList(pw, mRtcWakeupAlarms, " ", "RTC_WAKEUP", now);
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700468 }
469 if (mRtcAlarms.size() > 0) {
Dianne Hackborn043fcd92010-10-06 14:27:34 -0700470 dumpAlarmList(pw, mRtcAlarms, " ", "RTC", now);
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700471 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800472 }
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700473 if (mElapsedRealtimeWakeupAlarms.size() > 0 || mElapsedRealtimeAlarms.size() > 0) {
Dianne Hackborn043fcd92010-10-06 14:27:34 -0700474 final long now = SystemClock.elapsedRealtime();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800475 pw.println(" ");
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700476 pw.print(" Elapsed realtime wakeup (now=");
Dianne Hackborn043fcd92010-10-06 14:27:34 -0700477 TimeUtils.formatDuration(now, pw); pw.println("):");
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700478 if (mElapsedRealtimeWakeupAlarms.size() > 0) {
Dianne Hackborn043fcd92010-10-06 14:27:34 -0700479 dumpAlarmList(pw, mElapsedRealtimeWakeupAlarms, " ", "ELAPSED_WAKEUP", now);
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700480 }
481 if (mElapsedRealtimeAlarms.size() > 0) {
Dianne Hackborn043fcd92010-10-06 14:27:34 -0700482 dumpAlarmList(pw, mElapsedRealtimeAlarms, " ", "ELAPSED", now);
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700483 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800484 }
485
486 pw.println(" ");
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700487 pw.print(" Broadcast ref count: "); pw.println(mBroadcastRefCount);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800488
489 pw.println(" ");
490 pw.println(" Alarm Stats:");
491 for (Map.Entry<String, BroadcastStats> be : mBroadcastStats.entrySet()) {
492 BroadcastStats bs = be.getValue();
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700493 pw.print(" "); pw.println(be.getKey());
494 pw.print(" "); pw.print(bs.aggregateTime);
495 pw.print("ms running, "); pw.print(bs.numWakeup);
496 pw.println(" wakeups");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800497 for (Map.Entry<Intent.FilterComparison, FilterStats> fe
498 : bs.filterStats.entrySet()) {
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700499 pw.print(" "); pw.print(fe.getValue().count);
500 pw.print(" alarms: ");
501 pw.println(fe.getKey().getIntent().toShortString(true, false));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800502 }
503 }
504 }
505 }
506
Dianne Hackborn043fcd92010-10-06 14:27:34 -0700507 private static final void dumpAlarmList(PrintWriter pw, ArrayList<Alarm> list,
508 String prefix, String label, long now) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800509 for (int i=list.size()-1; i>=0; i--) {
510 Alarm a = list.get(i);
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700511 pw.print(prefix); pw.print(label); pw.print(" #"); pw.print(i);
512 pw.print(": "); pw.println(a);
Dianne Hackborn043fcd92010-10-06 14:27:34 -0700513 a.dump(pw, prefix + " ", now);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800514 }
515 }
516
517 private native int init();
518 private native void close(int fd);
Jeff Brown11c5f1a2010-03-31 15:29:40 -0700519 private native void set(int fd, int type, long seconds, long nanoseconds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800520 private native int waitForAlarm(int fd);
521 private native int setKernelTimezone(int fd, int minuteswest);
522
523 private void triggerAlarmsLocked(ArrayList<Alarm> alarmList,
524 ArrayList<Alarm> triggerList,
525 long now)
526 {
527 Iterator<Alarm> it = alarmList.iterator();
528 ArrayList<Alarm> repeats = new ArrayList<Alarm>();
529
530 while (it.hasNext())
531 {
532 Alarm alarm = it.next();
533
Joe Onorato8a9b2202010-02-26 18:56:32 -0800534 if (localLOGV) Slog.v(TAG, "Checking active alarm when=" + alarm.when + " " + alarm);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800535
536 if (alarm.when > now) {
537 // don't fire alarms in the future
538 break;
539 }
540
541 // If the alarm is late, then print a warning message.
542 // Note that this can happen if the user creates a new event on
543 // the Calendar app with a reminder that is in the past. In that
544 // case, the reminder alarm will fire immediately.
545 if (localLOGV && now - alarm.when > LATE_ALARM_THRESHOLD) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800546 Slog.v(TAG, "alarm is late! alarm time: " + alarm.when
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800547 + " now: " + now + " delay (in seconds): "
548 + (now - alarm.when) / 1000);
549 }
550
551 // Recurring alarms may have passed several alarm intervals while the
552 // phone was asleep or off, so pass a trigger count when sending them.
Joe Onorato8a9b2202010-02-26 18:56:32 -0800553 if (localLOGV) Slog.v(TAG, "Alarm triggering: " + alarm);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800554 alarm.count = 1;
555 if (alarm.repeatInterval > 0) {
556 // this adjustment will be zero if we're late by
557 // less than one full repeat interval
558 alarm.count += (now - alarm.when) / alarm.repeatInterval;
559 }
560 triggerList.add(alarm);
561
562 // remove the alarm from the list
563 it.remove();
564
565 // if it repeats queue it up to be read-added to the list
566 if (alarm.repeatInterval > 0) {
567 repeats.add(alarm);
568 }
569 }
570
571 // reset any repeating alarms.
572 it = repeats.iterator();
573 while (it.hasNext()) {
574 Alarm alarm = it.next();
575 alarm.when += alarm.count * alarm.repeatInterval;
576 addAlarmLocked(alarm);
577 }
578
579 if (alarmList.size() > 0) {
580 setLocked(alarmList.get(0));
581 }
582 }
583
584 /**
585 * This Comparator sorts Alarms into increasing time order.
586 */
587 public static class IncreasingTimeOrder implements Comparator<Alarm> {
588 public int compare(Alarm a1, Alarm a2) {
589 long when1 = a1.when;
590 long when2 = a2.when;
591 if (when1 - when2 > 0) {
592 return 1;
593 }
594 if (when1 - when2 < 0) {
595 return -1;
596 }
597 return 0;
598 }
599 }
600
601 private static class Alarm {
602 public int type;
603 public int count;
604 public long when;
605 public long repeatInterval;
606 public PendingIntent operation;
607
608 public Alarm() {
609 when = 0;
610 repeatInterval = 0;
611 operation = null;
612 }
613
614 @Override
615 public String toString()
616 {
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700617 StringBuilder sb = new StringBuilder(128);
618 sb.append("Alarm{");
619 sb.append(Integer.toHexString(System.identityHashCode(this)));
620 sb.append(" type ");
621 sb.append(type);
622 sb.append(" ");
623 sb.append(operation.getTargetPackage());
624 sb.append('}');
625 return sb.toString();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800626 }
627
Dianne Hackborn043fcd92010-10-06 14:27:34 -0700628 public void dump(PrintWriter pw, String prefix, long now) {
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700629 pw.print(prefix); pw.print("type="); pw.print(type);
Dianne Hackborn043fcd92010-10-06 14:27:34 -0700630 pw.print(" when="); TimeUtils.formatDuration(when, now, pw);
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700631 pw.print(" repeatInterval="); pw.print(repeatInterval);
632 pw.print(" count="); pw.println(count);
633 pw.print(prefix); pw.print("operation="); pw.println(operation);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800634 }
635 }
636
637 private class AlarmThread extends Thread
638 {
639 public AlarmThread()
640 {
641 super("AlarmManager");
642 }
643
644 public void run()
645 {
646 while (true)
647 {
648 int result = waitForAlarm(mDescriptor);
649
650 ArrayList<Alarm> triggerList = new ArrayList<Alarm>();
651
652 if ((result & TIME_CHANGED_MASK) != 0) {
653 remove(mTimeTickSender);
654 mClockReceiver.scheduleTimeTickEvent();
Dianne Hackborn1c633fc2009-12-08 19:45:14 -0800655 Intent intent = new Intent(Intent.ACTION_TIME_CHANGED);
656 intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
657 mContext.sendBroadcast(intent);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800658 }
659
660 synchronized (mLock) {
661 final long nowRTC = System.currentTimeMillis();
662 final long nowELAPSED = SystemClock.elapsedRealtime();
Joe Onorato8a9b2202010-02-26 18:56:32 -0800663 if (localLOGV) Slog.v(
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800664 TAG, "Checking for alarms... rtc=" + nowRTC
665 + ", elapsed=" + nowELAPSED);
666
667 if ((result & RTC_WAKEUP_MASK) != 0)
668 triggerAlarmsLocked(mRtcWakeupAlarms, triggerList, nowRTC);
669
670 if ((result & RTC_MASK) != 0)
671 triggerAlarmsLocked(mRtcAlarms, triggerList, nowRTC);
672
673 if ((result & ELAPSED_REALTIME_WAKEUP_MASK) != 0)
674 triggerAlarmsLocked(mElapsedRealtimeWakeupAlarms, triggerList, nowELAPSED);
675
676 if ((result & ELAPSED_REALTIME_MASK) != 0)
677 triggerAlarmsLocked(mElapsedRealtimeAlarms, triggerList, nowELAPSED);
678
679 // now trigger the alarms
680 Iterator<Alarm> it = triggerList.iterator();
681 while (it.hasNext()) {
682 Alarm alarm = it.next();
683 try {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800684 if (localLOGV) Slog.v(TAG, "sending alarm " + alarm);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800685 alarm.operation.send(mContext, 0,
686 mBackgroundIntent.putExtra(
687 Intent.EXTRA_ALARM_COUNT, alarm.count),
688 mResultReceiver, mHandler);
689
690 // we have an active broadcast so stay awake.
691 if (mBroadcastRefCount == 0) {
692 mWakeLock.acquire();
693 }
694 mBroadcastRefCount++;
695
696 BroadcastStats bs = getStatsLocked(alarm.operation);
697 if (bs.nesting == 0) {
698 bs.startTime = nowELAPSED;
699 } else {
700 bs.nesting++;
701 }
702 if (alarm.type == AlarmManager.ELAPSED_REALTIME_WAKEUP
703 || alarm.type == AlarmManager.RTC_WAKEUP) {
704 bs.numWakeup++;
705 ActivityManagerNative.noteWakeupAlarm(
706 alarm.operation);
707 }
708 } catch (PendingIntent.CanceledException e) {
709 if (alarm.repeatInterval > 0) {
710 // This IntentSender is no longer valid, but this
711 // is a repeating alarm, so toss the hoser.
712 remove(alarm.operation);
713 }
714 } catch (RuntimeException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800715 Slog.w(TAG, "Failure sending alarm.", e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800716 }
717 }
718 }
719 }
720 }
721 }
722
723 private class AlarmHandler extends Handler {
724 public static final int ALARM_EVENT = 1;
725 public static final int MINUTE_CHANGE_EVENT = 2;
726 public static final int DATE_CHANGE_EVENT = 3;
727
728 public AlarmHandler() {
729 }
730
731 public void handleMessage(Message msg) {
732 if (msg.what == ALARM_EVENT) {
733 ArrayList<Alarm> triggerList = new ArrayList<Alarm>();
734 synchronized (mLock) {
735 final long nowRTC = System.currentTimeMillis();
736 triggerAlarmsLocked(mRtcWakeupAlarms, triggerList, nowRTC);
737 triggerAlarmsLocked(mRtcAlarms, triggerList, nowRTC);
738 triggerAlarmsLocked(mElapsedRealtimeWakeupAlarms, triggerList, nowRTC);
739 triggerAlarmsLocked(mElapsedRealtimeAlarms, triggerList, nowRTC);
740 }
741
742 // now trigger the alarms without the lock held
743 Iterator<Alarm> it = triggerList.iterator();
744 while (it.hasNext())
745 {
746 Alarm alarm = it.next();
747 try {
748 alarm.operation.send();
749 } catch (PendingIntent.CanceledException e) {
750 if (alarm.repeatInterval > 0) {
751 // This IntentSender is no longer valid, but this
752 // is a repeating alarm, so toss the hoser.
753 remove(alarm.operation);
754 }
755 }
756 }
757 }
758 }
759 }
760
761 class ClockReceiver extends BroadcastReceiver {
762 public ClockReceiver() {
763 IntentFilter filter = new IntentFilter();
764 filter.addAction(Intent.ACTION_TIME_TICK);
765 filter.addAction(Intent.ACTION_DATE_CHANGED);
766 mContext.registerReceiver(this, filter);
767 }
768
769 @Override
770 public void onReceive(Context context, Intent intent) {
771 if (intent.getAction().equals(Intent.ACTION_TIME_TICK)) {
772 scheduleTimeTickEvent();
773 } else if (intent.getAction().equals(Intent.ACTION_DATE_CHANGED)) {
774 // Since the kernel does not keep track of DST, we need to
775 // reset the TZ information at the beginning of each day
776 // based off of the current Zone gmt offset + userspace tracked
777 // daylight savings information.
778 TimeZone zone = TimeZone.getTimeZone(SystemProperties.get(TIMEZONE_PROPERTY));
779 int gmtOffset = (zone.getRawOffset() + zone.getDSTSavings()) / 60000;
780
781 setKernelTimezone(mDescriptor, -(gmtOffset));
782 scheduleDateChangedEvent();
783 }
784 }
785
786 public void scheduleTimeTickEvent() {
787 Calendar calendar = Calendar.getInstance();
788 calendar.setTimeInMillis(System.currentTimeMillis());
789 calendar.add(Calendar.MINUTE, 1);
790 calendar.set(Calendar.SECOND, 0);
791 calendar.set(Calendar.MILLISECOND, 0);
792
793 set(AlarmManager.RTC, calendar.getTimeInMillis(), mTimeTickSender);
794 }
795
796 public void scheduleDateChangedEvent() {
797 Calendar calendar = Calendar.getInstance();
798 calendar.setTimeInMillis(System.currentTimeMillis());
799 calendar.set(Calendar.HOUR, 0);
800 calendar.set(Calendar.MINUTE, 0);
801 calendar.set(Calendar.SECOND, 0);
802 calendar.set(Calendar.MILLISECOND, 0);
803 calendar.add(Calendar.DAY_OF_MONTH, 1);
804
805 set(AlarmManager.RTC, calendar.getTimeInMillis(), mDateChangeSender);
806 }
807 }
808
809 class UninstallReceiver extends BroadcastReceiver {
810 public UninstallReceiver() {
811 IntentFilter filter = new IntentFilter();
812 filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
813 filter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800814 filter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800815 filter.addDataScheme("package");
816 mContext.registerReceiver(this, filter);
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800817 // Register for events related to sdcard installation.
818 IntentFilter sdFilter = new IntentFilter();
Suchi Amalapurapub56ae202010-02-04 22:51:07 -0800819 sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800820 mContext.registerReceiver(this, sdFilter);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800821 }
822
823 @Override
824 public void onReceive(Context context, Intent intent) {
825 synchronized (mLock) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800826 String action = intent.getAction();
827 String pkgList[] = null;
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800828 if (Intent.ACTION_QUERY_PACKAGE_RESTART.equals(action)) {
829 pkgList = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);
830 for (String packageName : pkgList) {
831 if (lookForPackageLocked(packageName)) {
832 setResultCode(Activity.RESULT_OK);
833 return;
834 }
835 }
836 return;
837 } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800838 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
839 } else {
Dianne Hackborn409578f2010-03-10 17:23:43 -0800840 if (Intent.ACTION_PACKAGE_REMOVED.equals(action)
841 && intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
842 // This package is being updated; don't kill its alarms.
843 return;
844 }
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800845 Uri data = intent.getData();
846 if (data != null) {
847 String pkg = data.getSchemeSpecificPart();
848 if (pkg != null) {
849 pkgList = new String[]{pkg};
850 }
851 }
852 }
853 if (pkgList != null && (pkgList.length > 0)) {
854 for (String pkg : pkgList) {
855 removeLocked(pkg);
856 mBroadcastStats.remove(pkg);
857 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800858 }
859 }
860 }
861 }
862
863 private final BroadcastStats getStatsLocked(PendingIntent pi) {
864 String pkg = pi.getTargetPackage();
865 BroadcastStats bs = mBroadcastStats.get(pkg);
866 if (bs == null) {
867 bs = new BroadcastStats();
868 mBroadcastStats.put(pkg, bs);
869 }
870 return bs;
871 }
872
873 class ResultReceiver implements PendingIntent.OnFinished {
874 public void onSendFinished(PendingIntent pi, Intent intent, int resultCode,
875 String resultData, Bundle resultExtras) {
876 synchronized (mLock) {
877 BroadcastStats bs = getStatsLocked(pi);
878 if (bs != null) {
879 bs.nesting--;
880 if (bs.nesting <= 0) {
881 bs.nesting = 0;
882 bs.aggregateTime += SystemClock.elapsedRealtime()
883 - bs.startTime;
884 Intent.FilterComparison fc = new Intent.FilterComparison(intent);
885 FilterStats fs = bs.filterStats.get(fc);
886 if (fs == null) {
887 fs = new FilterStats();
888 bs.filterStats.put(fc, fs);
889 }
890 fs.count++;
891 }
892 }
893 mBroadcastRefCount--;
894 if (mBroadcastRefCount == 0) {
895 mWakeLock.release();
896 }
897 }
898 }
899 }
900}