auto import from //depot/cupcake/@135843
diff --git a/services/java/com/android/server/AlarmManagerService.java b/services/java/com/android/server/AlarmManagerService.java
new file mode 100644
index 0000000..d66c6e5
--- /dev/null
+++ b/services/java/com/android/server/AlarmManagerService.java
@@ -0,0 +1,808 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import android.app.ActivityManagerNative;
+import android.app.AlarmManager;
+import android.app.IAlarmManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.os.PowerManager;
+import android.os.SystemClock;
+import android.os.SystemProperties;
+import android.text.TextUtils;
+import android.text.format.Time;
+import android.util.EventLog;
+import android.util.Log;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.TimeZone;
+
+class AlarmManagerService extends IAlarmManager.Stub {
+ // The threshold for how long an alarm can be late before we print a
+ // warning message. The time duration is in milliseconds.
+ private static final long LATE_ALARM_THRESHOLD = 10 * 1000;
+
+ private static final int RTC_WAKEUP_MASK = 1 << AlarmManager.RTC_WAKEUP;
+ private static final int RTC_MASK = 1 << AlarmManager.RTC;
+ private static final int ELAPSED_REALTIME_WAKEUP_MASK = 1 << AlarmManager.ELAPSED_REALTIME_WAKEUP;
+ private static final int ELAPSED_REALTIME_MASK = 1 << AlarmManager.ELAPSED_REALTIME;
+ private static final int TIME_CHANGED_MASK = 1 << 16;
+
+ private static final String TAG = "AlarmManager";
+ private static final String ClockReceiver_TAG = "ClockReceiver";
+ private static final boolean localLOGV = false;
+ private static final int ALARM_EVENT = 1;
+ private static final String TIMEZONE_PROPERTY = "persist.sys.timezone";
+
+ private static final Intent mBackgroundIntent
+ = new Intent().addFlags(Intent.FLAG_FROM_BACKGROUND);
+
+ private final Context mContext;
+
+ private Object mLock = new Object();
+
+ private final ArrayList<Alarm> mRtcWakeupAlarms = new ArrayList<Alarm>();
+ private final ArrayList<Alarm> mRtcAlarms = new ArrayList<Alarm>();
+ private final ArrayList<Alarm> mElapsedRealtimeWakeupAlarms = new ArrayList<Alarm>();
+ private final ArrayList<Alarm> mElapsedRealtimeAlarms = new ArrayList<Alarm>();
+ private final IncreasingTimeOrder mIncreasingTimeOrder = new IncreasingTimeOrder();
+
+ // slots corresponding with the inexact-repeat interval buckets,
+ // ordered from shortest to longest
+ private static final long sInexactSlotIntervals[] = {
+ AlarmManager.INTERVAL_FIFTEEN_MINUTES,
+ AlarmManager.INTERVAL_HALF_HOUR,
+ AlarmManager.INTERVAL_HOUR,
+ AlarmManager.INTERVAL_HALF_DAY,
+ AlarmManager.INTERVAL_DAY
+ };
+ private long mInexactDeliveryTimes[] = { 0, 0, 0, 0, 0};
+
+ private int mDescriptor;
+ private int mBroadcastRefCount = 0;
+ private PowerManager.WakeLock mWakeLock;
+ private final AlarmThread mWaitThread = new AlarmThread();
+ private final AlarmHandler mHandler = new AlarmHandler();
+ private ClockReceiver mClockReceiver;
+ private UninstallReceiver mUninstallReceiver;
+ private final ResultReceiver mResultReceiver = new ResultReceiver();
+ private final PendingIntent mTimeTickSender;
+ private final PendingIntent mDateChangeSender;
+
+ private static final class FilterStats {
+ int count;
+ }
+
+ private static final class BroadcastStats {
+ long aggregateTime;
+ int numWakeup;
+ long startTime;
+ int nesting;
+ HashMap<Intent.FilterComparison, FilterStats> filterStats
+ = new HashMap<Intent.FilterComparison, FilterStats>();
+ }
+
+ private final HashMap<String, BroadcastStats> mBroadcastStats
+ = new HashMap<String, BroadcastStats>();
+
+ public AlarmManagerService(Context context) {
+ mContext = context;
+ mDescriptor = init();
+ PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
+ mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
+
+ mTimeTickSender = PendingIntent.getBroadcast(context, 0,
+ new Intent(Intent.ACTION_TIME_TICK).addFlags(
+ Intent.FLAG_RECEIVER_REGISTERED_ONLY), 0);
+ mDateChangeSender = PendingIntent.getBroadcast(context, 0,
+ new Intent(Intent.ACTION_DATE_CHANGED), 0);
+
+ // now that we have initied the driver schedule the alarm
+ mClockReceiver= new ClockReceiver();
+ mClockReceiver.scheduleTimeTickEvent();
+ mClockReceiver.scheduleDateChangedEvent();
+ mUninstallReceiver = new UninstallReceiver();
+
+ if (mDescriptor != -1) {
+ mWaitThread.start();
+ } else {
+ Log.w(TAG, "Failed to open alarm driver. Falling back to a handler.");
+ }
+ }
+
+ protected void finalize() throws Throwable {
+ try {
+ close(mDescriptor);
+ } finally {
+ super.finalize();
+ }
+ }
+
+ public void set(int type, long triggerAtTime, PendingIntent operation) {
+ setRepeating(type, triggerAtTime, 0, operation);
+ }
+
+ public void setRepeating(int type, long triggerAtTime, long interval,
+ PendingIntent operation) {
+ if (operation == null) {
+ Log.w(TAG, "set/setRepeating ignored because there is no intent");
+ return;
+ }
+ synchronized (mLock) {
+ Alarm alarm = new Alarm();
+ alarm.type = type;
+ alarm.when = triggerAtTime;
+ alarm.repeatInterval = interval;
+ alarm.operation = operation;
+
+ // Remove this alarm if already scheduled.
+ removeLocked(operation);
+
+ if (localLOGV) Log.v(TAG, "set: " + alarm);
+
+ int index = addAlarmLocked(alarm);
+ if (index == 0) {
+ setLocked(alarm);
+ }
+ }
+ }
+
+ public void setInexactRepeating(int type, long triggerAtTime, long interval,
+ PendingIntent operation) {
+ if (operation == null) {
+ Log.w(TAG, "setInexactRepeating ignored because there is no intent");
+ return;
+ }
+
+ // find the slot in the delivery-times array that we will use
+ int intervalSlot;
+ for (intervalSlot = 0; intervalSlot < sInexactSlotIntervals.length; intervalSlot++) {
+ if (sInexactSlotIntervals[intervalSlot] == interval) {
+ break;
+ }
+ }
+
+ // Non-bucket intervals just fall back to the less-efficient
+ // unbucketed recurring alarm implementation
+ if (intervalSlot >= sInexactSlotIntervals.length) {
+ setRepeating(type, triggerAtTime, interval, operation);
+ return;
+ }
+
+ // Align bucketed alarm deliveries by trying to match
+ // the shortest-interval bucket already scheduled
+ long bucketTime = 0;
+ for (int slot = 0; slot < mInexactDeliveryTimes.length; slot++) {
+ if (mInexactDeliveryTimes[slot] > 0) {
+ bucketTime = mInexactDeliveryTimes[slot];
+ break;
+ }
+ }
+
+ if (bucketTime == 0) {
+ // If nothing is scheduled yet, just start at the requested time
+ bucketTime = triggerAtTime;
+ } else {
+ // Align the new alarm with the existing bucketed sequence. To achieve
+ // alignment, we slide the start time around by min{interval, slot interval}
+ long adjustment = (interval <= sInexactSlotIntervals[intervalSlot])
+ ? interval : sInexactSlotIntervals[intervalSlot];
+
+ // The bucket may have started in the past; adjust
+ while (bucketTime < triggerAtTime) {
+ bucketTime += adjustment;
+ }
+
+ // Or the bucket may be set to start more than an interval beyond
+ // our requested trigger time; pull it back to meet our needs
+ while (bucketTime > triggerAtTime + adjustment) {
+ bucketTime -= adjustment;
+ }
+ }
+
+ // Remember where this bucket started (reducing the amount of later
+ // fixup required) and set the alarm with the new, bucketed start time.
+ if (localLOGV) Log.v(TAG, "setInexactRepeating: interval=" + interval
+ + " bucketTime=" + bucketTime);
+ mInexactDeliveryTimes[intervalSlot] = bucketTime;
+ setRepeating(type, bucketTime, interval, operation);
+ }
+
+ public void setTimeZone(String tz) {
+ mContext.enforceCallingOrSelfPermission(
+ "android.permission.SET_TIME_ZONE",
+ "setTimeZone");
+
+ if (TextUtils.isEmpty(tz)) return;
+ TimeZone zone = TimeZone.getTimeZone(tz);
+ // Prevent reentrant calls from stepping on each other when writing
+ // the time zone property
+ boolean timeZoneWasChanged = false;
+ synchronized (this) {
+ String current = SystemProperties.get(TIMEZONE_PROPERTY);
+ if (current == null || !current.equals(zone.getID())) {
+ if (localLOGV) Log.v(TAG, "timezone changed: " + current + ", new=" + zone.getID());
+ timeZoneWasChanged = true;
+ SystemProperties.set(TIMEZONE_PROPERTY, zone.getID());
+ }
+
+ // Update the kernel timezone information
+ // Kernel tracks time offsets as 'minutes west of GMT'
+ int gmtOffset = (zone.getRawOffset() + zone.getDSTSavings()) / 60000;
+ setKernelTimezone(mDescriptor, -(gmtOffset));
+ }
+
+ TimeZone.setDefault(null);
+
+ if (timeZoneWasChanged) {
+ Intent intent = new Intent(Intent.ACTION_TIMEZONE_CHANGED);
+ intent.putExtra("time-zone", zone.getID());
+ mContext.sendBroadcast(intent);
+ }
+ }
+
+ public void remove(PendingIntent operation) {
+ if (operation == null) {
+ return;
+ }
+ synchronized (mLock) {
+ removeLocked(operation);
+ }
+ }
+
+ public void removeLocked(PendingIntent operation) {
+ removeLocked(mRtcWakeupAlarms, operation);
+ removeLocked(mRtcAlarms, operation);
+ removeLocked(mElapsedRealtimeWakeupAlarms, operation);
+ removeLocked(mElapsedRealtimeAlarms, operation);
+ }
+
+ private void removeLocked(ArrayList<Alarm> alarmList,
+ PendingIntent operation) {
+ if (alarmList.size() <= 0) {
+ return;
+ }
+
+ // iterator over the list removing any it where the intent match
+ Iterator<Alarm> it = alarmList.iterator();
+
+ while (it.hasNext()) {
+ Alarm alarm = it.next();
+ if (alarm.operation.equals(operation)) {
+ it.remove();
+ }
+ }
+ }
+
+ public void removeLocked(String packageName) {
+ removeLocked(mRtcWakeupAlarms, packageName);
+ removeLocked(mRtcAlarms, packageName);
+ removeLocked(mElapsedRealtimeWakeupAlarms, packageName);
+ removeLocked(mElapsedRealtimeAlarms, packageName);
+ }
+
+ private void removeLocked(ArrayList<Alarm> alarmList,
+ String packageName) {
+ if (alarmList.size() <= 0) {
+ return;
+ }
+
+ // iterator over the list removing any it where the intent match
+ Iterator<Alarm> it = alarmList.iterator();
+
+ while (it.hasNext()) {
+ Alarm alarm = it.next();
+ if (alarm.operation.getTargetPackage().equals(packageName)) {
+ it.remove();
+ }
+ }
+ }
+
+ private ArrayList<Alarm> getAlarmList(int type) {
+ switch (type) {
+ case AlarmManager.RTC_WAKEUP: return mRtcWakeupAlarms;
+ case AlarmManager.RTC: return mRtcAlarms;
+ case AlarmManager.ELAPSED_REALTIME_WAKEUP: return mElapsedRealtimeWakeupAlarms;
+ case AlarmManager.ELAPSED_REALTIME: return mElapsedRealtimeAlarms;
+ }
+
+ return null;
+ }
+
+ private int addAlarmLocked(Alarm alarm) {
+ ArrayList<Alarm> alarmList = getAlarmList(alarm.type);
+
+ int index = Collections.binarySearch(alarmList, alarm, mIncreasingTimeOrder);
+ if (index < 0) {
+ index = 0 - index - 1;
+ }
+ if (localLOGV) Log.v(TAG, "Adding alarm " + alarm + " at " + index);
+ alarmList.add(index, alarm);
+
+ if (localLOGV) {
+ // Display the list of alarms for this alarm type
+ Log.v(TAG, "alarms: " + alarmList.size() + " type: " + alarm.type);
+ int position = 0;
+ for (Alarm a : alarmList) {
+ Time time = new Time();
+ time.set(a.when);
+ String timeStr = time.format("%b %d %I:%M:%S %p");
+ Log.v(TAG, position + ": " + timeStr
+ + " " + a.operation.getTargetPackage());
+ position += 1;
+ }
+ }
+
+ return index;
+ }
+
+ public long timeToNextAlarm() {
+ long nextAlarm = 0xfffffffffffffffl;
+ synchronized (mLock) {
+ for (int i=AlarmManager.RTC_WAKEUP;
+ i<=AlarmManager.ELAPSED_REALTIME; i++) {
+ ArrayList<Alarm> alarmList = getAlarmList(i);
+ if (alarmList.size() > 0) {
+ Alarm a = alarmList.get(0);
+ if (a.when < nextAlarm) {
+ nextAlarm = a.when;
+ }
+ }
+ }
+ }
+ return nextAlarm;
+ }
+
+ private void setLocked(Alarm alarm)
+ {
+ if (mDescriptor != -1)
+ {
+ set(mDescriptor, alarm.type, (alarm.when * 1000 * 1000));
+ }
+ else
+ {
+ Message msg = Message.obtain();
+ msg.what = ALARM_EVENT;
+
+ mHandler.removeMessages(ALARM_EVENT);
+ mHandler.sendMessageAtTime(msg, alarm.when);
+ }
+ }
+
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
+ != PackageManager.PERMISSION_GRANTED) {
+ pw.println("Permission Denial: can't dump AlarmManager from from pid="
+ + Binder.getCallingPid()
+ + ", uid=" + Binder.getCallingUid());
+ return;
+ }
+
+ synchronized (mLock) {
+ pw.println("Current Alarm Manager state:");
+ if (mRtcWakeupAlarms.size() > 0) {
+ pw.println(" ");
+ pw.println(" Realtime wakeup alarms that are scheduled:");
+ dumpAlarmList(pw, mRtcWakeupAlarms, " ", "RTC_WAKEUP");
+ }
+ if (mRtcAlarms.size() > 0) {
+ pw.println(" ");
+ pw.println(" Realtime alarms that are scheduled:");
+ dumpAlarmList(pw, mRtcAlarms, " ", "RTC");
+ }
+ if (mElapsedRealtimeWakeupAlarms.size() > 0) {
+ pw.println(" ");
+ pw.println(" Elapsed realtime wakeup alarms that are scheduled:");
+ dumpAlarmList(pw, mElapsedRealtimeWakeupAlarms, " ", "ELAPSED_REALTIME_WAKEUP");
+ }
+ if (mElapsedRealtimeAlarms.size() > 0) {
+ pw.println(" ");
+ pw.println(" Elapsed realtime alarms that are scheduled:");
+ dumpAlarmList(pw, mElapsedRealtimeAlarms, " ", "ELAPSED_REALTIME");
+ }
+
+ pw.println(" ");
+ pw.println(" Broadcast ref count: " + mBroadcastRefCount);
+
+ pw.println(" ");
+ pw.println(" Alarm Stats:");
+ for (Map.Entry<String, BroadcastStats> be : mBroadcastStats.entrySet()) {
+ BroadcastStats bs = be.getValue();
+ pw.println(" " + be.getKey());
+ pw.println(" " + bs.aggregateTime + "ms running, "
+ + bs.numWakeup + " wakeups");
+ for (Map.Entry<Intent.FilterComparison, FilterStats> fe
+ : bs.filterStats.entrySet()) {
+ pw.println(" " + fe.getValue().count + " alarms: "
+ + fe.getKey().getIntent());
+ }
+ }
+ }
+ }
+
+ private static final void dumpAlarmList(PrintWriter pw, ArrayList<Alarm> list, String prefix, String label) {
+ for (int i=list.size()-1; i>=0; i--) {
+ Alarm a = list.get(i);
+ pw.println(prefix + label + " #" + i + ":");
+ a.dump(pw, prefix + " ");
+ }
+ }
+
+ private native int init();
+ private native void close(int fd);
+ private native void set(int fd, int type, long nanoseconds);
+ private native int waitForAlarm(int fd);
+ private native int setKernelTimezone(int fd, int minuteswest);
+
+ private void triggerAlarmsLocked(ArrayList<Alarm> alarmList,
+ ArrayList<Alarm> triggerList,
+ long now)
+ {
+ Iterator<Alarm> it = alarmList.iterator();
+ ArrayList<Alarm> repeats = new ArrayList<Alarm>();
+
+ while (it.hasNext())
+ {
+ Alarm alarm = it.next();
+
+ if (localLOGV) Log.v(TAG, "Checking active alarm when=" + alarm.when + " " + alarm);
+
+ if (alarm.when > now) {
+ // don't fire alarms in the future
+ break;
+ }
+
+ // If the alarm is late, then print a warning message.
+ // Note that this can happen if the user creates a new event on
+ // the Calendar app with a reminder that is in the past. In that
+ // case, the reminder alarm will fire immediately.
+ if (localLOGV && now - alarm.when > LATE_ALARM_THRESHOLD) {
+ Log.v(TAG, "alarm is late! alarm time: " + alarm.when
+ + " now: " + now + " delay (in seconds): "
+ + (now - alarm.when) / 1000);
+ }
+
+ // Recurring alarms may have passed several alarm intervals while the
+ // phone was asleep or off, so pass a trigger count when sending them.
+ if (localLOGV) Log.v(TAG, "Alarm triggering: " + alarm);
+ alarm.count = 1;
+ if (alarm.repeatInterval > 0) {
+ // this adjustment will be zero if we're late by
+ // less than one full repeat interval
+ alarm.count += (now - alarm.when) / alarm.repeatInterval;
+ }
+ triggerList.add(alarm);
+
+ // remove the alarm from the list
+ it.remove();
+
+ // if it repeats queue it up to be read-added to the list
+ if (alarm.repeatInterval > 0) {
+ repeats.add(alarm);
+ }
+ }
+
+ // reset any repeating alarms.
+ it = repeats.iterator();
+ while (it.hasNext()) {
+ Alarm alarm = it.next();
+ alarm.when += alarm.count * alarm.repeatInterval;
+ addAlarmLocked(alarm);
+ }
+
+ if (alarmList.size() > 0) {
+ setLocked(alarmList.get(0));
+ }
+ }
+
+ /**
+ * This Comparator sorts Alarms into increasing time order.
+ */
+ public static class IncreasingTimeOrder implements Comparator<Alarm> {
+ public int compare(Alarm a1, Alarm a2) {
+ long when1 = a1.when;
+ long when2 = a2.when;
+ if (when1 - when2 > 0) {
+ return 1;
+ }
+ if (when1 - when2 < 0) {
+ return -1;
+ }
+ return 0;
+ }
+ }
+
+ private static class Alarm {
+ public int type;
+ public int count;
+ public long when;
+ public long repeatInterval;
+ public PendingIntent operation;
+
+ public Alarm() {
+ when = 0;
+ repeatInterval = 0;
+ operation = null;
+ }
+
+ @Override
+ public String toString()
+ {
+ return "Alarm{"
+ + Integer.toHexString(System.identityHashCode(this))
+ + " type " + type + " " + operation.getTargetPackage() + "}";
+ }
+
+ public void dump(PrintWriter pw, String prefix)
+ {
+ pw.println(prefix + this);
+ pw.println(prefix + "type=" + type + " when=" + when
+ + " repeatInterval=" + repeatInterval
+ + " count=" + count);
+ pw.println(prefix + "operation=" + operation);
+ }
+ }
+
+ private class AlarmThread extends Thread
+ {
+ public AlarmThread()
+ {
+ super("AlarmManager");
+ }
+
+ public void run()
+ {
+ while (true)
+ {
+ int result = waitForAlarm(mDescriptor);
+
+ ArrayList<Alarm> triggerList = new ArrayList<Alarm>();
+
+ if ((result & TIME_CHANGED_MASK) != 0) {
+ remove(mTimeTickSender);
+ mClockReceiver.scheduleTimeTickEvent();
+ mContext.sendBroadcast(new Intent(Intent.ACTION_TIME_CHANGED));
+ }
+
+ synchronized (mLock) {
+ final long nowRTC = System.currentTimeMillis();
+ final long nowELAPSED = SystemClock.elapsedRealtime();
+ if (localLOGV) Log.v(
+ TAG, "Checking for alarms... rtc=" + nowRTC
+ + ", elapsed=" + nowELAPSED);
+
+ if ((result & RTC_WAKEUP_MASK) != 0)
+ triggerAlarmsLocked(mRtcWakeupAlarms, triggerList, nowRTC);
+
+ if ((result & RTC_MASK) != 0)
+ triggerAlarmsLocked(mRtcAlarms, triggerList, nowRTC);
+
+ if ((result & ELAPSED_REALTIME_WAKEUP_MASK) != 0)
+ triggerAlarmsLocked(mElapsedRealtimeWakeupAlarms, triggerList, nowELAPSED);
+
+ if ((result & ELAPSED_REALTIME_MASK) != 0)
+ triggerAlarmsLocked(mElapsedRealtimeAlarms, triggerList, nowELAPSED);
+
+ // now trigger the alarms
+ Iterator<Alarm> it = triggerList.iterator();
+ while (it.hasNext()) {
+ Alarm alarm = it.next();
+ try {
+ if (localLOGV) Log.v(TAG, "sending alarm " + alarm);
+ alarm.operation.send(mContext, 0,
+ mBackgroundIntent.putExtra(
+ Intent.EXTRA_ALARM_COUNT, alarm.count),
+ mResultReceiver, mHandler);
+
+ // we have an active broadcast so stay awake.
+ if (mBroadcastRefCount == 0) {
+ mWakeLock.acquire();
+ }
+ mBroadcastRefCount++;
+
+ BroadcastStats bs = getStatsLocked(alarm.operation);
+ if (bs.nesting == 0) {
+ bs.startTime = nowELAPSED;
+ } else {
+ bs.nesting++;
+ }
+ if (alarm.type == AlarmManager.ELAPSED_REALTIME_WAKEUP
+ || alarm.type == AlarmManager.RTC_WAKEUP) {
+ bs.numWakeup++;
+ ActivityManagerNative.noteWakeupAlarm(
+ alarm.operation);
+ }
+ } catch (PendingIntent.CanceledException e) {
+ if (alarm.repeatInterval > 0) {
+ // This IntentSender is no longer valid, but this
+ // is a repeating alarm, so toss the hoser.
+ remove(alarm.operation);
+ }
+ } catch (RuntimeException e) {
+ Log.w(TAG, "Failure sending alarm.", e);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private class AlarmHandler extends Handler {
+ public static final int ALARM_EVENT = 1;
+ public static final int MINUTE_CHANGE_EVENT = 2;
+ public static final int DATE_CHANGE_EVENT = 3;
+
+ public AlarmHandler() {
+ }
+
+ public void handleMessage(Message msg) {
+ if (msg.what == ALARM_EVENT) {
+ ArrayList<Alarm> triggerList = new ArrayList<Alarm>();
+ synchronized (mLock) {
+ final long nowRTC = System.currentTimeMillis();
+ triggerAlarmsLocked(mRtcWakeupAlarms, triggerList, nowRTC);
+ triggerAlarmsLocked(mRtcAlarms, triggerList, nowRTC);
+ triggerAlarmsLocked(mElapsedRealtimeWakeupAlarms, triggerList, nowRTC);
+ triggerAlarmsLocked(mElapsedRealtimeAlarms, triggerList, nowRTC);
+ }
+
+ // now trigger the alarms without the lock held
+ Iterator<Alarm> it = triggerList.iterator();
+ while (it.hasNext())
+ {
+ Alarm alarm = it.next();
+ try {
+ alarm.operation.send();
+ } catch (PendingIntent.CanceledException e) {
+ if (alarm.repeatInterval > 0) {
+ // This IntentSender is no longer valid, but this
+ // is a repeating alarm, so toss the hoser.
+ remove(alarm.operation);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ class ClockReceiver extends BroadcastReceiver {
+ public ClockReceiver() {
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_TIME_TICK);
+ filter.addAction(Intent.ACTION_DATE_CHANGED);
+ mContext.registerReceiver(this, filter);
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (intent.getAction().equals(Intent.ACTION_TIME_TICK)) {
+ scheduleTimeTickEvent();
+ } else if (intent.getAction().equals(Intent.ACTION_DATE_CHANGED)) {
+ // Since the kernel does not keep track of DST, we need to
+ // reset the TZ information at the beginning of each day
+ // based off of the current Zone gmt offset + userspace tracked
+ // daylight savings information.
+ TimeZone zone = TimeZone.getTimeZone(SystemProperties.get(TIMEZONE_PROPERTY));
+ int gmtOffset = (zone.getRawOffset() + zone.getDSTSavings()) / 60000;
+
+ setKernelTimezone(mDescriptor, -(gmtOffset));
+ scheduleDateChangedEvent();
+ }
+ }
+
+ public void scheduleTimeTickEvent() {
+ Calendar calendar = Calendar.getInstance();
+ calendar.setTimeInMillis(System.currentTimeMillis());
+ calendar.add(Calendar.MINUTE, 1);
+ calendar.set(Calendar.SECOND, 0);
+ calendar.set(Calendar.MILLISECOND, 0);
+
+ set(AlarmManager.RTC, calendar.getTimeInMillis(), mTimeTickSender);
+ }
+
+ public void scheduleDateChangedEvent() {
+ Calendar calendar = Calendar.getInstance();
+ calendar.setTimeInMillis(System.currentTimeMillis());
+ calendar.set(Calendar.HOUR, 0);
+ calendar.set(Calendar.MINUTE, 0);
+ calendar.set(Calendar.SECOND, 0);
+ calendar.set(Calendar.MILLISECOND, 0);
+ calendar.add(Calendar.DAY_OF_MONTH, 1);
+
+ set(AlarmManager.RTC, calendar.getTimeInMillis(), mDateChangeSender);
+ }
+ }
+
+ class UninstallReceiver extends BroadcastReceiver {
+ public UninstallReceiver() {
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+ filter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
+ filter.addDataScheme("package");
+ mContext.registerReceiver(this, filter);
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ synchronized (mLock) {
+ Uri data = intent.getData();
+ if (data != null) {
+ String pkg = data.getSchemeSpecificPart();
+ removeLocked(pkg);
+ mBroadcastStats.remove(pkg);
+ }
+ }
+ }
+ }
+
+ private final BroadcastStats getStatsLocked(PendingIntent pi) {
+ String pkg = pi.getTargetPackage();
+ BroadcastStats bs = mBroadcastStats.get(pkg);
+ if (bs == null) {
+ bs = new BroadcastStats();
+ mBroadcastStats.put(pkg, bs);
+ }
+ return bs;
+ }
+
+ class ResultReceiver implements PendingIntent.OnFinished {
+ public void onSendFinished(PendingIntent pi, Intent intent, int resultCode,
+ String resultData, Bundle resultExtras) {
+ synchronized (mLock) {
+ BroadcastStats bs = getStatsLocked(pi);
+ if (bs != null) {
+ bs.nesting--;
+ if (bs.nesting <= 0) {
+ bs.nesting = 0;
+ bs.aggregateTime += SystemClock.elapsedRealtime()
+ - bs.startTime;
+ Intent.FilterComparison fc = new Intent.FilterComparison(intent);
+ FilterStats fs = bs.filterStats.get(fc);
+ if (fs == null) {
+ fs = new FilterStats();
+ bs.filterStats.put(fc, fs);
+ }
+ fs.count++;
+ }
+ }
+ mBroadcastRefCount--;
+ if (mBroadcastRefCount == 0) {
+ mWakeLock.release();
+ }
+ }
+ }
+ }
+}