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
17package com.android.server;
18
19import android.app.Activity;
20import android.app.ActivityManagerNative;
21import android.app.AlarmManager;
22import android.app.IAlarmManager;
23import android.app.PendingIntent;
24import android.content.BroadcastReceiver;
25import android.content.Context;
26import android.content.Intent;
27import android.content.IntentFilter;
28import android.content.pm.PackageManager;
29import android.net.Uri;
30import android.os.Binder;
31import android.os.Bundle;
32import android.os.Handler;
33import android.os.Message;
34import android.os.PowerManager;
35import android.os.SystemClock;
36import android.os.SystemProperties;
37import android.text.TextUtils;
38import android.text.format.Time;
39import android.util.EventLog;
40import android.util.Slog;
41import android.util.TimeUtils;
42
43import java.io.FileDescriptor;
44import java.io.PrintWriter;
45import java.text.SimpleDateFormat;
46import java.util.ArrayList;
47import java.util.Calendar;
48import java.util.Collections;
49import java.util.Comparator;
50import java.util.Date;
51import java.util.HashMap;
52import java.util.Iterator;
53import java.util.Map;
54import java.util.TimeZone;
55
56class 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