Home | History | Annotate | Download | only in server
      1 /*
      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 
     17 package com.android.server;
     18 
     19 import android.app.Activity;
     20 import android.app.ActivityManagerNative;
     21 import android.app.AlarmManager;
     22 import android.app.IAlarmManager;
     23 import android.app.PendingIntent;
     24 import android.content.BroadcastReceiver;
     25 import android.content.Context;
     26 import android.content.Intent;
     27 import android.content.IntentFilter;
     28 import android.content.pm.PackageManager;
     29 import android.net.Uri;
     30 import android.os.Binder;
     31 import android.os.Bundle;
     32 import android.os.Handler;
     33 import android.os.Message;
     34 import android.os.PowerManager;
     35 import android.os.SystemClock;
     36 import android.os.SystemProperties;
     37 import android.text.TextUtils;
     38 import android.text.format.Time;
     39 import android.util.EventLog;
     40 import android.util.Slog;
     41 import android.util.TimeUtils;
     42 
     43 import java.io.FileDescriptor;
     44 import java.io.PrintWriter;
     45 import java.text.SimpleDateFormat;
     46 import java.util.ArrayList;
     47 import java.util.Calendar;
     48 import java.util.Collections;
     49 import java.util.Comparator;
     50 import java.util.Date;
     51 import java.util.HashMap;
     52 import java.util.Iterator;
     53 import java.util.Map;
     54 import java.util.TimeZone;
     55 
     56 class AlarmManagerService extends IAlarmManager.Stub {
     57     // The threshold for how long an alarm can be late before we print a
     58     // warning message.  The time duration is in milliseconds.
     59     private static final long LATE_ALARM_THRESHOLD = 10 * 1000;
     60 
     61     private static final int RTC_WAKEUP_MASK = 1 << AlarmManager.RTC_WAKEUP;
     62     private static final int RTC_MASK = 1 << AlarmManager.RTC;
     63     private static final int ELAPSED_REALTIME_WAKEUP_MASK = 1 << AlarmManager.ELAPSED_REALTIME_WAKEUP;
     64     private static final int ELAPSED_REALTIME_MASK = 1 << AlarmManager.ELAPSED_REALTIME;
     65     private static final int TIME_CHANGED_MASK = 1 << 16;
     66 
     67     // Alignment quantum for inexact repeating alarms
     68     private static final long QUANTUM = AlarmManager.INTERVAL_FIFTEEN_MINUTES;
     69 
     70     private static final String TAG = "AlarmManager";
     71     private static final String ClockReceiver_TAG = "ClockReceiver";
     72     private static final boolean localLOGV = false;
     73     private static final int ALARM_EVENT = 1;
     74     private static final String TIMEZONE_PROPERTY = "persist.sys.timezone";
     75 
     76     private static final Intent mBackgroundIntent
     77             = new Intent().addFlags(Intent.FLAG_FROM_BACKGROUND);
     78 
     79     private final Context mContext;
     80 
     81     private Object mLock = new Object();
     82 
     83     private final ArrayList<Alarm> mRtcWakeupAlarms = new ArrayList<Alarm>();
     84     private final ArrayList<Alarm> mRtcAlarms = new ArrayList<Alarm>();
     85     private final ArrayList<Alarm> mElapsedRealtimeWakeupAlarms = new ArrayList<Alarm>();
     86     private final ArrayList<Alarm> mElapsedRealtimeAlarms = new ArrayList<Alarm>();
     87     private final IncreasingTimeOrder mIncreasingTimeOrder = new IncreasingTimeOrder();
     88 
     89     private int mDescriptor;
     90     private int mBroadcastRefCount = 0;
     91     private PowerManager.WakeLock mWakeLock;
     92     private final AlarmThread mWaitThread = new AlarmThread();
     93     private final AlarmHandler mHandler = new AlarmHandler();
     94     private ClockReceiver mClockReceiver;
     95     private UninstallReceiver mUninstallReceiver;
     96     private final ResultReceiver mResultReceiver = new ResultReceiver();
     97     private final PendingIntent mTimeTickSender;
     98     private final PendingIntent mDateChangeSender;
     99 
    100     private static final class FilterStats {
    101         int count;
    102     }
    103 
    104     private static final class BroadcastStats {
    105         long aggregateTime;
    106         int numWakeup;
    107         long startTime;
    108         int nesting;
    109         HashMap<Intent.FilterComparison, FilterStats> filterStats
    110                 = new HashMap<Intent.FilterComparison, FilterStats>();
    111     }
    112 
    113     private final HashMap<String, BroadcastStats> mBroadcastStats
    114             = new HashMap<String, BroadcastStats>();
    115 
    116     public AlarmManagerService(Context context) {
    117         mContext = context;
    118         mDescriptor = init();
    119 
    120         // We have to set current TimeZone info to kernel
    121         // because kernel doesn't keep this after reboot
    122         String tz = SystemProperties.get(TIMEZONE_PROPERTY);
    123         if (tz != null) {
    124             setTimeZone(tz);
    125         }
    126 
    127         PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
    128         mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
    129 
    130         mTimeTickSender = PendingIntent.getBroadcast(context, 0,
    131                 new Intent(Intent.ACTION_TIME_TICK).addFlags(
    132                         Intent.FLAG_RECEIVER_REGISTERED_ONLY), 0);
    133         Intent intent = new Intent(Intent.ACTION_DATE_CHANGED);
    134         intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
    135         mDateChangeSender = PendingIntent.getBroadcast(context, 0, intent, 0);
    136 
    137         // now that we have initied the driver schedule the alarm
    138         mClockReceiver= new ClockReceiver();
    139         mClockReceiver.scheduleTimeTickEvent();
    140         mClockReceiver.scheduleDateChangedEvent();
    141         mUninstallReceiver = new UninstallReceiver();
    142 
    143         if (mDescriptor != -1) {
    144             mWaitThread.start();
    145         } else {
    146             Slog.w(TAG, "Failed to open alarm driver. Falling back to a handler.");
    147         }
    148     }
    149 
    150     protected void finalize() throws Throwable {
    151         try {
    152             close(mDescriptor);
    153         } finally {
    154             super.finalize();
    155         }
    156     }
    157 
    158     public void set(int type, long triggerAtTime, PendingIntent operation) {
    159         setRepeating(type, triggerAtTime, 0, operation);
    160     }
    161 
    162     public void setRepeating(int type, long triggerAtTime, long interval,
    163             PendingIntent operation) {
    164         if (operation == null) {
    165             Slog.w(TAG, "set/setRepeating ignored because there is no intent");
    166             return;
    167         }
    168         synchronized (mLock) {
    169             Alarm alarm = new Alarm();
    170             alarm.type = type;
    171             alarm.when = triggerAtTime;
    172             alarm.repeatInterval = interval;
    173             alarm.operation = operation;
    174 
    175             // Remove this alarm if already scheduled.
    176             removeLocked(operation);
    177 
    178             if (localLOGV) Slog.v(TAG, "set: " + alarm);
    179 
    180             int index = addAlarmLocked(alarm);
    181             if (index == 0) {
    182                 setLocked(alarm);
    183             }
    184         }
    185     }
    186 
    187     public void setInexactRepeating(int type, long triggerAtTime, long interval,
    188             PendingIntent operation) {
    189         if (operation == null) {
    190             Slog.w(TAG, "setInexactRepeating ignored because there is no intent");
    191             return;
    192         }
    193 
    194         if (interval <= 0) {
    195             Slog.w(TAG, "setInexactRepeating ignored because interval " + interval
    196                     + " is invalid");
    197             return;
    198         }
    199 
    200         // If the requested interval isn't a multiple of 15 minutes, just treat it as exact
    201         if (interval % QUANTUM != 0) {
    202             if (localLOGV) Slog.v(TAG, "Interval " + interval + " not a quantum multiple");
    203             setRepeating(type, triggerAtTime, interval, operation);
    204             return;
    205         }
    206 
    207         // Translate times into the ELAPSED timebase for alignment purposes so that
    208         // alignment never tries to match against wall clock times.
    209         final boolean isRtc = (type == AlarmManager.RTC || type == AlarmManager.RTC_WAKEUP);
    210         final long skew = (isRtc)
    211                 ? System.currentTimeMillis() - SystemClock.elapsedRealtime()
    212                 : 0;
    213 
    214         // Slip forward to the next ELAPSED-timebase quantum after the stated time.  If
    215         // we're *at* a quantum point, leave it alone.
    216         final long adjustedTriggerTime;
    217         long offset = (triggerAtTime - skew) % QUANTUM;
    218         if (offset != 0) {
    219             adjustedTriggerTime = triggerAtTime - offset + QUANTUM;
    220         } else {
    221             adjustedTriggerTime = triggerAtTime;
    222         }
    223 
    224         // Set the alarm based on the quantum-aligned start time
    225         if (localLOGV) Slog.v(TAG, "setInexactRepeating: type=" + type + " interval=" + interval
    226                 + " trigger=" + adjustedTriggerTime + " orig=" + triggerAtTime);
    227         setRepeating(type, adjustedTriggerTime, interval, operation);
    228     }
    229 
    230     public void setTime(long millis) {
    231         mContext.enforceCallingOrSelfPermission(
    232                 "android.permission.SET_TIME",
    233                 "setTime");
    234 
    235         SystemClock.setCurrentTimeMillis(millis);
    236     }
    237 
    238     public void setTimeZone(String tz) {
    239         mContext.enforceCallingOrSelfPermission(
    240                 "android.permission.SET_TIME_ZONE",
    241                 "setTimeZone");
    242 
    243         if (TextUtils.isEmpty(tz)) return;
    244         TimeZone zone = TimeZone.getTimeZone(tz);
    245         // Prevent reentrant calls from stepping on each other when writing
    246         // the time zone property
    247         boolean timeZoneWasChanged = false;
    248         synchronized (this) {
    249             String current = SystemProperties.get(TIMEZONE_PROPERTY);
    250             if (current == null || !current.equals(zone.getID())) {
    251                 if (localLOGV) Slog.v(TAG, "timezone changed: " + current + ", new=" + zone.getID());
    252                 timeZoneWasChanged = true;
    253                 SystemProperties.set(TIMEZONE_PROPERTY, zone.getID());
    254             }
    255 
    256             // Update the kernel timezone information
    257             // Kernel tracks time offsets as 'minutes west of GMT'
    258             int gmtOffset = zone.getOffset(System.currentTimeMillis());
    259             setKernelTimezone(mDescriptor, -(gmtOffset / 60000));
    260         }
    261 
    262         TimeZone.setDefault(null);
    263 
    264         if (timeZoneWasChanged) {
    265             Intent intent = new Intent(Intent.ACTION_TIMEZONE_CHANGED);
    266             intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
    267             intent.putExtra("time-zone", zone.getID());
    268             mContext.sendBroadcast(intent);
    269         }
    270     }
    271 
    272     public void remove(PendingIntent operation) {
    273         if (operation == null) {
    274             return;
    275         }
    276         synchronized (mLock) {
    277             removeLocked(operation);
    278         }
    279     }
    280 
    281     public void removeLocked(PendingIntent operation) {
    282         removeLocked(mRtcWakeupAlarms, operation);
    283         removeLocked(mRtcAlarms, operation);
    284         removeLocked(mElapsedRealtimeWakeupAlarms, operation);
    285         removeLocked(mElapsedRealtimeAlarms, operation);
    286     }
    287 
    288     private void removeLocked(ArrayList<Alarm> alarmList,
    289             PendingIntent operation) {
    290         if (alarmList.size() <= 0) {
    291             return;
    292         }
    293 
    294         // iterator over the list removing any it where the intent match
    295         Iterator<Alarm> it = alarmList.iterator();
    296 
    297         while (it.hasNext()) {
    298             Alarm alarm = it.next();
    299             if (alarm.operation.equals(operation)) {
    300                 it.remove();
    301             }
    302         }
    303     }
    304 
    305     public void removeLocked(String packageName) {
    306         removeLocked(mRtcWakeupAlarms, packageName);
    307         removeLocked(mRtcAlarms, packageName);
    308         removeLocked(mElapsedRealtimeWakeupAlarms, packageName);
    309         removeLocked(mElapsedRealtimeAlarms, packageName);
    310     }
    311 
    312     private void removeLocked(ArrayList<Alarm> alarmList,
    313             String packageName) {
    314         if (alarmList.size() <= 0) {
    315             return;
    316         }
    317 
    318         // iterator over the list removing any it where the intent match
    319         Iterator<Alarm> it = alarmList.iterator();
    320 
    321         while (it.hasNext()) {
    322             Alarm alarm = it.next();
    323             if (alarm.operation.getTargetPackage().equals(packageName)) {
    324                 it.remove();
    325             }
    326         }
    327     }
    328 
    329     public boolean lookForPackageLocked(String packageName) {
    330         return lookForPackageLocked(mRtcWakeupAlarms, packageName)
    331                 || lookForPackageLocked(mRtcAlarms, packageName)
    332                 || lookForPackageLocked(mElapsedRealtimeWakeupAlarms, packageName)
    333                 || lookForPackageLocked(mElapsedRealtimeAlarms, packageName);
    334     }
    335 
    336     private boolean lookForPackageLocked(ArrayList<Alarm> alarmList, String packageName) {
    337         for (int i=alarmList.size()-1; i>=0; i--) {
    338             if (alarmList.get(i).operation.getTargetPackage().equals(packageName)) {
    339                 return true;
    340             }
    341         }
    342         return false;
    343     }
    344 
    345     private ArrayList<Alarm> getAlarmList(int type) {
    346         switch (type) {
    347             case AlarmManager.RTC_WAKEUP:              return mRtcWakeupAlarms;
    348             case AlarmManager.RTC:                     return mRtcAlarms;
    349             case AlarmManager.ELAPSED_REALTIME_WAKEUP: return mElapsedRealtimeWakeupAlarms;
    350             case AlarmManager.ELAPSED_REALTIME:        return mElapsedRealtimeAlarms;
    351         }
    352 
    353         return null;
    354     }
    355 
    356     private int addAlarmLocked(Alarm alarm) {
    357         ArrayList<Alarm> alarmList = getAlarmList(alarm.type);
    358 
    359         int index = Collections.binarySearch(alarmList, alarm, mIncreasingTimeOrder);
    360         if (index < 0) {
    361             index = 0 - index - 1;
    362         }
    363         if (localLOGV) Slog.v(TAG, "Adding alarm " + alarm + " at " + index);
    364         alarmList.add(index, alarm);
    365 
    366         if (localLOGV) {
    367             // Display the list of alarms for this alarm type
    368             Slog.v(TAG, "alarms: " + alarmList.size() + " type: " + alarm.type);
    369             int position = 0;
    370             for (Alarm a : alarmList) {
    371                 Time time = new Time();
    372                 time.set(a.when);
    373                 String timeStr = time.format("%b %d %I:%M:%S %p");
    374                 Slog.v(TAG, position + ": " + timeStr
    375                         + " " + a.operation.getTargetPackage());
    376                 position += 1;
    377             }
    378         }
    379 
    380         return index;
    381     }
    382 
    383     public long timeToNextAlarm() {
    384         long nextAlarm = Long.MAX_VALUE;
    385         synchronized (mLock) {
    386             for (int i=AlarmManager.RTC_WAKEUP;
    387                     i<=AlarmManager.ELAPSED_REALTIME; i++) {
    388                 ArrayList<Alarm> alarmList = getAlarmList(i);
    389                 if (alarmList.size() > 0) {
    390                     Alarm a = alarmList.get(0);
    391                     if (a.when < nextAlarm) {
    392                         nextAlarm = a.when;
    393                     }
    394                 }
    395             }
    396         }
    397         return nextAlarm;
    398     }
    399 
    400     private void setLocked(Alarm alarm)
    401     {
    402         if (mDescriptor != -1)
    403         {
    404             // The kernel never triggers alarms with negative wakeup times
    405             // so we ensure they are positive.
    406             long alarmSeconds, alarmNanoseconds;
    407             if (alarm.when < 0) {
    408                 alarmSeconds = 0;
    409                 alarmNanoseconds = 0;
    410             } else {
    411                 alarmSeconds = alarm.when / 1000;
    412                 alarmNanoseconds = (alarm.when % 1000) * 1000 * 1000;
    413             }
    414 
    415             set(mDescriptor, alarm.type, alarmSeconds, alarmNanoseconds);
    416         }
    417         else
    418         {
    419             Message msg = Message.obtain();
    420             msg.what = ALARM_EVENT;
    421 
    422             mHandler.removeMessages(ALARM_EVENT);
    423             mHandler.sendMessageAtTime(msg, alarm.when);
    424         }
    425     }
    426 
    427     @Override
    428     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
    429         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
    430                 != PackageManager.PERMISSION_GRANTED) {
    431             pw.println("Permission Denial: can't dump AlarmManager from from pid="
    432                     + Binder.getCallingPid()
    433                     + ", uid=" + Binder.getCallingUid());
    434             return;
    435         }
    436 
    437         synchronized (mLock) {
    438             pw.println("Current Alarm Manager state:");
    439             if (mRtcWakeupAlarms.size() > 0 || mRtcAlarms.size() > 0) {
    440                 final long now = System.currentTimeMillis();
    441                 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    442                 pw.println(" ");
    443                 pw.print("  Realtime wakeup (now=");
    444                         pw.print(sdf.format(new Date(now))); pw.println("):");
    445                 if (mRtcWakeupAlarms.size() > 0) {
    446                     dumpAlarmList(pw, mRtcWakeupAlarms, "  ", "RTC_WAKEUP", now);
    447                 }
    448                 if (mRtcAlarms.size() > 0) {
    449                     dumpAlarmList(pw, mRtcAlarms, "  ", "RTC", now);
    450                 }
    451             }
    452             if (mElapsedRealtimeWakeupAlarms.size() > 0 || mElapsedRealtimeAlarms.size() > 0) {
    453                 final long now = SystemClock.elapsedRealtime();
    454                 pw.println(" ");
    455                 pw.print("  Elapsed realtime wakeup (now=");
    456                         TimeUtils.formatDuration(now, pw); pw.println("):");
    457                 if (mElapsedRealtimeWakeupAlarms.size() > 0) {
    458                     dumpAlarmList(pw, mElapsedRealtimeWakeupAlarms, "  ", "ELAPSED_WAKEUP", now);
    459                 }
    460                 if (mElapsedRealtimeAlarms.size() > 0) {
    461                     dumpAlarmList(pw, mElapsedRealtimeAlarms, "  ", "ELAPSED", now);
    462                 }
    463             }
    464 
    465             pw.println(" ");
    466             pw.print("  Broadcast ref count: "); pw.println(mBroadcastRefCount);
    467 
    468             pw.println(" ");
    469             pw.println("  Alarm Stats:");
    470             for (Map.Entry<String, BroadcastStats> be : mBroadcastStats.entrySet()) {
    471                 BroadcastStats bs = be.getValue();
    472                 pw.print("  "); pw.println(be.getKey());
    473                 pw.print("    "); pw.print(bs.aggregateTime);
    474                         pw.print("ms running, "); pw.print(bs.numWakeup);
    475                         pw.println(" wakeups");
    476                 for (Map.Entry<Intent.FilterComparison, FilterStats> fe
    477                         : bs.filterStats.entrySet()) {
    478                     pw.print("    "); pw.print(fe.getValue().count);
    479                             pw.print(" alarms: ");
    480                             pw.println(fe.getKey().getIntent().toShortString(false, true, false));
    481                 }
    482             }
    483         }
    484     }
    485 
    486     private static final void dumpAlarmList(PrintWriter pw, ArrayList<Alarm> list,
    487             String prefix, String label, long now) {
    488         for (int i=list.size()-1; i>=0; i--) {
    489             Alarm a = list.get(i);
    490             pw.print(prefix); pw.print(label); pw.print(" #"); pw.print(i);
    491                     pw.print(": "); pw.println(a);
    492             a.dump(pw, prefix + "  ", now);
    493         }
    494     }
    495 
    496     private native int init();
    497     private native void close(int fd);
    498     private native void set(int fd, int type, long seconds, long nanoseconds);
    499     private native int waitForAlarm(int fd);
    500     private native int setKernelTimezone(int fd, int minuteswest);
    501 
    502     private void triggerAlarmsLocked(ArrayList<Alarm> alarmList,
    503                                      ArrayList<Alarm> triggerList,
    504                                      long now)
    505     {
    506         Iterator<Alarm> it = alarmList.iterator();
    507         ArrayList<Alarm> repeats = new ArrayList<Alarm>();
    508 
    509         while (it.hasNext())
    510         {
    511             Alarm alarm = it.next();
    512 
    513             if (localLOGV) Slog.v(TAG, "Checking active alarm when=" + alarm.when + " " + alarm);
    514 
    515             if (alarm.when > now) {
    516                 // don't fire alarms in the future
    517                 break;
    518             }
    519 
    520             // If the alarm is late, then print a warning message.
    521             // Note that this can happen if the user creates a new event on
    522             // the Calendar app with a reminder that is in the past. In that
    523             // case, the reminder alarm will fire immediately.
    524             if (localLOGV && now - alarm.when > LATE_ALARM_THRESHOLD) {
    525                 Slog.v(TAG, "alarm is late! alarm time: " + alarm.when
    526                         + " now: " + now + " delay (in seconds): "
    527                         + (now - alarm.when) / 1000);
    528             }
    529 
    530             // Recurring alarms may have passed several alarm intervals while the
    531             // phone was asleep or off, so pass a trigger count when sending them.
    532             if (localLOGV) Slog.v(TAG, "Alarm triggering: " + alarm);
    533             alarm.count = 1;
    534             if (alarm.repeatInterval > 0) {
    535                 // this adjustment will be zero if we're late by
    536                 // less than one full repeat interval
    537                 alarm.count += (now - alarm.when) / alarm.repeatInterval;
    538             }
    539             triggerList.add(alarm);
    540 
    541             // remove the alarm from the list
    542             it.remove();
    543 
    544             // if it repeats queue it up to be read-added to the list
    545             if (alarm.repeatInterval > 0) {
    546                 repeats.add(alarm);
    547             }
    548         }
    549 
    550         // reset any repeating alarms.
    551         it = repeats.iterator();
    552         while (it.hasNext()) {
    553             Alarm alarm = it.next();
    554             alarm.when += alarm.count * alarm.repeatInterval;
    555             addAlarmLocked(alarm);
    556         }
    557 
    558         if (alarmList.size() > 0) {
    559             setLocked(alarmList.get(0));
    560         }
    561     }
    562 
    563     /**
    564      * This Comparator sorts Alarms into increasing time order.
    565      */
    566     public static class IncreasingTimeOrder implements Comparator<Alarm> {
    567         public int compare(Alarm a1, Alarm a2) {
    568             long when1 = a1.when;
    569             long when2 = a2.when;
    570             if (when1 - when2 > 0) {
    571                 return 1;
    572             }
    573             if (when1 - when2 < 0) {
    574                 return -1;
    575             }
    576             return 0;
    577         }
    578     }
    579 
    580     private static class Alarm {
    581         public int type;
    582         public int count;
    583         public long when;
    584         public long repeatInterval;
    585         public PendingIntent operation;
    586 
    587         public Alarm() {
    588             when = 0;
    589             repeatInterval = 0;
    590             operation = null;
    591         }
    592 
    593         @Override
    594         public String toString()
    595         {
    596             StringBuilder sb = new StringBuilder(128);
    597             sb.append("Alarm{");
    598             sb.append(Integer.toHexString(System.identityHashCode(this)));
    599             sb.append(" type ");
    600             sb.append(type);
    601             sb.append(" ");
    602             sb.append(operation.getTargetPackage());
    603             sb.append('}');
    604             return sb.toString();
    605         }
    606 
    607         public void dump(PrintWriter pw, String prefix, long now) {
    608             pw.print(prefix); pw.print("type="); pw.print(type);
    609                     pw.print(" when="); TimeUtils.formatDuration(when, now, pw);
    610                     pw.print(" repeatInterval="); pw.print(repeatInterval);
    611                     pw.print(" count="); pw.println(count);
    612             pw.print(prefix); pw.print("operation="); pw.println(operation);
    613         }
    614     }
    615 
    616     private class AlarmThread extends Thread
    617     {
    618         public AlarmThread()
    619         {
    620             super("AlarmManager");
    621         }
    622 
    623         public void run()
    624         {
    625             while (true)
    626             {
    627                 int result = waitForAlarm(mDescriptor);
    628 
    629                 ArrayList<Alarm> triggerList = new ArrayList<Alarm>();
    630 
    631                 if ((result & TIME_CHANGED_MASK) != 0) {
    632                     remove(mTimeTickSender);
    633                     mClockReceiver.scheduleTimeTickEvent();
    634                     Intent intent = new Intent(Intent.ACTION_TIME_CHANGED);
    635                     intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING
    636                             | Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
    637                     mContext.sendBroadcast(intent);
    638                 }
    639 
    640                 synchronized (mLock) {
    641                     final long nowRTC = System.currentTimeMillis();
    642                     final long nowELAPSED = SystemClock.elapsedRealtime();
    643                     if (localLOGV) Slog.v(
    644                         TAG, "Checking for alarms... rtc=" + nowRTC
    645                         + ", elapsed=" + nowELAPSED);
    646 
    647                     if ((result & RTC_WAKEUP_MASK) != 0)
    648                         triggerAlarmsLocked(mRtcWakeupAlarms, triggerList, nowRTC);
    649 
    650                     if ((result & RTC_MASK) != 0)
    651                         triggerAlarmsLocked(mRtcAlarms, triggerList, nowRTC);
    652 
    653                     if ((result & ELAPSED_REALTIME_WAKEUP_MASK) != 0)
    654                         triggerAlarmsLocked(mElapsedRealtimeWakeupAlarms, triggerList, nowELAPSED);
    655 
    656                     if ((result & ELAPSED_REALTIME_MASK) != 0)
    657                         triggerAlarmsLocked(mElapsedRealtimeAlarms, triggerList, nowELAPSED);
    658 
    659                     // now trigger the alarms
    660                     Iterator<Alarm> it = triggerList.iterator();
    661                     while (it.hasNext()) {
    662                         Alarm alarm = it.next();
    663                         try {
    664                             if (localLOGV) Slog.v(TAG, "sending alarm " + alarm);
    665                             alarm.operation.send(mContext, 0,
    666                                     mBackgroundIntent.putExtra(
    667                                             Intent.EXTRA_ALARM_COUNT, alarm.count),
    668                                     mResultReceiver, mHandler);
    669 
    670                             // we have an active broadcast so stay awake.
    671                             if (mBroadcastRefCount == 0) {
    672                                 mWakeLock.acquire();
    673                             }
    674                             mBroadcastRefCount++;
    675 
    676                             BroadcastStats bs = getStatsLocked(alarm.operation);
    677                             if (bs.nesting == 0) {
    678                                 bs.startTime = nowELAPSED;
    679                             } else {
    680                                 bs.nesting++;
    681                             }
    682                             if (alarm.type == AlarmManager.ELAPSED_REALTIME_WAKEUP
    683                                     || alarm.type == AlarmManager.RTC_WAKEUP) {
    684                                 bs.numWakeup++;
    685                                 ActivityManagerNative.noteWakeupAlarm(
    686                                         alarm.operation);
    687                             }
    688                         } catch (PendingIntent.CanceledException e) {
    689                             if (alarm.repeatInterval > 0) {
    690                                 // This IntentSender is no longer valid, but this
    691                                 // is a repeating alarm, so toss the hoser.
    692                                 remove(alarm.operation);
    693                             }
    694                         } catch (RuntimeException e) {
    695                             Slog.w(TAG, "Failure sending alarm.", e);
    696                         }
    697                     }
    698                 }
    699             }
    700         }
    701     }
    702 
    703     private class AlarmHandler extends Handler {
    704         public static final int ALARM_EVENT = 1;
    705         public static final int MINUTE_CHANGE_EVENT = 2;
    706         public static final int DATE_CHANGE_EVENT = 3;
    707 
    708         public AlarmHandler() {
    709         }
    710 
    711         public void handleMessage(Message msg) {
    712             if (msg.what == ALARM_EVENT) {
    713                 ArrayList<Alarm> triggerList = new ArrayList<Alarm>();
    714                 synchronized (mLock) {
    715                     final long nowRTC = System.currentTimeMillis();
    716                     triggerAlarmsLocked(mRtcWakeupAlarms, triggerList, nowRTC);
    717                     triggerAlarmsLocked(mRtcAlarms, triggerList, nowRTC);
    718                     triggerAlarmsLocked(mElapsedRealtimeWakeupAlarms, triggerList, nowRTC);
    719                     triggerAlarmsLocked(mElapsedRealtimeAlarms, triggerList, nowRTC);
    720                 }
    721 
    722                 // now trigger the alarms without the lock held
    723                 Iterator<Alarm> it = triggerList.iterator();
    724                 while (it.hasNext())
    725                 {
    726                     Alarm alarm = it.next();
    727                     try {
    728                         alarm.operation.send();
    729                     } catch (PendingIntent.CanceledException e) {
    730                         if (alarm.repeatInterval > 0) {
    731                             // This IntentSender is no longer valid, but this
    732                             // is a repeating alarm, so toss the hoser.
    733                             remove(alarm.operation);
    734                         }
    735                     }
    736                 }
    737             }
    738         }
    739     }
    740 
    741     class ClockReceiver extends BroadcastReceiver {
    742         public ClockReceiver() {
    743             IntentFilter filter = new IntentFilter();
    744             filter.addAction(Intent.ACTION_TIME_TICK);
    745             filter.addAction(Intent.ACTION_DATE_CHANGED);
    746             mContext.registerReceiver(this, filter);
    747         }
    748 
    749         @Override
    750         public void onReceive(Context context, Intent intent) {
    751             if (intent.getAction().equals(Intent.ACTION_TIME_TICK)) {
    752             	scheduleTimeTickEvent();
    753             } else if (intent.getAction().equals(Intent.ACTION_DATE_CHANGED)) {
    754                 // Since the kernel does not keep track of DST, we need to
    755                 // reset the TZ information at the beginning of each day
    756                 // based off of the current Zone gmt offset + userspace tracked
    757                 // daylight savings information.
    758                 TimeZone zone = TimeZone.getTimeZone(SystemProperties.get(TIMEZONE_PROPERTY));
    759                 int gmtOffset = zone.getOffset(System.currentTimeMillis());
    760                 setKernelTimezone(mDescriptor, -(gmtOffset / 60000));
    761             	scheduleDateChangedEvent();
    762             }
    763         }
    764 
    765         public void scheduleTimeTickEvent() {
    766             Calendar calendar = Calendar.getInstance();
    767             final long currentTime = System.currentTimeMillis();
    768             calendar.setTimeInMillis(currentTime);
    769             calendar.add(Calendar.MINUTE, 1);
    770             calendar.set(Calendar.SECOND, 0);
    771             calendar.set(Calendar.MILLISECOND, 0);
    772 
    773             // Schedule this event for the amount of time that it would take to get to
    774             // the top of the next minute.
    775             final long tickEventDelay = calendar.getTimeInMillis() - currentTime;
    776 
    777             set(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime() + tickEventDelay,
    778                     mTimeTickSender);
    779         }
    780 
    781         public void scheduleDateChangedEvent() {
    782             Calendar calendar = Calendar.getInstance();
    783             calendar.setTimeInMillis(System.currentTimeMillis());
    784             calendar.set(Calendar.HOUR, 0);
    785             calendar.set(Calendar.MINUTE, 0);
    786             calendar.set(Calendar.SECOND, 0);
    787             calendar.set(Calendar.MILLISECOND, 0);
    788             calendar.add(Calendar.DAY_OF_MONTH, 1);
    789 
    790             set(AlarmManager.RTC, calendar.getTimeInMillis(), mDateChangeSender);
    791         }
    792     }
    793 
    794     class UninstallReceiver extends BroadcastReceiver {
    795         public UninstallReceiver() {
    796             IntentFilter filter = new IntentFilter();
    797             filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
    798             filter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
    799             filter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
    800             filter.addDataScheme("package");
    801             mContext.registerReceiver(this, filter);
    802              // Register for events related to sdcard installation.
    803             IntentFilter sdFilter = new IntentFilter();
    804             sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
    805             mContext.registerReceiver(this, sdFilter);
    806         }
    807 
    808         @Override
    809         public void onReceive(Context context, Intent intent) {
    810             synchronized (mLock) {
    811                 String action = intent.getAction();
    812                 String pkgList[] = null;
    813                 if (Intent.ACTION_QUERY_PACKAGE_RESTART.equals(action)) {
    814                     pkgList = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);
    815                     for (String packageName : pkgList) {
    816                         if (lookForPackageLocked(packageName)) {
    817                             setResultCode(Activity.RESULT_OK);
    818                             return;
    819                         }
    820                     }
    821                     return;
    822                 } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
    823                     pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
    824                 } else {
    825                     if (Intent.ACTION_PACKAGE_REMOVED.equals(action)
    826                             && intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
    827                         // This package is being updated; don't kill its alarms.
    828                         return;
    829                     }
    830                     Uri data = intent.getData();
    831                     if (data != null) {
    832                         String pkg = data.getSchemeSpecificPart();
    833                         if (pkg != null) {
    834                             pkgList = new String[]{pkg};
    835                         }
    836                     }
    837                 }
    838                 if (pkgList != null && (pkgList.length > 0)) {
    839                     for (String pkg : pkgList) {
    840                         removeLocked(pkg);
    841                         mBroadcastStats.remove(pkg);
    842                     }
    843                 }
    844             }
    845         }
    846     }
    847 
    848     private final BroadcastStats getStatsLocked(PendingIntent pi) {
    849         String pkg = pi.getTargetPackage();
    850         BroadcastStats bs = mBroadcastStats.get(pkg);
    851         if (bs == null) {
    852             bs = new BroadcastStats();
    853             mBroadcastStats.put(pkg, bs);
    854         }
    855         return bs;
    856     }
    857 
    858     class ResultReceiver implements PendingIntent.OnFinished {
    859         public void onSendFinished(PendingIntent pi, Intent intent, int resultCode,
    860                 String resultData, Bundle resultExtras) {
    861             synchronized (mLock) {
    862                 BroadcastStats bs = getStatsLocked(pi);
    863                 if (bs != null) {
    864                     bs.nesting--;
    865                     if (bs.nesting <= 0) {
    866                         bs.nesting = 0;
    867                         bs.aggregateTime += SystemClock.elapsedRealtime()
    868                                 - bs.startTime;
    869                         Intent.FilterComparison fc = new Intent.FilterComparison(intent);
    870                         FilterStats fs = bs.filterStats.get(fc);
    871                         if (fs == null) {
    872                             fs = new FilterStats();
    873                             bs.filterStats.put(fc, fs);
    874                         }
    875                         fs.count++;
    876                     }
    877                 }
    878                 mBroadcastRefCount--;
    879                 if (mBroadcastRefCount == 0) {
    880                     mWakeLock.release();
    881                 }
    882             }
    883         }
    884     }
    885 }
    886