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