blob: 32ac8e1506cdc48a7fd845e56a392080e91e8101 [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;
Christopher Tatec4a07d12012-04-06 14:19:13 -070037import android.os.WorkSource;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080038import android.text.TextUtils;
39import android.text.format.Time;
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;
Christopher Tatec4a07d12012-04-06 14:19:13 -070053import java.util.LinkedList;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080054import java.util.Map;
55import java.util.TimeZone;
56
57class AlarmManagerService extends IAlarmManager.Stub {
58 // The threshold for how long an alarm can be late before we print a
59 // warning message. The time duration is in milliseconds.
60 private static final long LATE_ALARM_THRESHOLD = 10 * 1000;
61
62 private static final int RTC_WAKEUP_MASK = 1 << AlarmManager.RTC_WAKEUP;
63 private static final int RTC_MASK = 1 << AlarmManager.RTC;
64 private static final int ELAPSED_REALTIME_WAKEUP_MASK = 1 << AlarmManager.ELAPSED_REALTIME_WAKEUP;
65 private static final int ELAPSED_REALTIME_MASK = 1 << AlarmManager.ELAPSED_REALTIME;
66 private static final int TIME_CHANGED_MASK = 1 << 16;
Christopher Tateb8849c12011-02-08 13:39:01 -080067
68 // Alignment quantum for inexact repeating alarms
69 private static final long QUANTUM = AlarmManager.INTERVAL_FIFTEEN_MINUTES;
70
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080071 private static final String TAG = "AlarmManager";
72 private static final String ClockReceiver_TAG = "ClockReceiver";
73 private static final boolean localLOGV = false;
74 private static final int ALARM_EVENT = 1;
75 private static final String TIMEZONE_PROPERTY = "persist.sys.timezone";
76
77 private static final Intent mBackgroundIntent
78 = new Intent().addFlags(Intent.FLAG_FROM_BACKGROUND);
79
80 private final Context mContext;
81
82 private Object mLock = new Object();
83
84 private final ArrayList<Alarm> mRtcWakeupAlarms = new ArrayList<Alarm>();
85 private final ArrayList<Alarm> mRtcAlarms = new ArrayList<Alarm>();
86 private final ArrayList<Alarm> mElapsedRealtimeWakeupAlarms = new ArrayList<Alarm>();
87 private final ArrayList<Alarm> mElapsedRealtimeAlarms = new ArrayList<Alarm>();
88 private final IncreasingTimeOrder mIncreasingTimeOrder = new IncreasingTimeOrder();
89
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080090 private int mDescriptor;
91 private int mBroadcastRefCount = 0;
92 private PowerManager.WakeLock mWakeLock;
Christopher Tatec4a07d12012-04-06 14:19:13 -070093 private LinkedList<PendingIntent> mInFlight = new LinkedList<PendingIntent>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080094 private final AlarmThread mWaitThread = new AlarmThread();
95 private final AlarmHandler mHandler = new AlarmHandler();
96 private ClockReceiver mClockReceiver;
97 private UninstallReceiver mUninstallReceiver;
98 private final ResultReceiver mResultReceiver = new ResultReceiver();
99 private final PendingIntent mTimeTickSender;
100 private final PendingIntent mDateChangeSender;
101
102 private static final class FilterStats {
103 int count;
104 }
105
106 private static final class BroadcastStats {
107 long aggregateTime;
108 int numWakeup;
109 long startTime;
110 int nesting;
111 HashMap<Intent.FilterComparison, FilterStats> filterStats
112 = new HashMap<Intent.FilterComparison, FilterStats>();
113 }
114
115 private final HashMap<String, BroadcastStats> mBroadcastStats
116 = new HashMap<String, BroadcastStats>();
117
118 public AlarmManagerService(Context context) {
119 mContext = context;
120 mDescriptor = init();
Robert CH Chou64ba8e42009-11-04 21:38:49 +0800121
122 // We have to set current TimeZone info to kernel
123 // because kernel doesn't keep this after reboot
124 String tz = SystemProperties.get(TIMEZONE_PROPERTY);
125 if (tz != null) {
126 setTimeZone(tz);
127 }
128
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800129 PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
130 mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
131
132 mTimeTickSender = PendingIntent.getBroadcast(context, 0,
133 new Intent(Intent.ACTION_TIME_TICK).addFlags(
134 Intent.FLAG_RECEIVER_REGISTERED_ONLY), 0);
Dianne Hackborn1c633fc2009-12-08 19:45:14 -0800135 Intent intent = new Intent(Intent.ACTION_DATE_CHANGED);
136 intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
137 mDateChangeSender = PendingIntent.getBroadcast(context, 0, intent, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800138
139 // now that we have initied the driver schedule the alarm
140 mClockReceiver= new ClockReceiver();
141 mClockReceiver.scheduleTimeTickEvent();
142 mClockReceiver.scheduleDateChangedEvent();
143 mUninstallReceiver = new UninstallReceiver();
144
145 if (mDescriptor != -1) {
146 mWaitThread.start();
147 } else {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800148 Slog.w(TAG, "Failed to open alarm driver. Falling back to a handler.");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800149 }
150 }
151
152 protected void finalize() throws Throwable {
153 try {
154 close(mDescriptor);
155 } finally {
156 super.finalize();
157 }
158 }
159
160 public void set(int type, long triggerAtTime, PendingIntent operation) {
161 setRepeating(type, triggerAtTime, 0, operation);
162 }
163
164 public void setRepeating(int type, long triggerAtTime, long interval,
165 PendingIntent operation) {
166 if (operation == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800167 Slog.w(TAG, "set/setRepeating ignored because there is no intent");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800168 return;
169 }
170 synchronized (mLock) {
171 Alarm alarm = new Alarm();
172 alarm.type = type;
173 alarm.when = triggerAtTime;
174 alarm.repeatInterval = interval;
175 alarm.operation = operation;
176
177 // Remove this alarm if already scheduled.
178 removeLocked(operation);
179
Joe Onorato8a9b2202010-02-26 18:56:32 -0800180 if (localLOGV) Slog.v(TAG, "set: " + alarm);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800181
182 int index = addAlarmLocked(alarm);
183 if (index == 0) {
184 setLocked(alarm);
185 }
186 }
187 }
188
189 public void setInexactRepeating(int type, long triggerAtTime, long interval,
190 PendingIntent operation) {
191 if (operation == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800192 Slog.w(TAG, "setInexactRepeating ignored because there is no intent");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800193 return;
194 }
195
Christopher Tateb8849c12011-02-08 13:39:01 -0800196 if (interval <= 0) {
197 Slog.w(TAG, "setInexactRepeating ignored because interval " + interval
198 + " is invalid");
199 return;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800200 }
Christopher Tateb8849c12011-02-08 13:39:01 -0800201
202 // If the requested interval isn't a multiple of 15 minutes, just treat it as exact
203 if (interval % QUANTUM != 0) {
204 if (localLOGV) Slog.v(TAG, "Interval " + interval + " not a quantum multiple");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800205 setRepeating(type, triggerAtTime, interval, operation);
206 return;
207 }
208
Christopher Tateb8849c12011-02-08 13:39:01 -0800209 // Translate times into the ELAPSED timebase for alignment purposes so that
210 // alignment never tries to match against wall clock times.
211 final boolean isRtc = (type == AlarmManager.RTC || type == AlarmManager.RTC_WAKEUP);
212 final long skew = (isRtc)
213 ? System.currentTimeMillis() - SystemClock.elapsedRealtime()
214 : 0;
215
216 // Slip forward to the next ELAPSED-timebase quantum after the stated time. If
217 // we're *at* a quantum point, leave it alone.
218 final long adjustedTriggerTime;
219 long offset = (triggerAtTime - skew) % QUANTUM;
220 if (offset != 0) {
221 adjustedTriggerTime = triggerAtTime - offset + QUANTUM;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800222 } else {
Christopher Tateb8849c12011-02-08 13:39:01 -0800223 adjustedTriggerTime = triggerAtTime;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800224 }
225
Christopher Tateb8849c12011-02-08 13:39:01 -0800226 // Set the alarm based on the quantum-aligned start time
227 if (localLOGV) Slog.v(TAG, "setInexactRepeating: type=" + type + " interval=" + interval
228 + " trigger=" + adjustedTriggerTime + " orig=" + triggerAtTime);
229 setRepeating(type, adjustedTriggerTime, interval, operation);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800230 }
231
Dan Egnor97e44942010-02-04 20:27:47 -0800232 public void setTime(long millis) {
233 mContext.enforceCallingOrSelfPermission(
234 "android.permission.SET_TIME",
235 "setTime");
236
237 SystemClock.setCurrentTimeMillis(millis);
238 }
239
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800240 public void setTimeZone(String tz) {
241 mContext.enforceCallingOrSelfPermission(
242 "android.permission.SET_TIME_ZONE",
243 "setTimeZone");
244
245 if (TextUtils.isEmpty(tz)) return;
246 TimeZone zone = TimeZone.getTimeZone(tz);
247 // Prevent reentrant calls from stepping on each other when writing
248 // the time zone property
249 boolean timeZoneWasChanged = false;
250 synchronized (this) {
251 String current = SystemProperties.get(TIMEZONE_PROPERTY);
252 if (current == null || !current.equals(zone.getID())) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800253 if (localLOGV) Slog.v(TAG, "timezone changed: " + current + ", new=" + zone.getID());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800254 timeZoneWasChanged = true;
255 SystemProperties.set(TIMEZONE_PROPERTY, zone.getID());
256 }
257
258 // Update the kernel timezone information
259 // Kernel tracks time offsets as 'minutes west of GMT'
Lavettacn Xiaoc84cc4f2010-08-30 12:47:23 +0200260 int gmtOffset = zone.getOffset(System.currentTimeMillis());
Mike Lockwood1f7b4132009-11-20 15:12:51 -0500261 setKernelTimezone(mDescriptor, -(gmtOffset / 60000));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800262 }
263
264 TimeZone.setDefault(null);
265
266 if (timeZoneWasChanged) {
267 Intent intent = new Intent(Intent.ACTION_TIMEZONE_CHANGED);
Dianne Hackborn1c633fc2009-12-08 19:45:14 -0800268 intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800269 intent.putExtra("time-zone", zone.getID());
270 mContext.sendBroadcast(intent);
271 }
272 }
273
274 public void remove(PendingIntent operation) {
275 if (operation == null) {
276 return;
277 }
278 synchronized (mLock) {
279 removeLocked(operation);
280 }
281 }
282
283 public void removeLocked(PendingIntent operation) {
284 removeLocked(mRtcWakeupAlarms, operation);
285 removeLocked(mRtcAlarms, operation);
286 removeLocked(mElapsedRealtimeWakeupAlarms, operation);
287 removeLocked(mElapsedRealtimeAlarms, operation);
288 }
289
290 private void removeLocked(ArrayList<Alarm> alarmList,
291 PendingIntent operation) {
292 if (alarmList.size() <= 0) {
293 return;
294 }
295
296 // iterator over the list removing any it where the intent match
297 Iterator<Alarm> it = alarmList.iterator();
298
299 while (it.hasNext()) {
300 Alarm alarm = it.next();
301 if (alarm.operation.equals(operation)) {
302 it.remove();
303 }
304 }
305 }
306
307 public void removeLocked(String packageName) {
308 removeLocked(mRtcWakeupAlarms, packageName);
309 removeLocked(mRtcAlarms, packageName);
310 removeLocked(mElapsedRealtimeWakeupAlarms, packageName);
311 removeLocked(mElapsedRealtimeAlarms, packageName);
312 }
313
314 private void removeLocked(ArrayList<Alarm> alarmList,
315 String packageName) {
316 if (alarmList.size() <= 0) {
317 return;
318 }
319
320 // iterator over the list removing any it where the intent match
321 Iterator<Alarm> it = alarmList.iterator();
322
323 while (it.hasNext()) {
324 Alarm alarm = it.next();
325 if (alarm.operation.getTargetPackage().equals(packageName)) {
326 it.remove();
327 }
328 }
329 }
330
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800331 public boolean lookForPackageLocked(String packageName) {
332 return lookForPackageLocked(mRtcWakeupAlarms, packageName)
333 || lookForPackageLocked(mRtcAlarms, packageName)
334 || lookForPackageLocked(mElapsedRealtimeWakeupAlarms, packageName)
335 || lookForPackageLocked(mElapsedRealtimeAlarms, packageName);
336 }
337
338 private boolean lookForPackageLocked(ArrayList<Alarm> alarmList, String packageName) {
339 for (int i=alarmList.size()-1; i>=0; i--) {
340 if (alarmList.get(i).operation.getTargetPackage().equals(packageName)) {
341 return true;
342 }
343 }
344 return false;
345 }
346
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800347 private ArrayList<Alarm> getAlarmList(int type) {
348 switch (type) {
349 case AlarmManager.RTC_WAKEUP: return mRtcWakeupAlarms;
350 case AlarmManager.RTC: return mRtcAlarms;
351 case AlarmManager.ELAPSED_REALTIME_WAKEUP: return mElapsedRealtimeWakeupAlarms;
352 case AlarmManager.ELAPSED_REALTIME: return mElapsedRealtimeAlarms;
353 }
354
355 return null;
356 }
357
358 private int addAlarmLocked(Alarm alarm) {
359 ArrayList<Alarm> alarmList = getAlarmList(alarm.type);
360
361 int index = Collections.binarySearch(alarmList, alarm, mIncreasingTimeOrder);
362 if (index < 0) {
363 index = 0 - index - 1;
364 }
Joe Onorato8a9b2202010-02-26 18:56:32 -0800365 if (localLOGV) Slog.v(TAG, "Adding alarm " + alarm + " at " + index);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800366 alarmList.add(index, alarm);
367
368 if (localLOGV) {
369 // Display the list of alarms for this alarm type
Joe Onorato8a9b2202010-02-26 18:56:32 -0800370 Slog.v(TAG, "alarms: " + alarmList.size() + " type: " + alarm.type);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800371 int position = 0;
372 for (Alarm a : alarmList) {
373 Time time = new Time();
374 time.set(a.when);
375 String timeStr = time.format("%b %d %I:%M:%S %p");
Joe Onorato8a9b2202010-02-26 18:56:32 -0800376 Slog.v(TAG, position + ": " + timeStr
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800377 + " " + a.operation.getTargetPackage());
378 position += 1;
379 }
380 }
381
382 return index;
383 }
384
385 public long timeToNextAlarm() {
Jeff Brown11c5f1a2010-03-31 15:29:40 -0700386 long nextAlarm = Long.MAX_VALUE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800387 synchronized (mLock) {
388 for (int i=AlarmManager.RTC_WAKEUP;
389 i<=AlarmManager.ELAPSED_REALTIME; i++) {
390 ArrayList<Alarm> alarmList = getAlarmList(i);
391 if (alarmList.size() > 0) {
392 Alarm a = alarmList.get(0);
393 if (a.when < nextAlarm) {
394 nextAlarm = a.when;
395 }
396 }
397 }
398 }
399 return nextAlarm;
400 }
401
402 private void setLocked(Alarm alarm)
403 {
404 if (mDescriptor != -1)
405 {
Jeff Brown11c5f1a2010-03-31 15:29:40 -0700406 // The kernel never triggers alarms with negative wakeup times
407 // so we ensure they are positive.
408 long alarmSeconds, alarmNanoseconds;
409 if (alarm.when < 0) {
410 alarmSeconds = 0;
411 alarmNanoseconds = 0;
412 } else {
413 alarmSeconds = alarm.when / 1000;
414 alarmNanoseconds = (alarm.when % 1000) * 1000 * 1000;
415 }
416
417 set(mDescriptor, alarm.type, alarmSeconds, alarmNanoseconds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800418 }
419 else
420 {
421 Message msg = Message.obtain();
422 msg.what = ALARM_EVENT;
423
424 mHandler.removeMessages(ALARM_EVENT);
425 mHandler.sendMessageAtTime(msg, alarm.when);
426 }
427 }
428
429 @Override
430 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
431 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
432 != PackageManager.PERMISSION_GRANTED) {
433 pw.println("Permission Denial: can't dump AlarmManager from from pid="
434 + Binder.getCallingPid()
435 + ", uid=" + Binder.getCallingUid());
436 return;
437 }
438
439 synchronized (mLock) {
440 pw.println("Current Alarm Manager state:");
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700441 if (mRtcWakeupAlarms.size() > 0 || mRtcAlarms.size() > 0) {
Dianne Hackborn043fcd92010-10-06 14:27:34 -0700442 final long now = System.currentTimeMillis();
443 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800444 pw.println(" ");
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700445 pw.print(" Realtime wakeup (now=");
Dianne Hackborn043fcd92010-10-06 14:27:34 -0700446 pw.print(sdf.format(new Date(now))); pw.println("):");
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700447 if (mRtcWakeupAlarms.size() > 0) {
Dianne Hackborn043fcd92010-10-06 14:27:34 -0700448 dumpAlarmList(pw, mRtcWakeupAlarms, " ", "RTC_WAKEUP", now);
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700449 }
450 if (mRtcAlarms.size() > 0) {
Dianne Hackborn043fcd92010-10-06 14:27:34 -0700451 dumpAlarmList(pw, mRtcAlarms, " ", "RTC", now);
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700452 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800453 }
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700454 if (mElapsedRealtimeWakeupAlarms.size() > 0 || mElapsedRealtimeAlarms.size() > 0) {
Dianne Hackborn043fcd92010-10-06 14:27:34 -0700455 final long now = SystemClock.elapsedRealtime();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800456 pw.println(" ");
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700457 pw.print(" Elapsed realtime wakeup (now=");
Dianne Hackborn043fcd92010-10-06 14:27:34 -0700458 TimeUtils.formatDuration(now, pw); pw.println("):");
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700459 if (mElapsedRealtimeWakeupAlarms.size() > 0) {
Dianne Hackborn043fcd92010-10-06 14:27:34 -0700460 dumpAlarmList(pw, mElapsedRealtimeWakeupAlarms, " ", "ELAPSED_WAKEUP", now);
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700461 }
462 if (mElapsedRealtimeAlarms.size() > 0) {
Dianne Hackborn043fcd92010-10-06 14:27:34 -0700463 dumpAlarmList(pw, mElapsedRealtimeAlarms, " ", "ELAPSED", now);
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700464 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800465 }
466
467 pw.println(" ");
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700468 pw.print(" Broadcast ref count: "); pw.println(mBroadcastRefCount);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800469
470 pw.println(" ");
471 pw.println(" Alarm Stats:");
472 for (Map.Entry<String, BroadcastStats> be : mBroadcastStats.entrySet()) {
473 BroadcastStats bs = be.getValue();
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700474 pw.print(" "); pw.println(be.getKey());
475 pw.print(" "); pw.print(bs.aggregateTime);
476 pw.print("ms running, "); pw.print(bs.numWakeup);
477 pw.println(" wakeups");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800478 for (Map.Entry<Intent.FilterComparison, FilterStats> fe
479 : bs.filterStats.entrySet()) {
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700480 pw.print(" "); pw.print(fe.getValue().count);
481 pw.print(" alarms: ");
Dianne Hackborn21c241e2012-03-08 13:57:23 -0800482 pw.println(fe.getKey().getIntent().toShortString(
483 false, true, false, true));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800484 }
485 }
486 }
487 }
488
Dianne Hackborn043fcd92010-10-06 14:27:34 -0700489 private static final void dumpAlarmList(PrintWriter pw, ArrayList<Alarm> list,
490 String prefix, String label, long now) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800491 for (int i=list.size()-1; i>=0; i--) {
492 Alarm a = list.get(i);
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700493 pw.print(prefix); pw.print(label); pw.print(" #"); pw.print(i);
494 pw.print(": "); pw.println(a);
Dianne Hackborn043fcd92010-10-06 14:27:34 -0700495 a.dump(pw, prefix + " ", now);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800496 }
497 }
498
499 private native int init();
500 private native void close(int fd);
Jeff Brown11c5f1a2010-03-31 15:29:40 -0700501 private native void set(int fd, int type, long seconds, long nanoseconds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800502 private native int waitForAlarm(int fd);
503 private native int setKernelTimezone(int fd, int minuteswest);
504
505 private void triggerAlarmsLocked(ArrayList<Alarm> alarmList,
506 ArrayList<Alarm> triggerList,
507 long now)
508 {
509 Iterator<Alarm> it = alarmList.iterator();
510 ArrayList<Alarm> repeats = new ArrayList<Alarm>();
511
512 while (it.hasNext())
513 {
514 Alarm alarm = it.next();
515
Joe Onorato8a9b2202010-02-26 18:56:32 -0800516 if (localLOGV) Slog.v(TAG, "Checking active alarm when=" + alarm.when + " " + alarm);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800517
518 if (alarm.when > now) {
519 // don't fire alarms in the future
520 break;
521 }
522
523 // If the alarm is late, then print a warning message.
524 // Note that this can happen if the user creates a new event on
525 // the Calendar app with a reminder that is in the past. In that
526 // case, the reminder alarm will fire immediately.
527 if (localLOGV && now - alarm.when > LATE_ALARM_THRESHOLD) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800528 Slog.v(TAG, "alarm is late! alarm time: " + alarm.when
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800529 + " now: " + now + " delay (in seconds): "
530 + (now - alarm.when) / 1000);
531 }
532
533 // Recurring alarms may have passed several alarm intervals while the
534 // phone was asleep or off, so pass a trigger count when sending them.
Joe Onorato8a9b2202010-02-26 18:56:32 -0800535 if (localLOGV) Slog.v(TAG, "Alarm triggering: " + alarm);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800536 alarm.count = 1;
537 if (alarm.repeatInterval > 0) {
538 // this adjustment will be zero if we're late by
539 // less than one full repeat interval
540 alarm.count += (now - alarm.when) / alarm.repeatInterval;
541 }
542 triggerList.add(alarm);
543
544 // remove the alarm from the list
545 it.remove();
546
547 // if it repeats queue it up to be read-added to the list
548 if (alarm.repeatInterval > 0) {
549 repeats.add(alarm);
550 }
551 }
552
553 // reset any repeating alarms.
554 it = repeats.iterator();
555 while (it.hasNext()) {
556 Alarm alarm = it.next();
557 alarm.when += alarm.count * alarm.repeatInterval;
558 addAlarmLocked(alarm);
559 }
560
561 if (alarmList.size() > 0) {
562 setLocked(alarmList.get(0));
563 }
564 }
565
566 /**
567 * This Comparator sorts Alarms into increasing time order.
568 */
569 public static class IncreasingTimeOrder implements Comparator<Alarm> {
570 public int compare(Alarm a1, Alarm a2) {
571 long when1 = a1.when;
572 long when2 = a2.when;
573 if (when1 - when2 > 0) {
574 return 1;
575 }
576 if (when1 - when2 < 0) {
577 return -1;
578 }
579 return 0;
580 }
581 }
582
583 private static class Alarm {
584 public int type;
585 public int count;
586 public long when;
587 public long repeatInterval;
588 public PendingIntent operation;
589
590 public Alarm() {
591 when = 0;
592 repeatInterval = 0;
593 operation = null;
594 }
595
596 @Override
597 public String toString()
598 {
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700599 StringBuilder sb = new StringBuilder(128);
600 sb.append("Alarm{");
601 sb.append(Integer.toHexString(System.identityHashCode(this)));
602 sb.append(" type ");
603 sb.append(type);
604 sb.append(" ");
605 sb.append(operation.getTargetPackage());
606 sb.append('}');
607 return sb.toString();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800608 }
609
Dianne Hackborn043fcd92010-10-06 14:27:34 -0700610 public void dump(PrintWriter pw, String prefix, long now) {
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700611 pw.print(prefix); pw.print("type="); pw.print(type);
Dianne Hackborn043fcd92010-10-06 14:27:34 -0700612 pw.print(" when="); TimeUtils.formatDuration(when, now, pw);
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700613 pw.print(" repeatInterval="); pw.print(repeatInterval);
614 pw.print(" count="); pw.println(count);
615 pw.print(prefix); pw.print("operation="); pw.println(operation);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800616 }
617 }
618
619 private class AlarmThread extends Thread
620 {
621 public AlarmThread()
622 {
623 super("AlarmManager");
624 }
625
626 public void run()
627 {
628 while (true)
629 {
630 int result = waitForAlarm(mDescriptor);
631
632 ArrayList<Alarm> triggerList = new ArrayList<Alarm>();
633
634 if ((result & TIME_CHANGED_MASK) != 0) {
635 remove(mTimeTickSender);
636 mClockReceiver.scheduleTimeTickEvent();
Dianne Hackborn1c633fc2009-12-08 19:45:14 -0800637 Intent intent = new Intent(Intent.ACTION_TIME_CHANGED);
Dianne Hackborn89ba6752011-01-23 16:51:16 -0800638 intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING
639 | Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
Dianne Hackborn1c633fc2009-12-08 19:45:14 -0800640 mContext.sendBroadcast(intent);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800641 }
642
643 synchronized (mLock) {
644 final long nowRTC = System.currentTimeMillis();
645 final long nowELAPSED = SystemClock.elapsedRealtime();
Joe Onorato8a9b2202010-02-26 18:56:32 -0800646 if (localLOGV) Slog.v(
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800647 TAG, "Checking for alarms... rtc=" + nowRTC
648 + ", elapsed=" + nowELAPSED);
649
650 if ((result & RTC_WAKEUP_MASK) != 0)
651 triggerAlarmsLocked(mRtcWakeupAlarms, triggerList, nowRTC);
652
653 if ((result & RTC_MASK) != 0)
654 triggerAlarmsLocked(mRtcAlarms, triggerList, nowRTC);
655
656 if ((result & ELAPSED_REALTIME_WAKEUP_MASK) != 0)
657 triggerAlarmsLocked(mElapsedRealtimeWakeupAlarms, triggerList, nowELAPSED);
658
659 if ((result & ELAPSED_REALTIME_MASK) != 0)
660 triggerAlarmsLocked(mElapsedRealtimeAlarms, triggerList, nowELAPSED);
661
662 // now trigger the alarms
663 Iterator<Alarm> it = triggerList.iterator();
664 while (it.hasNext()) {
665 Alarm alarm = it.next();
666 try {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800667 if (localLOGV) Slog.v(TAG, "sending alarm " + alarm);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800668 alarm.operation.send(mContext, 0,
669 mBackgroundIntent.putExtra(
670 Intent.EXTRA_ALARM_COUNT, alarm.count),
671 mResultReceiver, mHandler);
672
Christopher Tatec4a07d12012-04-06 14:19:13 -0700673 // we have an active broadcast so stay awake.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800674 if (mBroadcastRefCount == 0) {
Christopher Tatec4a07d12012-04-06 14:19:13 -0700675 setWakelockWorkSource(alarm.operation);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800676 mWakeLock.acquire();
677 }
Christopher Tatec4a07d12012-04-06 14:19:13 -0700678 mInFlight.add(alarm.operation);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800679 mBroadcastRefCount++;
680
681 BroadcastStats bs = getStatsLocked(alarm.operation);
682 if (bs.nesting == 0) {
683 bs.startTime = nowELAPSED;
684 } else {
685 bs.nesting++;
686 }
687 if (alarm.type == AlarmManager.ELAPSED_REALTIME_WAKEUP
688 || alarm.type == AlarmManager.RTC_WAKEUP) {
689 bs.numWakeup++;
690 ActivityManagerNative.noteWakeupAlarm(
691 alarm.operation);
692 }
693 } catch (PendingIntent.CanceledException e) {
694 if (alarm.repeatInterval > 0) {
695 // This IntentSender is no longer valid, but this
696 // is a repeating alarm, so toss the hoser.
697 remove(alarm.operation);
698 }
699 } catch (RuntimeException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800700 Slog.w(TAG, "Failure sending alarm.", e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800701 }
702 }
703 }
704 }
705 }
706 }
Christopher Tatec4a07d12012-04-06 14:19:13 -0700707
708 void setWakelockWorkSource(PendingIntent pi) {
709 try {
710 final int uid = ActivityManagerNative.getDefault()
711 .getUidForIntentSender(pi.getTarget());
712 if (uid >= 0) {
713 mWakeLock.setWorkSource(new WorkSource(uid));
714 return;
715 }
716 } catch (Exception e) {
717 }
718
719 // Something went wrong; fall back to attributing the lock to the OS
720 mWakeLock.setWorkSource(null);
721 }
722
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800723 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));
Lavettacn Xiaoc84cc4f2010-08-30 12:47:23 +0200779 int gmtOffset = zone.getOffset(System.currentTimeMillis());
780 setKernelTimezone(mDescriptor, -(gmtOffset / 60000));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800781 scheduleDateChangedEvent();
782 }
783 }
784
785 public void scheduleTimeTickEvent() {
786 Calendar calendar = Calendar.getInstance();
Paul Westbrook51608a52011-08-25 13:18:54 -0700787 final long currentTime = System.currentTimeMillis();
788 calendar.setTimeInMillis(currentTime);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800789 calendar.add(Calendar.MINUTE, 1);
790 calendar.set(Calendar.SECOND, 0);
791 calendar.set(Calendar.MILLISECOND, 0);
Paul Westbrook51608a52011-08-25 13:18:54 -0700792
793 // Schedule this event for the amount of time that it would take to get to
794 // the top of the next minute.
795 final long tickEventDelay = calendar.getTimeInMillis() - currentTime;
796
797 set(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime() + tickEventDelay,
798 mTimeTickSender);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800799 }
800
801 public void scheduleDateChangedEvent() {
802 Calendar calendar = Calendar.getInstance();
803 calendar.setTimeInMillis(System.currentTimeMillis());
804 calendar.set(Calendar.HOUR, 0);
805 calendar.set(Calendar.MINUTE, 0);
806 calendar.set(Calendar.SECOND, 0);
807 calendar.set(Calendar.MILLISECOND, 0);
808 calendar.add(Calendar.DAY_OF_MONTH, 1);
809
810 set(AlarmManager.RTC, calendar.getTimeInMillis(), mDateChangeSender);
811 }
812 }
813
814 class UninstallReceiver extends BroadcastReceiver {
815 public UninstallReceiver() {
816 IntentFilter filter = new IntentFilter();
817 filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
818 filter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800819 filter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800820 filter.addDataScheme("package");
821 mContext.registerReceiver(this, filter);
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800822 // Register for events related to sdcard installation.
823 IntentFilter sdFilter = new IntentFilter();
Suchi Amalapurapub56ae202010-02-04 22:51:07 -0800824 sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800825 mContext.registerReceiver(this, sdFilter);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800826 }
827
828 @Override
829 public void onReceive(Context context, Intent intent) {
830 synchronized (mLock) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800831 String action = intent.getAction();
832 String pkgList[] = null;
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800833 if (Intent.ACTION_QUERY_PACKAGE_RESTART.equals(action)) {
834 pkgList = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);
835 for (String packageName : pkgList) {
836 if (lookForPackageLocked(packageName)) {
837 setResultCode(Activity.RESULT_OK);
838 return;
839 }
840 }
841 return;
842 } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800843 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
844 } else {
Dianne Hackborn409578f2010-03-10 17:23:43 -0800845 if (Intent.ACTION_PACKAGE_REMOVED.equals(action)
846 && intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
847 // This package is being updated; don't kill its alarms.
848 return;
849 }
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800850 Uri data = intent.getData();
851 if (data != null) {
852 String pkg = data.getSchemeSpecificPart();
853 if (pkg != null) {
854 pkgList = new String[]{pkg};
855 }
856 }
857 }
858 if (pkgList != null && (pkgList.length > 0)) {
859 for (String pkg : pkgList) {
860 removeLocked(pkg);
861 mBroadcastStats.remove(pkg);
862 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800863 }
864 }
865 }
866 }
867
868 private final BroadcastStats getStatsLocked(PendingIntent pi) {
869 String pkg = pi.getTargetPackage();
870 BroadcastStats bs = mBroadcastStats.get(pkg);
871 if (bs == null) {
872 bs = new BroadcastStats();
873 mBroadcastStats.put(pkg, bs);
874 }
875 return bs;
876 }
877
878 class ResultReceiver implements PendingIntent.OnFinished {
879 public void onSendFinished(PendingIntent pi, Intent intent, int resultCode,
880 String resultData, Bundle resultExtras) {
881 synchronized (mLock) {
882 BroadcastStats bs = getStatsLocked(pi);
883 if (bs != null) {
884 bs.nesting--;
885 if (bs.nesting <= 0) {
886 bs.nesting = 0;
887 bs.aggregateTime += SystemClock.elapsedRealtime()
888 - bs.startTime;
889 Intent.FilterComparison fc = new Intent.FilterComparison(intent);
890 FilterStats fs = bs.filterStats.get(fc);
891 if (fs == null) {
892 fs = new FilterStats();
893 bs.filterStats.put(fc, fs);
894 }
895 fs.count++;
896 }
897 }
Christopher Tatec4a07d12012-04-06 14:19:13 -0700898 mInFlight.removeFirst();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800899 mBroadcastRefCount--;
900 if (mBroadcastRefCount == 0) {
901 mWakeLock.release();
Christopher Tatec4a07d12012-04-06 14:19:13 -0700902 } else {
903 // the next of our alarms is now in flight. reattribute the wakelock.
904 final PendingIntent nowInFlight = mInFlight.peekFirst();
905 if (nowInFlight != null) {
906 setWakelockWorkSource(nowInFlight);
907 } else {
908 // should never happen
909 Slog.e(TAG, "Alarm wakelock still held but sent queue empty");
910 mWakeLock.setWorkSource(null);
911 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800912 }
913 }
914 }
915 }
916}