blob: e9066de6003bb87e43b843b18f4b121221ec95f9 [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;
Dianne Hackborn80a4af22012-08-27 19:18:31 -070037import android.os.UserHandle;
Christopher Tatec4a07d12012-04-06 14:19:13 -070038import android.os.WorkSource;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080039import android.text.TextUtils;
40import android.text.format.Time;
Joe Onorato8a9b2202010-02-26 18:56:32 -080041import android.util.Slog;
Dianne Hackborn043fcd92010-10-06 14:27:34 -070042import android.util.TimeUtils;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080043
44import java.io.FileDescriptor;
45import java.io.PrintWriter;
Dianne Hackborn043fcd92010-10-06 14:27:34 -070046import java.text.SimpleDateFormat;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080047import java.util.ArrayList;
48import java.util.Calendar;
49import java.util.Collections;
50import java.util.Comparator;
Mike Lockwood1f7b4132009-11-20 15:12:51 -050051import java.util.Date;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080052import java.util.HashMap;
53import java.util.Iterator;
Christopher Tatec4a07d12012-04-06 14:19:13 -070054import java.util.LinkedList;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080055import java.util.Map;
56import java.util.TimeZone;
57
58class AlarmManagerService extends IAlarmManager.Stub {
59 // The threshold for how long an alarm can be late before we print a
60 // warning message. The time duration is in milliseconds.
61 private static final long LATE_ALARM_THRESHOLD = 10 * 1000;
62
63 private static final int RTC_WAKEUP_MASK = 1 << AlarmManager.RTC_WAKEUP;
64 private static final int RTC_MASK = 1 << AlarmManager.RTC;
65 private static final int ELAPSED_REALTIME_WAKEUP_MASK = 1 << AlarmManager.ELAPSED_REALTIME_WAKEUP;
66 private static final int ELAPSED_REALTIME_MASK = 1 << AlarmManager.ELAPSED_REALTIME;
67 private static final int TIME_CHANGED_MASK = 1 << 16;
Christopher Tateb8849c12011-02-08 13:39:01 -080068
69 // Alignment quantum for inexact repeating alarms
70 private static final long QUANTUM = AlarmManager.INTERVAL_FIFTEEN_MINUTES;
71
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080072 private static final String TAG = "AlarmManager";
73 private static final String ClockReceiver_TAG = "ClockReceiver";
74 private static final boolean localLOGV = false;
75 private static final int ALARM_EVENT = 1;
76 private static final String TIMEZONE_PROPERTY = "persist.sys.timezone";
77
78 private static final Intent mBackgroundIntent
79 = new Intent().addFlags(Intent.FLAG_FROM_BACKGROUND);
80
81 private final Context mContext;
82
83 private Object mLock = new Object();
84
85 private final ArrayList<Alarm> mRtcWakeupAlarms = new ArrayList<Alarm>();
86 private final ArrayList<Alarm> mRtcAlarms = new ArrayList<Alarm>();
87 private final ArrayList<Alarm> mElapsedRealtimeWakeupAlarms = new ArrayList<Alarm>();
88 private final ArrayList<Alarm> mElapsedRealtimeAlarms = new ArrayList<Alarm>();
89 private final IncreasingTimeOrder mIncreasingTimeOrder = new IncreasingTimeOrder();
90
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080091 private int mDescriptor;
92 private int mBroadcastRefCount = 0;
93 private PowerManager.WakeLock mWakeLock;
Christopher Tatec4a07d12012-04-06 14:19:13 -070094 private LinkedList<PendingIntent> mInFlight = new LinkedList<PendingIntent>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080095 private final AlarmThread mWaitThread = new AlarmThread();
96 private final AlarmHandler mHandler = new AlarmHandler();
97 private ClockReceiver mClockReceiver;
98 private UninstallReceiver mUninstallReceiver;
99 private final ResultReceiver mResultReceiver = new ResultReceiver();
100 private final PendingIntent mTimeTickSender;
101 private final PendingIntent mDateChangeSender;
102
103 private static final class FilterStats {
104 int count;
105 }
106
107 private static final class BroadcastStats {
108 long aggregateTime;
109 int numWakeup;
110 long startTime;
111 int nesting;
112 HashMap<Intent.FilterComparison, FilterStats> filterStats
113 = new HashMap<Intent.FilterComparison, FilterStats>();
114 }
115
116 private final HashMap<String, BroadcastStats> mBroadcastStats
117 = new HashMap<String, BroadcastStats>();
118
119 public AlarmManagerService(Context context) {
120 mContext = context;
121 mDescriptor = init();
Robert CH Chou64ba8e42009-11-04 21:38:49 +0800122
123 // We have to set current TimeZone info to kernel
124 // because kernel doesn't keep this after reboot
125 String tz = SystemProperties.get(TIMEZONE_PROPERTY);
126 if (tz != null) {
127 setTimeZone(tz);
128 }
129
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800130 PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
131 mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
132
Dianne Hackborndb5aca92012-10-26 13:39:41 -0700133 mTimeTickSender = PendingIntent.getBroadcastAsUser(context, 0,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800134 new Intent(Intent.ACTION_TIME_TICK).addFlags(
Dianne Hackborndb5aca92012-10-26 13:39:41 -0700135 Intent.FLAG_RECEIVER_REGISTERED_ONLY), 0,
136 UserHandle.ALL);
Dianne Hackborn1c633fc2009-12-08 19:45:14 -0800137 Intent intent = new Intent(Intent.ACTION_DATE_CHANGED);
138 intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
Dianne Hackborndb5aca92012-10-26 13:39:41 -0700139 mDateChangeSender = PendingIntent.getBroadcastAsUser(context, 0, intent,
140 Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT, UserHandle.ALL);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800141
142 // now that we have initied the driver schedule the alarm
143 mClockReceiver= new ClockReceiver();
144 mClockReceiver.scheduleTimeTickEvent();
145 mClockReceiver.scheduleDateChangedEvent();
146 mUninstallReceiver = new UninstallReceiver();
147
148 if (mDescriptor != -1) {
149 mWaitThread.start();
150 } else {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800151 Slog.w(TAG, "Failed to open alarm driver. Falling back to a handler.");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800152 }
153 }
154
155 protected void finalize() throws Throwable {
156 try {
157 close(mDescriptor);
158 } finally {
159 super.finalize();
160 }
161 }
162
163 public void set(int type, long triggerAtTime, PendingIntent operation) {
164 setRepeating(type, triggerAtTime, 0, operation);
165 }
166
167 public void setRepeating(int type, long triggerAtTime, long interval,
168 PendingIntent operation) {
169 if (operation == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800170 Slog.w(TAG, "set/setRepeating ignored because there is no intent");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800171 return;
172 }
173 synchronized (mLock) {
174 Alarm alarm = new Alarm();
175 alarm.type = type;
176 alarm.when = triggerAtTime;
177 alarm.repeatInterval = interval;
178 alarm.operation = operation;
179
180 // Remove this alarm if already scheduled.
181 removeLocked(operation);
182
Joe Onorato8a9b2202010-02-26 18:56:32 -0800183 if (localLOGV) Slog.v(TAG, "set: " + alarm);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800184
185 int index = addAlarmLocked(alarm);
186 if (index == 0) {
187 setLocked(alarm);
188 }
189 }
190 }
191
192 public void setInexactRepeating(int type, long triggerAtTime, long interval,
193 PendingIntent operation) {
194 if (operation == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800195 Slog.w(TAG, "setInexactRepeating ignored because there is no intent");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800196 return;
197 }
198
Christopher Tateb8849c12011-02-08 13:39:01 -0800199 if (interval <= 0) {
200 Slog.w(TAG, "setInexactRepeating ignored because interval " + interval
201 + " is invalid");
202 return;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800203 }
Christopher Tateb8849c12011-02-08 13:39:01 -0800204
205 // If the requested interval isn't a multiple of 15 minutes, just treat it as exact
206 if (interval % QUANTUM != 0) {
207 if (localLOGV) Slog.v(TAG, "Interval " + interval + " not a quantum multiple");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800208 setRepeating(type, triggerAtTime, interval, operation);
209 return;
210 }
211
Christopher Tateb8849c12011-02-08 13:39:01 -0800212 // Translate times into the ELAPSED timebase for alignment purposes so that
213 // alignment never tries to match against wall clock times.
214 final boolean isRtc = (type == AlarmManager.RTC || type == AlarmManager.RTC_WAKEUP);
215 final long skew = (isRtc)
216 ? System.currentTimeMillis() - SystemClock.elapsedRealtime()
217 : 0;
218
219 // Slip forward to the next ELAPSED-timebase quantum after the stated time. If
220 // we're *at* a quantum point, leave it alone.
221 final long adjustedTriggerTime;
222 long offset = (triggerAtTime - skew) % QUANTUM;
223 if (offset != 0) {
224 adjustedTriggerTime = triggerAtTime - offset + QUANTUM;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800225 } else {
Christopher Tateb8849c12011-02-08 13:39:01 -0800226 adjustedTriggerTime = triggerAtTime;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800227 }
228
Christopher Tateb8849c12011-02-08 13:39:01 -0800229 // Set the alarm based on the quantum-aligned start time
230 if (localLOGV) Slog.v(TAG, "setInexactRepeating: type=" + type + " interval=" + interval
231 + " trigger=" + adjustedTriggerTime + " orig=" + triggerAtTime);
232 setRepeating(type, adjustedTriggerTime, interval, operation);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800233 }
234
Dan Egnor97e44942010-02-04 20:27:47 -0800235 public void setTime(long millis) {
236 mContext.enforceCallingOrSelfPermission(
237 "android.permission.SET_TIME",
238 "setTime");
239
240 SystemClock.setCurrentTimeMillis(millis);
241 }
242
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800243 public void setTimeZone(String tz) {
244 mContext.enforceCallingOrSelfPermission(
245 "android.permission.SET_TIME_ZONE",
246 "setTimeZone");
247
Christopher Tate89779822012-08-31 14:40:03 -0700248 long oldId = Binder.clearCallingIdentity();
249 try {
250 if (TextUtils.isEmpty(tz)) return;
251 TimeZone zone = TimeZone.getTimeZone(tz);
252 // Prevent reentrant calls from stepping on each other when writing
253 // the time zone property
254 boolean timeZoneWasChanged = false;
255 synchronized (this) {
256 String current = SystemProperties.get(TIMEZONE_PROPERTY);
257 if (current == null || !current.equals(zone.getID())) {
258 if (localLOGV) {
259 Slog.v(TAG, "timezone changed: " + current + ", new=" + zone.getID());
260 }
261 timeZoneWasChanged = true;
262 SystemProperties.set(TIMEZONE_PROPERTY, zone.getID());
263 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800264
Christopher Tate89779822012-08-31 14:40:03 -0700265 // Update the kernel timezone information
266 // Kernel tracks time offsets as 'minutes west of GMT'
267 int gmtOffset = zone.getOffset(System.currentTimeMillis());
268 setKernelTimezone(mDescriptor, -(gmtOffset / 60000));
269 }
270
271 TimeZone.setDefault(null);
272
273 if (timeZoneWasChanged) {
274 Intent intent = new Intent(Intent.ACTION_TIMEZONE_CHANGED);
275 intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
276 intent.putExtra("time-zone", zone.getID());
277 mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
278 }
279 } finally {
280 Binder.restoreCallingIdentity(oldId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800281 }
282 }
283
284 public void remove(PendingIntent operation) {
285 if (operation == null) {
286 return;
287 }
288 synchronized (mLock) {
289 removeLocked(operation);
290 }
291 }
292
293 public void removeLocked(PendingIntent operation) {
294 removeLocked(mRtcWakeupAlarms, operation);
295 removeLocked(mRtcAlarms, operation);
296 removeLocked(mElapsedRealtimeWakeupAlarms, operation);
297 removeLocked(mElapsedRealtimeAlarms, operation);
298 }
299
300 private void removeLocked(ArrayList<Alarm> alarmList,
301 PendingIntent operation) {
302 if (alarmList.size() <= 0) {
303 return;
304 }
305
306 // iterator over the list removing any it where the intent match
307 Iterator<Alarm> it = alarmList.iterator();
308
309 while (it.hasNext()) {
310 Alarm alarm = it.next();
311 if (alarm.operation.equals(operation)) {
312 it.remove();
313 }
314 }
315 }
Dianne Hackborn80a4af22012-08-27 19:18:31 -0700316
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800317 public void removeLocked(String packageName) {
318 removeLocked(mRtcWakeupAlarms, packageName);
319 removeLocked(mRtcAlarms, packageName);
320 removeLocked(mElapsedRealtimeWakeupAlarms, packageName);
321 removeLocked(mElapsedRealtimeAlarms, packageName);
322 }
323
324 private void removeLocked(ArrayList<Alarm> alarmList,
325 String packageName) {
326 if (alarmList.size() <= 0) {
327 return;
328 }
329
330 // iterator over the list removing any it where the intent match
331 Iterator<Alarm> it = alarmList.iterator();
332
333 while (it.hasNext()) {
334 Alarm alarm = it.next();
335 if (alarm.operation.getTargetPackage().equals(packageName)) {
336 it.remove();
337 }
338 }
339 }
Dianne Hackborn80a4af22012-08-27 19:18:31 -0700340
341 public void removeUserLocked(int userHandle) {
342 removeUserLocked(mRtcWakeupAlarms, userHandle);
343 removeUserLocked(mRtcAlarms, userHandle);
344 removeUserLocked(mElapsedRealtimeWakeupAlarms, userHandle);
345 removeUserLocked(mElapsedRealtimeAlarms, userHandle);
346 }
347
348 private void removeUserLocked(ArrayList<Alarm> alarmList, int userHandle) {
349 if (alarmList.size() <= 0) {
350 return;
351 }
352
353 // iterator over the list removing any it where the intent match
354 Iterator<Alarm> it = alarmList.iterator();
355
356 while (it.hasNext()) {
357 Alarm alarm = it.next();
Dianne Hackborn8832c182012-09-17 17:20:24 -0700358 if (UserHandle.getUserId(alarm.operation.getCreatorUid()) == userHandle) {
Dianne Hackborn80a4af22012-08-27 19:18:31 -0700359 it.remove();
360 }
361 }
362 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800363
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800364 public boolean lookForPackageLocked(String packageName) {
365 return lookForPackageLocked(mRtcWakeupAlarms, packageName)
366 || lookForPackageLocked(mRtcAlarms, packageName)
367 || lookForPackageLocked(mElapsedRealtimeWakeupAlarms, packageName)
368 || lookForPackageLocked(mElapsedRealtimeAlarms, packageName);
369 }
370
371 private boolean lookForPackageLocked(ArrayList<Alarm> alarmList, String packageName) {
372 for (int i=alarmList.size()-1; i>=0; i--) {
373 if (alarmList.get(i).operation.getTargetPackage().equals(packageName)) {
374 return true;
375 }
376 }
377 return false;
378 }
379
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800380 private ArrayList<Alarm> getAlarmList(int type) {
381 switch (type) {
382 case AlarmManager.RTC_WAKEUP: return mRtcWakeupAlarms;
383 case AlarmManager.RTC: return mRtcAlarms;
384 case AlarmManager.ELAPSED_REALTIME_WAKEUP: return mElapsedRealtimeWakeupAlarms;
385 case AlarmManager.ELAPSED_REALTIME: return mElapsedRealtimeAlarms;
386 }
387
388 return null;
389 }
390
391 private int addAlarmLocked(Alarm alarm) {
392 ArrayList<Alarm> alarmList = getAlarmList(alarm.type);
393
394 int index = Collections.binarySearch(alarmList, alarm, mIncreasingTimeOrder);
395 if (index < 0) {
396 index = 0 - index - 1;
397 }
Joe Onorato8a9b2202010-02-26 18:56:32 -0800398 if (localLOGV) Slog.v(TAG, "Adding alarm " + alarm + " at " + index);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800399 alarmList.add(index, alarm);
400
401 if (localLOGV) {
402 // Display the list of alarms for this alarm type
Joe Onorato8a9b2202010-02-26 18:56:32 -0800403 Slog.v(TAG, "alarms: " + alarmList.size() + " type: " + alarm.type);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800404 int position = 0;
405 for (Alarm a : alarmList) {
406 Time time = new Time();
407 time.set(a.when);
408 String timeStr = time.format("%b %d %I:%M:%S %p");
Joe Onorato8a9b2202010-02-26 18:56:32 -0800409 Slog.v(TAG, position + ": " + timeStr
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800410 + " " + a.operation.getTargetPackage());
411 position += 1;
412 }
413 }
414
415 return index;
416 }
417
418 public long timeToNextAlarm() {
Jeff Brown11c5f1a2010-03-31 15:29:40 -0700419 long nextAlarm = Long.MAX_VALUE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800420 synchronized (mLock) {
421 for (int i=AlarmManager.RTC_WAKEUP;
422 i<=AlarmManager.ELAPSED_REALTIME; i++) {
423 ArrayList<Alarm> alarmList = getAlarmList(i);
424 if (alarmList.size() > 0) {
425 Alarm a = alarmList.get(0);
426 if (a.when < nextAlarm) {
427 nextAlarm = a.when;
428 }
429 }
430 }
431 }
432 return nextAlarm;
433 }
434
435 private void setLocked(Alarm alarm)
436 {
437 if (mDescriptor != -1)
438 {
Jeff Brown11c5f1a2010-03-31 15:29:40 -0700439 // The kernel never triggers alarms with negative wakeup times
440 // so we ensure they are positive.
441 long alarmSeconds, alarmNanoseconds;
442 if (alarm.when < 0) {
443 alarmSeconds = 0;
444 alarmNanoseconds = 0;
445 } else {
446 alarmSeconds = alarm.when / 1000;
447 alarmNanoseconds = (alarm.when % 1000) * 1000 * 1000;
448 }
449
450 set(mDescriptor, alarm.type, alarmSeconds, alarmNanoseconds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800451 }
452 else
453 {
454 Message msg = Message.obtain();
455 msg.what = ALARM_EVENT;
456
457 mHandler.removeMessages(ALARM_EVENT);
458 mHandler.sendMessageAtTime(msg, alarm.when);
459 }
460 }
461
462 @Override
463 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
464 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
465 != PackageManager.PERMISSION_GRANTED) {
466 pw.println("Permission Denial: can't dump AlarmManager from from pid="
467 + Binder.getCallingPid()
468 + ", uid=" + Binder.getCallingUid());
469 return;
470 }
471
472 synchronized (mLock) {
473 pw.println("Current Alarm Manager state:");
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700474 if (mRtcWakeupAlarms.size() > 0 || mRtcAlarms.size() > 0) {
Dianne Hackborn043fcd92010-10-06 14:27:34 -0700475 final long now = System.currentTimeMillis();
476 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800477 pw.println(" ");
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700478 pw.print(" Realtime wakeup (now=");
Dianne Hackborn043fcd92010-10-06 14:27:34 -0700479 pw.print(sdf.format(new Date(now))); pw.println("):");
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700480 if (mRtcWakeupAlarms.size() > 0) {
Dianne Hackborn043fcd92010-10-06 14:27:34 -0700481 dumpAlarmList(pw, mRtcWakeupAlarms, " ", "RTC_WAKEUP", now);
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700482 }
483 if (mRtcAlarms.size() > 0) {
Dianne Hackborn043fcd92010-10-06 14:27:34 -0700484 dumpAlarmList(pw, mRtcAlarms, " ", "RTC", now);
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700485 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800486 }
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700487 if (mElapsedRealtimeWakeupAlarms.size() > 0 || mElapsedRealtimeAlarms.size() > 0) {
Dianne Hackborn043fcd92010-10-06 14:27:34 -0700488 final long now = SystemClock.elapsedRealtime();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800489 pw.println(" ");
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700490 pw.print(" Elapsed realtime wakeup (now=");
Dianne Hackborn043fcd92010-10-06 14:27:34 -0700491 TimeUtils.formatDuration(now, pw); pw.println("):");
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700492 if (mElapsedRealtimeWakeupAlarms.size() > 0) {
Dianne Hackborn043fcd92010-10-06 14:27:34 -0700493 dumpAlarmList(pw, mElapsedRealtimeWakeupAlarms, " ", "ELAPSED_WAKEUP", now);
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700494 }
495 if (mElapsedRealtimeAlarms.size() > 0) {
Dianne Hackborn043fcd92010-10-06 14:27:34 -0700496 dumpAlarmList(pw, mElapsedRealtimeAlarms, " ", "ELAPSED", now);
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700497 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800498 }
499
500 pw.println(" ");
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700501 pw.print(" Broadcast ref count: "); pw.println(mBroadcastRefCount);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800502
503 pw.println(" ");
504 pw.println(" Alarm Stats:");
505 for (Map.Entry<String, BroadcastStats> be : mBroadcastStats.entrySet()) {
506 BroadcastStats bs = be.getValue();
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700507 pw.print(" "); pw.println(be.getKey());
508 pw.print(" "); pw.print(bs.aggregateTime);
509 pw.print("ms running, "); pw.print(bs.numWakeup);
510 pw.println(" wakeups");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800511 for (Map.Entry<Intent.FilterComparison, FilterStats> fe
512 : bs.filterStats.entrySet()) {
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700513 pw.print(" "); pw.print(fe.getValue().count);
514 pw.print(" alarms: ");
Dianne Hackborn21c241e2012-03-08 13:57:23 -0800515 pw.println(fe.getKey().getIntent().toShortString(
516 false, true, false, true));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800517 }
518 }
519 }
520 }
521
Dianne Hackborn043fcd92010-10-06 14:27:34 -0700522 private static final void dumpAlarmList(PrintWriter pw, ArrayList<Alarm> list,
523 String prefix, String label, long now) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800524 for (int i=list.size()-1; i>=0; i--) {
525 Alarm a = list.get(i);
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700526 pw.print(prefix); pw.print(label); pw.print(" #"); pw.print(i);
527 pw.print(": "); pw.println(a);
Dianne Hackborn043fcd92010-10-06 14:27:34 -0700528 a.dump(pw, prefix + " ", now);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800529 }
530 }
531
532 private native int init();
533 private native void close(int fd);
Jeff Brown11c5f1a2010-03-31 15:29:40 -0700534 private native void set(int fd, int type, long seconds, long nanoseconds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800535 private native int waitForAlarm(int fd);
536 private native int setKernelTimezone(int fd, int minuteswest);
537
538 private void triggerAlarmsLocked(ArrayList<Alarm> alarmList,
539 ArrayList<Alarm> triggerList,
540 long now)
541 {
542 Iterator<Alarm> it = alarmList.iterator();
543 ArrayList<Alarm> repeats = new ArrayList<Alarm>();
544
545 while (it.hasNext())
546 {
547 Alarm alarm = it.next();
548
Joe Onorato8a9b2202010-02-26 18:56:32 -0800549 if (localLOGV) Slog.v(TAG, "Checking active alarm when=" + alarm.when + " " + alarm);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800550
551 if (alarm.when > now) {
552 // don't fire alarms in the future
553 break;
554 }
555
556 // If the alarm is late, then print a warning message.
557 // Note that this can happen if the user creates a new event on
558 // the Calendar app with a reminder that is in the past. In that
559 // case, the reminder alarm will fire immediately.
560 if (localLOGV && now - alarm.when > LATE_ALARM_THRESHOLD) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800561 Slog.v(TAG, "alarm is late! alarm time: " + alarm.when
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800562 + " now: " + now + " delay (in seconds): "
563 + (now - alarm.when) / 1000);
564 }
565
566 // Recurring alarms may have passed several alarm intervals while the
567 // phone was asleep or off, so pass a trigger count when sending them.
Joe Onorato8a9b2202010-02-26 18:56:32 -0800568 if (localLOGV) Slog.v(TAG, "Alarm triggering: " + alarm);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800569 alarm.count = 1;
570 if (alarm.repeatInterval > 0) {
571 // this adjustment will be zero if we're late by
572 // less than one full repeat interval
573 alarm.count += (now - alarm.when) / alarm.repeatInterval;
574 }
575 triggerList.add(alarm);
576
577 // remove the alarm from the list
578 it.remove();
579
580 // if it repeats queue it up to be read-added to the list
581 if (alarm.repeatInterval > 0) {
582 repeats.add(alarm);
583 }
584 }
585
586 // reset any repeating alarms.
587 it = repeats.iterator();
588 while (it.hasNext()) {
589 Alarm alarm = it.next();
590 alarm.when += alarm.count * alarm.repeatInterval;
591 addAlarmLocked(alarm);
592 }
593
594 if (alarmList.size() > 0) {
595 setLocked(alarmList.get(0));
596 }
597 }
598
599 /**
600 * This Comparator sorts Alarms into increasing time order.
601 */
602 public static class IncreasingTimeOrder implements Comparator<Alarm> {
603 public int compare(Alarm a1, Alarm a2) {
604 long when1 = a1.when;
605 long when2 = a2.when;
606 if (when1 - when2 > 0) {
607 return 1;
608 }
609 if (when1 - when2 < 0) {
610 return -1;
611 }
612 return 0;
613 }
614 }
615
616 private static class Alarm {
617 public int type;
618 public int count;
619 public long when;
620 public long repeatInterval;
621 public PendingIntent operation;
622
623 public Alarm() {
624 when = 0;
625 repeatInterval = 0;
626 operation = null;
627 }
628
629 @Override
630 public String toString()
631 {
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700632 StringBuilder sb = new StringBuilder(128);
633 sb.append("Alarm{");
634 sb.append(Integer.toHexString(System.identityHashCode(this)));
635 sb.append(" type ");
636 sb.append(type);
637 sb.append(" ");
638 sb.append(operation.getTargetPackage());
639 sb.append('}');
640 return sb.toString();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800641 }
642
Dianne Hackborn043fcd92010-10-06 14:27:34 -0700643 public void dump(PrintWriter pw, String prefix, long now) {
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700644 pw.print(prefix); pw.print("type="); pw.print(type);
Dianne Hackborn043fcd92010-10-06 14:27:34 -0700645 pw.print(" when="); TimeUtils.formatDuration(when, now, pw);
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700646 pw.print(" repeatInterval="); pw.print(repeatInterval);
647 pw.print(" count="); pw.println(count);
648 pw.print(prefix); pw.print("operation="); pw.println(operation);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800649 }
650 }
651
652 private class AlarmThread extends Thread
653 {
654 public AlarmThread()
655 {
656 super("AlarmManager");
657 }
658
659 public void run()
660 {
661 while (true)
662 {
663 int result = waitForAlarm(mDescriptor);
664
665 ArrayList<Alarm> triggerList = new ArrayList<Alarm>();
666
667 if ((result & TIME_CHANGED_MASK) != 0) {
668 remove(mTimeTickSender);
669 mClockReceiver.scheduleTimeTickEvent();
Dianne Hackborn1c633fc2009-12-08 19:45:14 -0800670 Intent intent = new Intent(Intent.ACTION_TIME_CHANGED);
Dianne Hackborn89ba6752011-01-23 16:51:16 -0800671 intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING
672 | Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
Dianne Hackborn5ac72a22012-08-29 18:32:08 -0700673 mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800674 }
675
676 synchronized (mLock) {
677 final long nowRTC = System.currentTimeMillis();
678 final long nowELAPSED = SystemClock.elapsedRealtime();
Joe Onorato8a9b2202010-02-26 18:56:32 -0800679 if (localLOGV) Slog.v(
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800680 TAG, "Checking for alarms... rtc=" + nowRTC
681 + ", elapsed=" + nowELAPSED);
682
683 if ((result & RTC_WAKEUP_MASK) != 0)
684 triggerAlarmsLocked(mRtcWakeupAlarms, triggerList, nowRTC);
685
686 if ((result & RTC_MASK) != 0)
687 triggerAlarmsLocked(mRtcAlarms, triggerList, nowRTC);
688
689 if ((result & ELAPSED_REALTIME_WAKEUP_MASK) != 0)
690 triggerAlarmsLocked(mElapsedRealtimeWakeupAlarms, triggerList, nowELAPSED);
691
692 if ((result & ELAPSED_REALTIME_MASK) != 0)
693 triggerAlarmsLocked(mElapsedRealtimeAlarms, triggerList, nowELAPSED);
694
695 // now trigger the alarms
696 Iterator<Alarm> it = triggerList.iterator();
697 while (it.hasNext()) {
698 Alarm alarm = it.next();
699 try {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800700 if (localLOGV) Slog.v(TAG, "sending alarm " + alarm);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800701 alarm.operation.send(mContext, 0,
702 mBackgroundIntent.putExtra(
703 Intent.EXTRA_ALARM_COUNT, alarm.count),
704 mResultReceiver, mHandler);
705
Christopher Tatec4a07d12012-04-06 14:19:13 -0700706 // we have an active broadcast so stay awake.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800707 if (mBroadcastRefCount == 0) {
Christopher Tatec4a07d12012-04-06 14:19:13 -0700708 setWakelockWorkSource(alarm.operation);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800709 mWakeLock.acquire();
710 }
Christopher Tatec4a07d12012-04-06 14:19:13 -0700711 mInFlight.add(alarm.operation);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800712 mBroadcastRefCount++;
713
714 BroadcastStats bs = getStatsLocked(alarm.operation);
715 if (bs.nesting == 0) {
716 bs.startTime = nowELAPSED;
717 } else {
718 bs.nesting++;
719 }
720 if (alarm.type == AlarmManager.ELAPSED_REALTIME_WAKEUP
721 || alarm.type == AlarmManager.RTC_WAKEUP) {
722 bs.numWakeup++;
723 ActivityManagerNative.noteWakeupAlarm(
724 alarm.operation);
725 }
726 } catch (PendingIntent.CanceledException e) {
727 if (alarm.repeatInterval > 0) {
728 // This IntentSender is no longer valid, but this
729 // is a repeating alarm, so toss the hoser.
730 remove(alarm.operation);
731 }
732 } catch (RuntimeException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800733 Slog.w(TAG, "Failure sending alarm.", e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800734 }
735 }
736 }
737 }
738 }
739 }
Christopher Tatec4a07d12012-04-06 14:19:13 -0700740
741 void setWakelockWorkSource(PendingIntent pi) {
742 try {
743 final int uid = ActivityManagerNative.getDefault()
744 .getUidForIntentSender(pi.getTarget());
745 if (uid >= 0) {
746 mWakeLock.setWorkSource(new WorkSource(uid));
747 return;
748 }
749 } catch (Exception e) {
750 }
751
752 // Something went wrong; fall back to attributing the lock to the OS
753 mWakeLock.setWorkSource(null);
754 }
755
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800756 private class AlarmHandler extends Handler {
757 public static final int ALARM_EVENT = 1;
758 public static final int MINUTE_CHANGE_EVENT = 2;
759 public static final int DATE_CHANGE_EVENT = 3;
760
761 public AlarmHandler() {
762 }
763
764 public void handleMessage(Message msg) {
765 if (msg.what == ALARM_EVENT) {
766 ArrayList<Alarm> triggerList = new ArrayList<Alarm>();
767 synchronized (mLock) {
768 final long nowRTC = System.currentTimeMillis();
769 triggerAlarmsLocked(mRtcWakeupAlarms, triggerList, nowRTC);
770 triggerAlarmsLocked(mRtcAlarms, triggerList, nowRTC);
771 triggerAlarmsLocked(mElapsedRealtimeWakeupAlarms, triggerList, nowRTC);
772 triggerAlarmsLocked(mElapsedRealtimeAlarms, triggerList, nowRTC);
773 }
774
775 // now trigger the alarms without the lock held
776 Iterator<Alarm> it = triggerList.iterator();
777 while (it.hasNext())
778 {
779 Alarm alarm = it.next();
780 try {
781 alarm.operation.send();
782 } catch (PendingIntent.CanceledException e) {
783 if (alarm.repeatInterval > 0) {
784 // This IntentSender is no longer valid, but this
785 // is a repeating alarm, so toss the hoser.
786 remove(alarm.operation);
787 }
788 }
789 }
790 }
791 }
792 }
793
794 class ClockReceiver extends BroadcastReceiver {
795 public ClockReceiver() {
796 IntentFilter filter = new IntentFilter();
797 filter.addAction(Intent.ACTION_TIME_TICK);
798 filter.addAction(Intent.ACTION_DATE_CHANGED);
799 mContext.registerReceiver(this, filter);
800 }
801
802 @Override
803 public void onReceive(Context context, Intent intent) {
804 if (intent.getAction().equals(Intent.ACTION_TIME_TICK)) {
805 scheduleTimeTickEvent();
806 } else if (intent.getAction().equals(Intent.ACTION_DATE_CHANGED)) {
807 // Since the kernel does not keep track of DST, we need to
808 // reset the TZ information at the beginning of each day
809 // based off of the current Zone gmt offset + userspace tracked
810 // daylight savings information.
811 TimeZone zone = TimeZone.getTimeZone(SystemProperties.get(TIMEZONE_PROPERTY));
Lavettacn Xiaoc84cc4f2010-08-30 12:47:23 +0200812 int gmtOffset = zone.getOffset(System.currentTimeMillis());
813 setKernelTimezone(mDescriptor, -(gmtOffset / 60000));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800814 scheduleDateChangedEvent();
815 }
816 }
817
818 public void scheduleTimeTickEvent() {
Paul Westbrook51608a52011-08-25 13:18:54 -0700819 final long currentTime = System.currentTimeMillis();
Sungmin Choi563914a2013-01-10 17:28:40 +0900820 final long nextTime = 60000 * ((currentTime / 60000) + 1);
Paul Westbrook51608a52011-08-25 13:18:54 -0700821
822 // Schedule this event for the amount of time that it would take to get to
823 // the top of the next minute.
Sungmin Choi563914a2013-01-10 17:28:40 +0900824 final long tickEventDelay = nextTime - currentTime;
Paul Westbrook51608a52011-08-25 13:18:54 -0700825
826 set(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime() + tickEventDelay,
827 mTimeTickSender);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800828 }
829
830 public void scheduleDateChangedEvent() {
831 Calendar calendar = Calendar.getInstance();
832 calendar.setTimeInMillis(System.currentTimeMillis());
833 calendar.set(Calendar.HOUR, 0);
834 calendar.set(Calendar.MINUTE, 0);
835 calendar.set(Calendar.SECOND, 0);
836 calendar.set(Calendar.MILLISECOND, 0);
837 calendar.add(Calendar.DAY_OF_MONTH, 1);
838
839 set(AlarmManager.RTC, calendar.getTimeInMillis(), mDateChangeSender);
840 }
841 }
842
843 class UninstallReceiver extends BroadcastReceiver {
844 public UninstallReceiver() {
845 IntentFilter filter = new IntentFilter();
846 filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
847 filter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800848 filter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800849 filter.addDataScheme("package");
850 mContext.registerReceiver(this, filter);
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800851 // Register for events related to sdcard installation.
852 IntentFilter sdFilter = new IntentFilter();
Suchi Amalapurapub56ae202010-02-04 22:51:07 -0800853 sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
Dianne Hackborn80a4af22012-08-27 19:18:31 -0700854 sdFilter.addAction(Intent.ACTION_USER_STOPPED);
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800855 mContext.registerReceiver(this, sdFilter);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800856 }
857
858 @Override
859 public void onReceive(Context context, Intent intent) {
860 synchronized (mLock) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800861 String action = intent.getAction();
862 String pkgList[] = null;
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800863 if (Intent.ACTION_QUERY_PACKAGE_RESTART.equals(action)) {
864 pkgList = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);
865 for (String packageName : pkgList) {
866 if (lookForPackageLocked(packageName)) {
867 setResultCode(Activity.RESULT_OK);
868 return;
869 }
870 }
871 return;
872 } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800873 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
Dianne Hackborn80a4af22012-08-27 19:18:31 -0700874 } else if (Intent.ACTION_USER_STOPPED.equals(action)) {
875 int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
876 if (userHandle >= 0) {
877 removeUserLocked(userHandle);
878 }
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800879 } else {
Dianne Hackborn409578f2010-03-10 17:23:43 -0800880 if (Intent.ACTION_PACKAGE_REMOVED.equals(action)
881 && intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
882 // This package is being updated; don't kill its alarms.
883 return;
884 }
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800885 Uri data = intent.getData();
886 if (data != null) {
887 String pkg = data.getSchemeSpecificPart();
888 if (pkg != null) {
889 pkgList = new String[]{pkg};
890 }
891 }
892 }
893 if (pkgList != null && (pkgList.length > 0)) {
894 for (String pkg : pkgList) {
895 removeLocked(pkg);
896 mBroadcastStats.remove(pkg);
897 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800898 }
899 }
900 }
901 }
902
903 private final BroadcastStats getStatsLocked(PendingIntent pi) {
904 String pkg = pi.getTargetPackage();
905 BroadcastStats bs = mBroadcastStats.get(pkg);
906 if (bs == null) {
907 bs = new BroadcastStats();
908 mBroadcastStats.put(pkg, bs);
909 }
910 return bs;
911 }
912
913 class ResultReceiver implements PendingIntent.OnFinished {
914 public void onSendFinished(PendingIntent pi, Intent intent, int resultCode,
915 String resultData, Bundle resultExtras) {
916 synchronized (mLock) {
917 BroadcastStats bs = getStatsLocked(pi);
918 if (bs != null) {
919 bs.nesting--;
920 if (bs.nesting <= 0) {
921 bs.nesting = 0;
922 bs.aggregateTime += SystemClock.elapsedRealtime()
923 - bs.startTime;
924 Intent.FilterComparison fc = new Intent.FilterComparison(intent);
925 FilterStats fs = bs.filterStats.get(fc);
926 if (fs == null) {
927 fs = new FilterStats();
928 bs.filterStats.put(fc, fs);
929 }
930 fs.count++;
931 }
932 }
Christopher Tatec4a07d12012-04-06 14:19:13 -0700933 mInFlight.removeFirst();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800934 mBroadcastRefCount--;
935 if (mBroadcastRefCount == 0) {
936 mWakeLock.release();
Christopher Tatec4a07d12012-04-06 14:19:13 -0700937 } else {
938 // the next of our alarms is now in flight. reattribute the wakelock.
939 final PendingIntent nowInFlight = mInFlight.peekFirst();
940 if (nowInFlight != null) {
941 setWakelockWorkSource(nowInFlight);
942 } else {
943 // should never happen
944 Slog.e(TAG, "Alarm wakelock still held but sent queue empty");
945 mWakeLock.setWorkSource(null);
946 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800947 }
948 }
949 }
950 }
951}