blob: 057440572384bc078689e10512f6b42b5fdb651e [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;
Christopher Tateb8849c12011-02-08 13:39:01 -080066
67 // Alignment quantum for inexact repeating alarms
68 private static final long QUANTUM = AlarmManager.INTERVAL_FIFTEEN_MINUTES;
69
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080070 private static final String TAG = "AlarmManager";
71 private static final String ClockReceiver_TAG = "ClockReceiver";
72 private static final boolean localLOGV = false;
73 private static final int ALARM_EVENT = 1;
74 private static final String TIMEZONE_PROPERTY = "persist.sys.timezone";
75
76 private static final Intent mBackgroundIntent
77 = new Intent().addFlags(Intent.FLAG_FROM_BACKGROUND);
78
79 private final Context mContext;
80
81 private Object mLock = new Object();
82
83 private final ArrayList<Alarm> mRtcWakeupAlarms = new ArrayList<Alarm>();
84 private final ArrayList<Alarm> mRtcAlarms = new ArrayList<Alarm>();
85 private final ArrayList<Alarm> mElapsedRealtimeWakeupAlarms = new ArrayList<Alarm>();
86 private final ArrayList<Alarm> mElapsedRealtimeAlarms = new ArrayList<Alarm>();
87 private final IncreasingTimeOrder mIncreasingTimeOrder = new IncreasingTimeOrder();
88
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080089 private int mDescriptor;
90 private int mBroadcastRefCount = 0;
91 private PowerManager.WakeLock mWakeLock;
92 private final AlarmThread mWaitThread = new AlarmThread();
93 private final AlarmHandler mHandler = new AlarmHandler();
94 private ClockReceiver mClockReceiver;
95 private UninstallReceiver mUninstallReceiver;
96 private final ResultReceiver mResultReceiver = new ResultReceiver();
97 private final PendingIntent mTimeTickSender;
98 private final PendingIntent mDateChangeSender;
99
100 private static final class FilterStats {
101 int count;
102 }
103
104 private static final class BroadcastStats {
105 long aggregateTime;
106 int numWakeup;
107 long startTime;
108 int nesting;
109 HashMap<Intent.FilterComparison, FilterStats> filterStats
110 = new HashMap<Intent.FilterComparison, FilterStats>();
111 }
112
113 private final HashMap<String, BroadcastStats> mBroadcastStats
114 = new HashMap<String, BroadcastStats>();
115
116 public AlarmManagerService(Context context) {
117 mContext = context;
118 mDescriptor = init();
Robert CH Chou64ba8e42009-11-04 21:38:49 +0800119
120 // We have to set current TimeZone info to kernel
121 // because kernel doesn't keep this after reboot
122 String tz = SystemProperties.get(TIMEZONE_PROPERTY);
123 if (tz != null) {
124 setTimeZone(tz);
125 }
126
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800127 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
Christopher Tateb8849c12011-02-08 13:39:01 -0800194 if (interval <= 0) {
195 Slog.w(TAG, "setInexactRepeating ignored because interval " + interval
196 + " is invalid");
197 return;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800198 }
Christopher Tateb8849c12011-02-08 13:39:01 -0800199
200 // If the requested interval isn't a multiple of 15 minutes, just treat it as exact
201 if (interval % QUANTUM != 0) {
202 if (localLOGV) Slog.v(TAG, "Interval " + interval + " not a quantum multiple");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800203 setRepeating(type, triggerAtTime, interval, operation);
204 return;
205 }
206
Christopher Tateb8849c12011-02-08 13:39:01 -0800207 // Translate times into the ELAPSED timebase for alignment purposes so that
208 // alignment never tries to match against wall clock times.
209 final boolean isRtc = (type == AlarmManager.RTC || type == AlarmManager.RTC_WAKEUP);
210 final long skew = (isRtc)
211 ? System.currentTimeMillis() - SystemClock.elapsedRealtime()
212 : 0;
213
214 // Slip forward to the next ELAPSED-timebase quantum after the stated time. If
215 // we're *at* a quantum point, leave it alone.
216 final long adjustedTriggerTime;
217 long offset = (triggerAtTime - skew) % QUANTUM;
218 if (offset != 0) {
219 adjustedTriggerTime = triggerAtTime - offset + QUANTUM;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800220 } else {
Christopher Tateb8849c12011-02-08 13:39:01 -0800221 adjustedTriggerTime = triggerAtTime;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800222 }
223
Christopher Tateb8849c12011-02-08 13:39:01 -0800224 // Set the alarm based on the quantum-aligned start time
225 if (localLOGV) Slog.v(TAG, "setInexactRepeating: type=" + type + " interval=" + interval
226 + " trigger=" + adjustedTriggerTime + " orig=" + triggerAtTime);
227 setRepeating(type, adjustedTriggerTime, interval, operation);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800228 }
229
Dan Egnor97e44942010-02-04 20:27:47 -0800230 public void setTime(long millis) {
231 mContext.enforceCallingOrSelfPermission(
232 "android.permission.SET_TIME",
233 "setTime");
234
235 SystemClock.setCurrentTimeMillis(millis);
236 }
237
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800238 public void setTimeZone(String tz) {
239 mContext.enforceCallingOrSelfPermission(
240 "android.permission.SET_TIME_ZONE",
241 "setTimeZone");
242
243 if (TextUtils.isEmpty(tz)) return;
244 TimeZone zone = TimeZone.getTimeZone(tz);
245 // Prevent reentrant calls from stepping on each other when writing
246 // the time zone property
247 boolean timeZoneWasChanged = false;
248 synchronized (this) {
249 String current = SystemProperties.get(TIMEZONE_PROPERTY);
250 if (current == null || !current.equals(zone.getID())) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800251 if (localLOGV) Slog.v(TAG, "timezone changed: " + current + ", new=" + zone.getID());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800252 timeZoneWasChanged = true;
253 SystemProperties.set(TIMEZONE_PROPERTY, zone.getID());
254 }
255
256 // Update the kernel timezone information
257 // Kernel tracks time offsets as 'minutes west of GMT'
Lavettacn Xiaoc84cc4f2010-08-30 12:47:23 +0200258 int gmtOffset = zone.getOffset(System.currentTimeMillis());
Mike Lockwood1f7b4132009-11-20 15:12:51 -0500259 setKernelTimezone(mDescriptor, -(gmtOffset / 60000));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800260 }
261
262 TimeZone.setDefault(null);
263
264 if (timeZoneWasChanged) {
265 Intent intent = new Intent(Intent.ACTION_TIMEZONE_CHANGED);
Dianne Hackborn1c633fc2009-12-08 19:45:14 -0800266 intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800267 intent.putExtra("time-zone", zone.getID());
268 mContext.sendBroadcast(intent);
269 }
270 }
271
272 public void remove(PendingIntent operation) {
273 if (operation == null) {
274 return;
275 }
276 synchronized (mLock) {
277 removeLocked(operation);
278 }
279 }
280
281 public void removeLocked(PendingIntent operation) {
282 removeLocked(mRtcWakeupAlarms, operation);
283 removeLocked(mRtcAlarms, operation);
284 removeLocked(mElapsedRealtimeWakeupAlarms, operation);
285 removeLocked(mElapsedRealtimeAlarms, operation);
286 }
287
288 private void removeLocked(ArrayList<Alarm> alarmList,
289 PendingIntent operation) {
290 if (alarmList.size() <= 0) {
291 return;
292 }
293
294 // iterator over the list removing any it where the intent match
295 Iterator<Alarm> it = alarmList.iterator();
296
297 while (it.hasNext()) {
298 Alarm alarm = it.next();
299 if (alarm.operation.equals(operation)) {
300 it.remove();
301 }
302 }
303 }
304
305 public void removeLocked(String packageName) {
306 removeLocked(mRtcWakeupAlarms, packageName);
307 removeLocked(mRtcAlarms, packageName);
308 removeLocked(mElapsedRealtimeWakeupAlarms, packageName);
309 removeLocked(mElapsedRealtimeAlarms, packageName);
310 }
311
312 private void removeLocked(ArrayList<Alarm> alarmList,
313 String packageName) {
314 if (alarmList.size() <= 0) {
315 return;
316 }
317
318 // iterator over the list removing any it where the intent match
319 Iterator<Alarm> it = alarmList.iterator();
320
321 while (it.hasNext()) {
322 Alarm alarm = it.next();
323 if (alarm.operation.getTargetPackage().equals(packageName)) {
324 it.remove();
325 }
326 }
327 }
328
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800329 public boolean lookForPackageLocked(String packageName) {
330 return lookForPackageLocked(mRtcWakeupAlarms, packageName)
331 || lookForPackageLocked(mRtcAlarms, packageName)
332 || lookForPackageLocked(mElapsedRealtimeWakeupAlarms, packageName)
333 || lookForPackageLocked(mElapsedRealtimeAlarms, packageName);
334 }
335
336 private boolean lookForPackageLocked(ArrayList<Alarm> alarmList, String packageName) {
337 for (int i=alarmList.size()-1; i>=0; i--) {
338 if (alarmList.get(i).operation.getTargetPackage().equals(packageName)) {
339 return true;
340 }
341 }
342 return false;
343 }
344
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800345 private ArrayList<Alarm> getAlarmList(int type) {
346 switch (type) {
347 case AlarmManager.RTC_WAKEUP: return mRtcWakeupAlarms;
348 case AlarmManager.RTC: return mRtcAlarms;
349 case AlarmManager.ELAPSED_REALTIME_WAKEUP: return mElapsedRealtimeWakeupAlarms;
350 case AlarmManager.ELAPSED_REALTIME: return mElapsedRealtimeAlarms;
351 }
352
353 return null;
354 }
355
356 private int addAlarmLocked(Alarm alarm) {
357 ArrayList<Alarm> alarmList = getAlarmList(alarm.type);
358
359 int index = Collections.binarySearch(alarmList, alarm, mIncreasingTimeOrder);
360 if (index < 0) {
361 index = 0 - index - 1;
362 }
Joe Onorato8a9b2202010-02-26 18:56:32 -0800363 if (localLOGV) Slog.v(TAG, "Adding alarm " + alarm + " at " + index);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800364 alarmList.add(index, alarm);
365
366 if (localLOGV) {
367 // Display the list of alarms for this alarm type
Joe Onorato8a9b2202010-02-26 18:56:32 -0800368 Slog.v(TAG, "alarms: " + alarmList.size() + " type: " + alarm.type);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800369 int position = 0;
370 for (Alarm a : alarmList) {
371 Time time = new Time();
372 time.set(a.when);
373 String timeStr = time.format("%b %d %I:%M:%S %p");
Joe Onorato8a9b2202010-02-26 18:56:32 -0800374 Slog.v(TAG, position + ": " + timeStr
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800375 + " " + a.operation.getTargetPackage());
376 position += 1;
377 }
378 }
379
380 return index;
381 }
382
383 public long timeToNextAlarm() {
Jeff Brown11c5f1a2010-03-31 15:29:40 -0700384 long nextAlarm = Long.MAX_VALUE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800385 synchronized (mLock) {
386 for (int i=AlarmManager.RTC_WAKEUP;
387 i<=AlarmManager.ELAPSED_REALTIME; i++) {
388 ArrayList<Alarm> alarmList = getAlarmList(i);
389 if (alarmList.size() > 0) {
390 Alarm a = alarmList.get(0);
391 if (a.when < nextAlarm) {
392 nextAlarm = a.when;
393 }
394 }
395 }
396 }
397 return nextAlarm;
398 }
399
400 private void setLocked(Alarm alarm)
401 {
402 if (mDescriptor != -1)
403 {
Jeff Brown11c5f1a2010-03-31 15:29:40 -0700404 // The kernel never triggers alarms with negative wakeup times
405 // so we ensure they are positive.
406 long alarmSeconds, alarmNanoseconds;
407 if (alarm.when < 0) {
408 alarmSeconds = 0;
409 alarmNanoseconds = 0;
410 } else {
411 alarmSeconds = alarm.when / 1000;
412 alarmNanoseconds = (alarm.when % 1000) * 1000 * 1000;
413 }
414
415 set(mDescriptor, alarm.type, alarmSeconds, alarmNanoseconds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800416 }
417 else
418 {
419 Message msg = Message.obtain();
420 msg.what = ALARM_EVENT;
421
422 mHandler.removeMessages(ALARM_EVENT);
423 mHandler.sendMessageAtTime(msg, alarm.when);
424 }
425 }
426
427 @Override
428 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
429 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
430 != PackageManager.PERMISSION_GRANTED) {
431 pw.println("Permission Denial: can't dump AlarmManager from from pid="
432 + Binder.getCallingPid()
433 + ", uid=" + Binder.getCallingUid());
434 return;
435 }
436
437 synchronized (mLock) {
438 pw.println("Current Alarm Manager state:");
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700439 if (mRtcWakeupAlarms.size() > 0 || mRtcAlarms.size() > 0) {
Dianne Hackborn043fcd92010-10-06 14:27:34 -0700440 final long now = System.currentTimeMillis();
441 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800442 pw.println(" ");
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700443 pw.print(" Realtime wakeup (now=");
Dianne Hackborn043fcd92010-10-06 14:27:34 -0700444 pw.print(sdf.format(new Date(now))); pw.println("):");
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700445 if (mRtcWakeupAlarms.size() > 0) {
Dianne Hackborn043fcd92010-10-06 14:27:34 -0700446 dumpAlarmList(pw, mRtcWakeupAlarms, " ", "RTC_WAKEUP", now);
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700447 }
448 if (mRtcAlarms.size() > 0) {
Dianne Hackborn043fcd92010-10-06 14:27:34 -0700449 dumpAlarmList(pw, mRtcAlarms, " ", "RTC", now);
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700450 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800451 }
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700452 if (mElapsedRealtimeWakeupAlarms.size() > 0 || mElapsedRealtimeAlarms.size() > 0) {
Dianne Hackborn043fcd92010-10-06 14:27:34 -0700453 final long now = SystemClock.elapsedRealtime();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800454 pw.println(" ");
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700455 pw.print(" Elapsed realtime wakeup (now=");
Dianne Hackborn043fcd92010-10-06 14:27:34 -0700456 TimeUtils.formatDuration(now, pw); pw.println("):");
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700457 if (mElapsedRealtimeWakeupAlarms.size() > 0) {
Dianne Hackborn043fcd92010-10-06 14:27:34 -0700458 dumpAlarmList(pw, mElapsedRealtimeWakeupAlarms, " ", "ELAPSED_WAKEUP", now);
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700459 }
460 if (mElapsedRealtimeAlarms.size() > 0) {
Dianne Hackborn043fcd92010-10-06 14:27:34 -0700461 dumpAlarmList(pw, mElapsedRealtimeAlarms, " ", "ELAPSED", now);
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700462 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800463 }
464
465 pw.println(" ");
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700466 pw.print(" Broadcast ref count: "); pw.println(mBroadcastRefCount);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800467
468 pw.println(" ");
469 pw.println(" Alarm Stats:");
470 for (Map.Entry<String, BroadcastStats> be : mBroadcastStats.entrySet()) {
471 BroadcastStats bs = be.getValue();
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700472 pw.print(" "); pw.println(be.getKey());
473 pw.print(" "); pw.print(bs.aggregateTime);
474 pw.print("ms running, "); pw.print(bs.numWakeup);
475 pw.println(" wakeups");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800476 for (Map.Entry<Intent.FilterComparison, FilterStats> fe
477 : bs.filterStats.entrySet()) {
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700478 pw.print(" "); pw.print(fe.getValue().count);
479 pw.print(" alarms: ");
Dianne Hackborn21c241e2012-03-08 13:57:23 -0800480 pw.println(fe.getKey().getIntent().toShortString(
481 false, true, false, true));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800482 }
483 }
484 }
485 }
486
Dianne Hackborn043fcd92010-10-06 14:27:34 -0700487 private static final void dumpAlarmList(PrintWriter pw, ArrayList<Alarm> list,
488 String prefix, String label, long now) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800489 for (int i=list.size()-1; i>=0; i--) {
490 Alarm a = list.get(i);
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700491 pw.print(prefix); pw.print(label); pw.print(" #"); pw.print(i);
492 pw.print(": "); pw.println(a);
Dianne Hackborn043fcd92010-10-06 14:27:34 -0700493 a.dump(pw, prefix + " ", now);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800494 }
495 }
496
497 private native int init();
498 private native void close(int fd);
Jeff Brown11c5f1a2010-03-31 15:29:40 -0700499 private native void set(int fd, int type, long seconds, long nanoseconds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800500 private native int waitForAlarm(int fd);
501 private native int setKernelTimezone(int fd, int minuteswest);
502
503 private void triggerAlarmsLocked(ArrayList<Alarm> alarmList,
504 ArrayList<Alarm> triggerList,
505 long now)
506 {
507 Iterator<Alarm> it = alarmList.iterator();
508 ArrayList<Alarm> repeats = new ArrayList<Alarm>();
509
510 while (it.hasNext())
511 {
512 Alarm alarm = it.next();
513
Joe Onorato8a9b2202010-02-26 18:56:32 -0800514 if (localLOGV) Slog.v(TAG, "Checking active alarm when=" + alarm.when + " " + alarm);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800515
516 if (alarm.when > now) {
517 // don't fire alarms in the future
518 break;
519 }
520
521 // If the alarm is late, then print a warning message.
522 // Note that this can happen if the user creates a new event on
523 // the Calendar app with a reminder that is in the past. In that
524 // case, the reminder alarm will fire immediately.
525 if (localLOGV && now - alarm.when > LATE_ALARM_THRESHOLD) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800526 Slog.v(TAG, "alarm is late! alarm time: " + alarm.when
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800527 + " now: " + now + " delay (in seconds): "
528 + (now - alarm.when) / 1000);
529 }
530
531 // Recurring alarms may have passed several alarm intervals while the
532 // phone was asleep or off, so pass a trigger count when sending them.
Joe Onorato8a9b2202010-02-26 18:56:32 -0800533 if (localLOGV) Slog.v(TAG, "Alarm triggering: " + alarm);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800534 alarm.count = 1;
535 if (alarm.repeatInterval > 0) {
536 // this adjustment will be zero if we're late by
537 // less than one full repeat interval
538 alarm.count += (now - alarm.when) / alarm.repeatInterval;
539 }
540 triggerList.add(alarm);
541
542 // remove the alarm from the list
543 it.remove();
544
545 // if it repeats queue it up to be read-added to the list
546 if (alarm.repeatInterval > 0) {
547 repeats.add(alarm);
548 }
549 }
550
551 // reset any repeating alarms.
552 it = repeats.iterator();
553 while (it.hasNext()) {
554 Alarm alarm = it.next();
555 alarm.when += alarm.count * alarm.repeatInterval;
556 addAlarmLocked(alarm);
557 }
558
559 if (alarmList.size() > 0) {
560 setLocked(alarmList.get(0));
561 }
562 }
563
564 /**
565 * This Comparator sorts Alarms into increasing time order.
566 */
567 public static class IncreasingTimeOrder implements Comparator<Alarm> {
568 public int compare(Alarm a1, Alarm a2) {
569 long when1 = a1.when;
570 long when2 = a2.when;
571 if (when1 - when2 > 0) {
572 return 1;
573 }
574 if (when1 - when2 < 0) {
575 return -1;
576 }
577 return 0;
578 }
579 }
580
581 private static class Alarm {
582 public int type;
583 public int count;
584 public long when;
585 public long repeatInterval;
586 public PendingIntent operation;
587
588 public Alarm() {
589 when = 0;
590 repeatInterval = 0;
591 operation = null;
592 }
593
594 @Override
595 public String toString()
596 {
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700597 StringBuilder sb = new StringBuilder(128);
598 sb.append("Alarm{");
599 sb.append(Integer.toHexString(System.identityHashCode(this)));
600 sb.append(" type ");
601 sb.append(type);
602 sb.append(" ");
603 sb.append(operation.getTargetPackage());
604 sb.append('}');
605 return sb.toString();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800606 }
607
Dianne Hackborn043fcd92010-10-06 14:27:34 -0700608 public void dump(PrintWriter pw, String prefix, long now) {
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700609 pw.print(prefix); pw.print("type="); pw.print(type);
Dianne Hackborn043fcd92010-10-06 14:27:34 -0700610 pw.print(" when="); TimeUtils.formatDuration(when, now, pw);
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700611 pw.print(" repeatInterval="); pw.print(repeatInterval);
612 pw.print(" count="); pw.println(count);
613 pw.print(prefix); pw.print("operation="); pw.println(operation);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800614 }
615 }
616
617 private class AlarmThread extends Thread
618 {
619 public AlarmThread()
620 {
621 super("AlarmManager");
622 }
623
624 public void run()
625 {
626 while (true)
627 {
628 int result = waitForAlarm(mDescriptor);
629
630 ArrayList<Alarm> triggerList = new ArrayList<Alarm>();
631
632 if ((result & TIME_CHANGED_MASK) != 0) {
633 remove(mTimeTickSender);
634 mClockReceiver.scheduleTimeTickEvent();
Dianne Hackborn1c633fc2009-12-08 19:45:14 -0800635 Intent intent = new Intent(Intent.ACTION_TIME_CHANGED);
Dianne Hackborn89ba6752011-01-23 16:51:16 -0800636 intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING
637 | Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
Dianne Hackborn1c633fc2009-12-08 19:45:14 -0800638 mContext.sendBroadcast(intent);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800639 }
640
641 synchronized (mLock) {
642 final long nowRTC = System.currentTimeMillis();
643 final long nowELAPSED = SystemClock.elapsedRealtime();
Joe Onorato8a9b2202010-02-26 18:56:32 -0800644 if (localLOGV) Slog.v(
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800645 TAG, "Checking for alarms... rtc=" + nowRTC
646 + ", elapsed=" + nowELAPSED);
647
648 if ((result & RTC_WAKEUP_MASK) != 0)
649 triggerAlarmsLocked(mRtcWakeupAlarms, triggerList, nowRTC);
650
651 if ((result & RTC_MASK) != 0)
652 triggerAlarmsLocked(mRtcAlarms, triggerList, nowRTC);
653
654 if ((result & ELAPSED_REALTIME_WAKEUP_MASK) != 0)
655 triggerAlarmsLocked(mElapsedRealtimeWakeupAlarms, triggerList, nowELAPSED);
656
657 if ((result & ELAPSED_REALTIME_MASK) != 0)
658 triggerAlarmsLocked(mElapsedRealtimeAlarms, triggerList, nowELAPSED);
659
660 // now trigger the alarms
661 Iterator<Alarm> it = triggerList.iterator();
662 while (it.hasNext()) {
663 Alarm alarm = it.next();
664 try {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800665 if (localLOGV) Slog.v(TAG, "sending alarm " + alarm);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800666 alarm.operation.send(mContext, 0,
667 mBackgroundIntent.putExtra(
668 Intent.EXTRA_ALARM_COUNT, alarm.count),
669 mResultReceiver, mHandler);
670
671 // we have an active broadcast so stay awake.
672 if (mBroadcastRefCount == 0) {
673 mWakeLock.acquire();
674 }
675 mBroadcastRefCount++;
676
677 BroadcastStats bs = getStatsLocked(alarm.operation);
678 if (bs.nesting == 0) {
679 bs.startTime = nowELAPSED;
680 } else {
681 bs.nesting++;
682 }
683 if (alarm.type == AlarmManager.ELAPSED_REALTIME_WAKEUP
684 || alarm.type == AlarmManager.RTC_WAKEUP) {
685 bs.numWakeup++;
686 ActivityManagerNative.noteWakeupAlarm(
687 alarm.operation);
688 }
689 } catch (PendingIntent.CanceledException e) {
690 if (alarm.repeatInterval > 0) {
691 // This IntentSender is no longer valid, but this
692 // is a repeating alarm, so toss the hoser.
693 remove(alarm.operation);
694 }
695 } catch (RuntimeException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800696 Slog.w(TAG, "Failure sending alarm.", e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800697 }
698 }
699 }
700 }
701 }
702 }
703
704 private class AlarmHandler extends Handler {
705 public static final int ALARM_EVENT = 1;
706 public static final int MINUTE_CHANGE_EVENT = 2;
707 public static final int DATE_CHANGE_EVENT = 3;
708
709 public AlarmHandler() {
710 }
711
712 public void handleMessage(Message msg) {
713 if (msg.what == ALARM_EVENT) {
714 ArrayList<Alarm> triggerList = new ArrayList<Alarm>();
715 synchronized (mLock) {
716 final long nowRTC = System.currentTimeMillis();
717 triggerAlarmsLocked(mRtcWakeupAlarms, triggerList, nowRTC);
718 triggerAlarmsLocked(mRtcAlarms, triggerList, nowRTC);
719 triggerAlarmsLocked(mElapsedRealtimeWakeupAlarms, triggerList, nowRTC);
720 triggerAlarmsLocked(mElapsedRealtimeAlarms, triggerList, nowRTC);
721 }
722
723 // now trigger the alarms without the lock held
724 Iterator<Alarm> it = triggerList.iterator();
725 while (it.hasNext())
726 {
727 Alarm alarm = it.next();
728 try {
729 alarm.operation.send();
730 } catch (PendingIntent.CanceledException e) {
731 if (alarm.repeatInterval > 0) {
732 // This IntentSender is no longer valid, but this
733 // is a repeating alarm, so toss the hoser.
734 remove(alarm.operation);
735 }
736 }
737 }
738 }
739 }
740 }
741
742 class ClockReceiver extends BroadcastReceiver {
743 public ClockReceiver() {
744 IntentFilter filter = new IntentFilter();
745 filter.addAction(Intent.ACTION_TIME_TICK);
746 filter.addAction(Intent.ACTION_DATE_CHANGED);
747 mContext.registerReceiver(this, filter);
748 }
749
750 @Override
751 public void onReceive(Context context, Intent intent) {
752 if (intent.getAction().equals(Intent.ACTION_TIME_TICK)) {
753 scheduleTimeTickEvent();
754 } else if (intent.getAction().equals(Intent.ACTION_DATE_CHANGED)) {
755 // Since the kernel does not keep track of DST, we need to
756 // reset the TZ information at the beginning of each day
757 // based off of the current Zone gmt offset + userspace tracked
758 // daylight savings information.
759 TimeZone zone = TimeZone.getTimeZone(SystemProperties.get(TIMEZONE_PROPERTY));
Lavettacn Xiaoc84cc4f2010-08-30 12:47:23 +0200760 int gmtOffset = zone.getOffset(System.currentTimeMillis());
761 setKernelTimezone(mDescriptor, -(gmtOffset / 60000));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800762 scheduleDateChangedEvent();
763 }
764 }
765
766 public void scheduleTimeTickEvent() {
767 Calendar calendar = Calendar.getInstance();
Paul Westbrook51608a52011-08-25 13:18:54 -0700768 final long currentTime = System.currentTimeMillis();
769 calendar.setTimeInMillis(currentTime);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800770 calendar.add(Calendar.MINUTE, 1);
771 calendar.set(Calendar.SECOND, 0);
772 calendar.set(Calendar.MILLISECOND, 0);
Paul Westbrook51608a52011-08-25 13:18:54 -0700773
774 // Schedule this event for the amount of time that it would take to get to
775 // the top of the next minute.
776 final long tickEventDelay = calendar.getTimeInMillis() - currentTime;
777
778 set(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime() + tickEventDelay,
779 mTimeTickSender);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800780 }
781
782 public void scheduleDateChangedEvent() {
783 Calendar calendar = Calendar.getInstance();
784 calendar.setTimeInMillis(System.currentTimeMillis());
785 calendar.set(Calendar.HOUR, 0);
786 calendar.set(Calendar.MINUTE, 0);
787 calendar.set(Calendar.SECOND, 0);
788 calendar.set(Calendar.MILLISECOND, 0);
789 calendar.add(Calendar.DAY_OF_MONTH, 1);
790
791 set(AlarmManager.RTC, calendar.getTimeInMillis(), mDateChangeSender);
792 }
793 }
794
795 class UninstallReceiver extends BroadcastReceiver {
796 public UninstallReceiver() {
797 IntentFilter filter = new IntentFilter();
798 filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
799 filter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800800 filter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800801 filter.addDataScheme("package");
802 mContext.registerReceiver(this, filter);
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800803 // Register for events related to sdcard installation.
804 IntentFilter sdFilter = new IntentFilter();
Suchi Amalapurapub56ae202010-02-04 22:51:07 -0800805 sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800806 mContext.registerReceiver(this, sdFilter);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800807 }
808
809 @Override
810 public void onReceive(Context context, Intent intent) {
811 synchronized (mLock) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800812 String action = intent.getAction();
813 String pkgList[] = null;
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800814 if (Intent.ACTION_QUERY_PACKAGE_RESTART.equals(action)) {
815 pkgList = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);
816 for (String packageName : pkgList) {
817 if (lookForPackageLocked(packageName)) {
818 setResultCode(Activity.RESULT_OK);
819 return;
820 }
821 }
822 return;
823 } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800824 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
825 } else {
Dianne Hackborn409578f2010-03-10 17:23:43 -0800826 if (Intent.ACTION_PACKAGE_REMOVED.equals(action)
827 && intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
828 // This package is being updated; don't kill its alarms.
829 return;
830 }
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800831 Uri data = intent.getData();
832 if (data != null) {
833 String pkg = data.getSchemeSpecificPart();
834 if (pkg != null) {
835 pkgList = new String[]{pkg};
836 }
837 }
838 }
839 if (pkgList != null && (pkgList.length > 0)) {
840 for (String pkg : pkgList) {
841 removeLocked(pkg);
842 mBroadcastStats.remove(pkg);
843 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800844 }
845 }
846 }
847 }
848
849 private final BroadcastStats getStatsLocked(PendingIntent pi) {
850 String pkg = pi.getTargetPackage();
851 BroadcastStats bs = mBroadcastStats.get(pkg);
852 if (bs == null) {
853 bs = new BroadcastStats();
854 mBroadcastStats.put(pkg, bs);
855 }
856 return bs;
857 }
858
859 class ResultReceiver implements PendingIntent.OnFinished {
860 public void onSendFinished(PendingIntent pi, Intent intent, int resultCode,
861 String resultData, Bundle resultExtras) {
862 synchronized (mLock) {
863 BroadcastStats bs = getStatsLocked(pi);
864 if (bs != null) {
865 bs.nesting--;
866 if (bs.nesting <= 0) {
867 bs.nesting = 0;
868 bs.aggregateTime += SystemClock.elapsedRealtime()
869 - bs.startTime;
870 Intent.FilterComparison fc = new Intent.FilterComparison(intent);
871 FilterStats fs = bs.filterStats.get(fc);
872 if (fs == null) {
873 fs = new FilterStats();
874 bs.filterStats.put(fc, fs);
875 }
876 fs.count++;
877 }
878 }
879 mBroadcastRefCount--;
880 if (mBroadcastRefCount == 0) {
881 mWakeLock.release();
882 }
883 }
884 }
885 }
886}