AlarmManagerService.java revision a1f1a3c573acd91024fda0ceb3b921c73b186963
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.ComponentName;
26import android.content.Context;
27import android.content.Intent;
28import android.content.IntentFilter;
29import android.content.pm.PackageManager;
30import android.net.Uri;
31import android.os.Binder;
32import android.os.Bundle;
33import android.os.Handler;
34import android.os.IBinder;
35import android.os.Message;
36import android.os.PowerManager;
37import android.os.SystemClock;
38import android.os.SystemProperties;
39import android.os.UserHandle;
40import android.os.WorkSource;
41import android.text.TextUtils;
42import android.util.ArrayMap;
43import android.util.Pair;
44import android.util.Slog;
45import android.util.SparseArray;
46import android.util.TimeUtils;
47
48import java.io.ByteArrayOutputStream;
49import java.io.FileDescriptor;
50import java.io.PrintWriter;
51import java.text.SimpleDateFormat;
52import java.util.ArrayList;
53import java.util.Arrays;
54import java.util.Calendar;
55import java.util.Collections;
56import java.util.Comparator;
57import java.util.Date;
58import java.util.LinkedList;
59import java.util.TimeZone;
60
61import static android.app.AlarmManager.RTC_WAKEUP;
62import static android.app.AlarmManager.RTC;
63import static android.app.AlarmManager.ELAPSED_REALTIME_WAKEUP;
64import static android.app.AlarmManager.ELAPSED_REALTIME;
65
66import com.android.internal.util.LocalLog;
67
68class AlarmManagerService extends SystemService {
69    // The threshold for how long an alarm can be late before we print a
70    // warning message.  The time duration is in milliseconds.
71    private static final long LATE_ALARM_THRESHOLD = 10 * 1000;
72
73    private static final int RTC_WAKEUP_MASK = 1 << RTC_WAKEUP;
74    private static final int RTC_MASK = 1 << RTC;
75    private static final int ELAPSED_REALTIME_WAKEUP_MASK = 1 << ELAPSED_REALTIME_WAKEUP;
76    private static final int ELAPSED_REALTIME_MASK = 1 << ELAPSED_REALTIME;
77    static final int TIME_CHANGED_MASK = 1 << 16;
78    static final int IS_WAKEUP_MASK = RTC_WAKEUP_MASK|ELAPSED_REALTIME_WAKEUP_MASK;
79
80    // Mask for testing whether a given alarm type is wakeup vs non-wakeup
81    static final int TYPE_NONWAKEUP_MASK = 0x1; // low bit => non-wakeup
82
83    static final String TAG = "AlarmManager";
84    static final String ClockReceiver_TAG = "ClockReceiver";
85    static final boolean localLOGV = false;
86    static final boolean DEBUG_BATCH = localLOGV || false;
87    static final boolean DEBUG_VALIDATE = localLOGV || false;
88    static final int ALARM_EVENT = 1;
89    static final String TIMEZONE_PROPERTY = "persist.sys.timezone";
90
91    static final Intent mBackgroundIntent
92            = new Intent().addFlags(Intent.FLAG_FROM_BACKGROUND);
93    static final IncreasingTimeOrder sIncreasingTimeOrder = new IncreasingTimeOrder();
94
95    static final boolean WAKEUP_STATS = false;
96
97    final LocalLog mLog = new LocalLog(TAG);
98
99    final Object mLock = new Object();
100
101    long mNativeData;
102    private long mNextWakeup;
103    private long mNextNonWakeup;
104    int mBroadcastRefCount = 0;
105    PowerManager.WakeLock mWakeLock;
106    ArrayList<InFlight> mInFlight = new ArrayList<InFlight>();
107    final AlarmHandler mHandler = new AlarmHandler();
108    ClockReceiver mClockReceiver;
109    private UninstallReceiver mUninstallReceiver;
110    final ResultReceiver mResultReceiver = new ResultReceiver();
111    PendingIntent mTimeTickSender;
112    PendingIntent mDateChangeSender;
113
114    class WakeupEvent {
115        public long when;
116        public int uid;
117        public String action;
118
119        public WakeupEvent(long theTime, int theUid, String theAction) {
120            when = theTime;
121            uid = theUid;
122            action = theAction;
123        }
124    }
125
126    final LinkedList<WakeupEvent> mRecentWakeups = new LinkedList<WakeupEvent>();
127    final long RECENT_WAKEUP_PERIOD = 1000L * 60 * 60 * 24; // one day
128
129    static final class Batch {
130        long start;     // These endpoints are always in ELAPSED
131        long end;
132        boolean standalone; // certain "batches" don't participate in coalescing
133
134        final ArrayList<Alarm> alarms = new ArrayList<Alarm>();
135
136        Batch() {
137            start = 0;
138            end = Long.MAX_VALUE;
139        }
140
141        Batch(Alarm seed) {
142            start = seed.whenElapsed;
143            end = seed.maxWhen;
144            alarms.add(seed);
145        }
146
147        int size() {
148            return alarms.size();
149        }
150
151        Alarm get(int index) {
152            return alarms.get(index);
153        }
154
155        boolean canHold(long whenElapsed, long maxWhen) {
156            return (end >= whenElapsed) && (start <= maxWhen);
157        }
158
159        boolean add(Alarm alarm) {
160            boolean newStart = false;
161            // narrows the batch if necessary; presumes that canHold(alarm) is true
162            int index = Collections.binarySearch(alarms, alarm, sIncreasingTimeOrder);
163            if (index < 0) {
164                index = 0 - index - 1;
165            }
166            alarms.add(index, alarm);
167            if (DEBUG_BATCH) {
168                Slog.v(TAG, "Adding " + alarm + " to " + this);
169            }
170            if (alarm.whenElapsed > start) {
171                start = alarm.whenElapsed;
172                newStart = true;
173            }
174            if (alarm.maxWhen < end) {
175                end = alarm.maxWhen;
176            }
177
178            if (DEBUG_BATCH) {
179                Slog.v(TAG, "    => now " + this);
180            }
181            return newStart;
182        }
183
184        boolean remove(final PendingIntent operation) {
185            boolean didRemove = false;
186            long newStart = 0;  // recalculate endpoints as we go
187            long newEnd = Long.MAX_VALUE;
188            for (int i = 0; i < alarms.size(); ) {
189                Alarm alarm = alarms.get(i);
190                if (alarm.operation.equals(operation)) {
191                    alarms.remove(i);
192                    didRemove = true;
193                } else {
194                    if (alarm.whenElapsed > newStart) {
195                        newStart = alarm.whenElapsed;
196                    }
197                    if (alarm.maxWhen < newEnd) {
198                        newEnd = alarm.maxWhen;
199                    }
200                    i++;
201                }
202            }
203            if (didRemove) {
204                // commit the new batch bounds
205                start = newStart;
206                end = newEnd;
207            }
208            return didRemove;
209        }
210
211        boolean remove(final String packageName) {
212            boolean didRemove = false;
213            long newStart = 0;  // recalculate endpoints as we go
214            long newEnd = Long.MAX_VALUE;
215            for (int i = 0; i < alarms.size(); ) {
216                Alarm alarm = alarms.get(i);
217                if (alarm.operation.getTargetPackage().equals(packageName)) {
218                    alarms.remove(i);
219                    didRemove = true;
220                } else {
221                    if (alarm.whenElapsed > newStart) {
222                        newStart = alarm.whenElapsed;
223                    }
224                    if (alarm.maxWhen < newEnd) {
225                        newEnd = alarm.maxWhen;
226                    }
227                    i++;
228                }
229            }
230            if (didRemove) {
231                // commit the new batch bounds
232                start = newStart;
233                end = newEnd;
234            }
235            return didRemove;
236        }
237
238        boolean remove(final int userHandle) {
239            boolean didRemove = false;
240            long newStart = 0;  // recalculate endpoints as we go
241            long newEnd = Long.MAX_VALUE;
242            for (int i = 0; i < alarms.size(); ) {
243                Alarm alarm = alarms.get(i);
244                if (UserHandle.getUserId(alarm.operation.getCreatorUid()) == userHandle) {
245                    alarms.remove(i);
246                    didRemove = true;
247                } else {
248                    if (alarm.whenElapsed > newStart) {
249                        newStart = alarm.whenElapsed;
250                    }
251                    if (alarm.maxWhen < newEnd) {
252                        newEnd = alarm.maxWhen;
253                    }
254                    i++;
255                }
256            }
257            if (didRemove) {
258                // commit the new batch bounds
259                start = newStart;
260                end = newEnd;
261            }
262            return didRemove;
263        }
264
265        boolean hasPackage(final String packageName) {
266            final int N = alarms.size();
267            for (int i = 0; i < N; i++) {
268                Alarm a = alarms.get(i);
269                if (a.operation.getTargetPackage().equals(packageName)) {
270                    return true;
271                }
272            }
273            return false;
274        }
275
276        boolean hasWakeups() {
277            final int N = alarms.size();
278            for (int i = 0; i < N; i++) {
279                Alarm a = alarms.get(i);
280                // non-wakeup alarms are types 1 and 3, i.e. have the low bit set
281                if ((a.type & TYPE_NONWAKEUP_MASK) == 0) {
282                    return true;
283                }
284            }
285            return false;
286        }
287
288        @Override
289        public String toString() {
290            StringBuilder b = new StringBuilder(40);
291            b.append("Batch{"); b.append(Integer.toHexString(this.hashCode()));
292            b.append(" num="); b.append(size());
293            b.append(" start="); b.append(start);
294            b.append(" end="); b.append(end);
295            if (standalone) {
296                b.append(" STANDALONE");
297            }
298            b.append('}');
299            return b.toString();
300        }
301    }
302
303    static class BatchTimeOrder implements Comparator<Batch> {
304        public int compare(Batch b1, Batch b2) {
305            long when1 = b1.start;
306            long when2 = b2.start;
307            if (when1 - when2 > 0) {
308                return 1;
309            }
310            if (when1 - when2 < 0) {
311                return -1;
312            }
313            return 0;
314        }
315    }
316
317    final Comparator<Alarm> mAlarmDispatchComparator = new Comparator<Alarm>() {
318        @Override
319        public int compare(Alarm lhs, Alarm rhs) {
320            if (lhs.wakeup != rhs.wakeup) {
321                return lhs.wakeup ? -1 : 1;
322            }
323            if (lhs.whenElapsed < rhs.whenElapsed) {
324                return -1;
325            } else if (lhs.whenElapsed > rhs.whenElapsed) {
326                return 1;
327            }
328            return 0;
329        }
330    };
331
332    // minimum recurrence period or alarm futurity for us to be able to fuzz it
333    static final long MIN_FUZZABLE_INTERVAL = 10000;
334    static final BatchTimeOrder sBatchOrder = new BatchTimeOrder();
335    final ArrayList<Batch> mAlarmBatches = new ArrayList<Batch>();
336
337    public AlarmManagerService(Context context) {
338        super(context);
339    }
340
341    static long convertToElapsed(long when, int type) {
342        final boolean isRtc = (type == RTC || type == RTC_WAKEUP);
343        if (isRtc) {
344            when -= System.currentTimeMillis() - SystemClock.elapsedRealtime();
345        }
346        return when;
347    }
348
349    // Apply a heuristic to { recurrence interval, futurity of the trigger time } to
350    // calculate the end of our nominal delivery window for the alarm.
351    static long maxTriggerTime(long now, long triggerAtTime, long interval) {
352        // Current heuristic: batchable window is 75% of either the recurrence interval
353        // [for a periodic alarm] or of the time from now to the desired delivery time,
354        // with a minimum delay/interval of 10 seconds, under which we will simply not
355        // defer the alarm.
356        long futurity = (interval == 0)
357                ? (triggerAtTime - now)
358                : interval;
359        if (futurity < MIN_FUZZABLE_INTERVAL) {
360            futurity = 0;
361        }
362        return triggerAtTime + (long)(.75 * futurity);
363    }
364
365    // returns true if the batch was added at the head
366    static boolean addBatchLocked(ArrayList<Batch> list, Batch newBatch) {
367        int index = Collections.binarySearch(list, newBatch, sBatchOrder);
368        if (index < 0) {
369            index = 0 - index - 1;
370        }
371        list.add(index, newBatch);
372        return (index == 0);
373    }
374
375    // Return the index of the matching batch, or -1 if none found.
376    int attemptCoalesceLocked(long whenElapsed, long maxWhen) {
377        final int N = mAlarmBatches.size();
378        for (int i = 0; i < N; i++) {
379            Batch b = mAlarmBatches.get(i);
380            if (!b.standalone && b.canHold(whenElapsed, maxWhen)) {
381                return i;
382            }
383        }
384        return -1;
385    }
386
387    // The RTC clock has moved arbitrarily, so we need to recalculate all the batching
388    void rebatchAllAlarms() {
389        synchronized (mLock) {
390            rebatchAllAlarmsLocked(true);
391        }
392    }
393
394    void rebatchAllAlarmsLocked(boolean doValidate) {
395        ArrayList<Batch> oldSet = (ArrayList<Batch>) mAlarmBatches.clone();
396        mAlarmBatches.clear();
397        final long nowElapsed = SystemClock.elapsedRealtime();
398        final int oldBatches = oldSet.size();
399        for (int batchNum = 0; batchNum < oldBatches; batchNum++) {
400            Batch batch = oldSet.get(batchNum);
401            final int N = batch.size();
402            for (int i = 0; i < N; i++) {
403                Alarm a = batch.get(i);
404                long whenElapsed = convertToElapsed(a.when, a.type);
405                final long maxElapsed;
406                if (a.whenElapsed == a.maxWhen) {
407                    // Exact
408                    maxElapsed = whenElapsed;
409                } else {
410                    // Not exact.  Preserve any explicit window, otherwise recalculate
411                    // the window based on the alarm's new futurity.  Note that this
412                    // reflects a policy of preferring timely to deferred delivery.
413                    maxElapsed = (a.windowLength > 0)
414                            ? (whenElapsed + a.windowLength)
415                            : maxTriggerTime(nowElapsed, whenElapsed, a.repeatInterval);
416                }
417                setImplLocked(a.type, a.when, whenElapsed, a.windowLength, maxElapsed,
418                        a.repeatInterval, a.operation, batch.standalone, doValidate, a.workSource);
419            }
420        }
421    }
422
423    static final class InFlight extends Intent {
424        final PendingIntent mPendingIntent;
425        final WorkSource mWorkSource;
426        final Pair<String, ComponentName> mTarget;
427        final BroadcastStats mBroadcastStats;
428        final FilterStats mFilterStats;
429
430        InFlight(AlarmManagerService service, PendingIntent pendingIntent, WorkSource workSource) {
431            mPendingIntent = pendingIntent;
432            mWorkSource = workSource;
433            Intent intent = pendingIntent.getIntent();
434            mTarget = intent != null
435                    ? new Pair<String, ComponentName>(intent.getAction(), intent.getComponent())
436                    : null;
437            mBroadcastStats = service.getStatsLocked(pendingIntent);
438            FilterStats fs = mBroadcastStats.filterStats.get(mTarget);
439            if (fs == null) {
440                fs = new FilterStats(mBroadcastStats, mTarget);
441                mBroadcastStats.filterStats.put(mTarget, fs);
442            }
443            mFilterStats = fs;
444        }
445    }
446
447    static final class FilterStats {
448        final BroadcastStats mBroadcastStats;
449        final Pair<String, ComponentName> mTarget;
450
451        long aggregateTime;
452        int count;
453        int numWakeup;
454        long startTime;
455        int nesting;
456
457        FilterStats(BroadcastStats broadcastStats, Pair<String, ComponentName> target) {
458            mBroadcastStats = broadcastStats;
459            mTarget = target;
460        }
461    }
462
463    static final class BroadcastStats {
464        final int mUid;
465        final String mPackageName;
466
467        long aggregateTime;
468        int count;
469        int numWakeup;
470        long startTime;
471        int nesting;
472        final ArrayMap<Pair<String, ComponentName>, FilterStats> filterStats
473                = new ArrayMap<Pair<String, ComponentName>, FilterStats>();
474
475        BroadcastStats(int uid, String packageName) {
476            mUid = uid;
477            mPackageName = packageName;
478        }
479    }
480
481    final SparseArray<ArrayMap<String, BroadcastStats>> mBroadcastStats
482            = new SparseArray<ArrayMap<String, BroadcastStats>>();
483
484    @Override
485    public void onStart() {
486        mNativeData = init();
487        mNextWakeup = mNextNonWakeup = 0;
488
489        // We have to set current TimeZone info to kernel
490        // because kernel doesn't keep this after reboot
491        setTimeZoneImpl(SystemProperties.get(TIMEZONE_PROPERTY));
492
493        PowerManager pm = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE);
494        mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*alarm*");
495
496        mTimeTickSender = PendingIntent.getBroadcastAsUser(getContext(), 0,
497                new Intent(Intent.ACTION_TIME_TICK).addFlags(
498                        Intent.FLAG_RECEIVER_REGISTERED_ONLY
499                        | Intent.FLAG_RECEIVER_FOREGROUND), 0,
500                        UserHandle.ALL);
501        Intent intent = new Intent(Intent.ACTION_DATE_CHANGED);
502        intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
503        mDateChangeSender = PendingIntent.getBroadcastAsUser(getContext(), 0, intent,
504                Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT, UserHandle.ALL);
505
506        // now that we have initied the driver schedule the alarm
507        mClockReceiver = new ClockReceiver();
508        mClockReceiver.scheduleTimeTickEvent();
509        mClockReceiver.scheduleDateChangedEvent();
510        mUninstallReceiver = new UninstallReceiver();
511
512        if (mNativeData != 0) {
513            AlarmThread waitThread = new AlarmThread();
514            waitThread.start();
515        } else {
516            Slog.w(TAG, "Failed to open alarm driver. Falling back to a handler.");
517        }
518
519        publishBinderService(Context.ALARM_SERVICE, mService);
520    }
521
522    @Override
523    protected void finalize() throws Throwable {
524        try {
525            close(mNativeData);
526        } finally {
527            super.finalize();
528        }
529    }
530
531    void setTimeZoneImpl(String tz) {
532        if (TextUtils.isEmpty(tz)) {
533            return;
534        }
535
536        TimeZone zone = TimeZone.getTimeZone(tz);
537        // Prevent reentrant calls from stepping on each other when writing
538        // the time zone property
539        boolean timeZoneWasChanged = false;
540        synchronized (this) {
541            String current = SystemProperties.get(TIMEZONE_PROPERTY);
542            if (current == null || !current.equals(zone.getID())) {
543                if (localLOGV) {
544                    Slog.v(TAG, "timezone changed: " + current + ", new=" + zone.getID());
545                }
546                timeZoneWasChanged = true;
547                SystemProperties.set(TIMEZONE_PROPERTY, zone.getID());
548            }
549
550            // Update the kernel timezone information
551            // Kernel tracks time offsets as 'minutes west of GMT'
552            int gmtOffset = zone.getOffset(System.currentTimeMillis());
553            setKernelTimezone(mNativeData, -(gmtOffset / 60000));
554        }
555
556        TimeZone.setDefault(null);
557
558        if (timeZoneWasChanged) {
559            Intent intent = new Intent(Intent.ACTION_TIMEZONE_CHANGED);
560            intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
561            intent.putExtra("time-zone", zone.getID());
562            getContext().sendBroadcastAsUser(intent, UserHandle.ALL);
563        }
564    }
565
566    void removeImpl(PendingIntent operation) {
567        if (operation == null) {
568            return;
569        }
570        synchronized (mLock) {
571            removeLocked(operation);
572        }
573    }
574
575    void setImpl(int type, long triggerAtTime, long windowLength, long interval,
576            PendingIntent operation, boolean isStandalone, WorkSource workSource) {
577        if (operation == null) {
578            Slog.w(TAG, "set/setRepeating ignored because there is no intent");
579            return;
580        }
581
582        // Sanity check the window length.  This will catch people mistakenly
583        // trying to pass an end-of-window timestamp rather than a duration.
584        if (windowLength > AlarmManager.INTERVAL_HALF_DAY) {
585            Slog.w(TAG, "Window length " + windowLength
586                    + "ms suspiciously long; limiting to 1 hour");
587            windowLength = AlarmManager.INTERVAL_HOUR;
588        }
589
590        if (type < RTC_WAKEUP || type > ELAPSED_REALTIME) {
591            throw new IllegalArgumentException("Invalid alarm type " + type);
592        }
593
594        if (triggerAtTime < 0) {
595            final long who = Binder.getCallingUid();
596            final long what = Binder.getCallingPid();
597            Slog.w(TAG, "Invalid alarm trigger time! " + triggerAtTime + " from uid=" + who
598                    + " pid=" + what);
599            triggerAtTime = 0;
600        }
601
602        final long nowElapsed = SystemClock.elapsedRealtime();
603        final long triggerElapsed = convertToElapsed(triggerAtTime, type);
604        final long maxElapsed;
605        if (windowLength == AlarmManager.WINDOW_EXACT) {
606            maxElapsed = triggerElapsed;
607        } else if (windowLength < 0) {
608            maxElapsed = maxTriggerTime(nowElapsed, triggerElapsed, interval);
609        } else {
610            maxElapsed = triggerElapsed + windowLength;
611        }
612
613        synchronized (mLock) {
614            if (DEBUG_BATCH) {
615                Slog.v(TAG, "set(" + operation + ") : type=" + type
616                        + " triggerAtTime=" + triggerAtTime + " win=" + windowLength
617                        + " tElapsed=" + triggerElapsed + " maxElapsed=" + maxElapsed
618                        + " interval=" + interval + " standalone=" + isStandalone);
619            }
620            setImplLocked(type, triggerAtTime, triggerElapsed, windowLength, maxElapsed,
621                    interval, operation, isStandalone, true, workSource);
622        }
623    }
624
625    private void setImplLocked(int type, long when, long whenElapsed, long windowLength,
626            long maxWhen, long interval, PendingIntent operation, boolean isStandalone,
627            boolean doValidate, WorkSource workSource) {
628        Alarm a = new Alarm(type, when, whenElapsed, windowLength, maxWhen, interval,
629                operation, workSource);
630        removeLocked(operation);
631
632        int whichBatch = (isStandalone) ? -1 : attemptCoalesceLocked(whenElapsed, maxWhen);
633        if (whichBatch < 0) {
634            Batch batch = new Batch(a);
635            batch.standalone = isStandalone;
636            addBatchLocked(mAlarmBatches, batch);
637        } else {
638            Batch batch = mAlarmBatches.get(whichBatch);
639            if (batch.add(a)) {
640                // The start time of this batch advanced, so batch ordering may
641                // have just been broken.  Move it to where it now belongs.
642                mAlarmBatches.remove(whichBatch);
643                addBatchLocked(mAlarmBatches, batch);
644            }
645        }
646
647        if (DEBUG_VALIDATE) {
648            if (doValidate && !validateConsistencyLocked()) {
649                Slog.v(TAG, "Tipping-point operation: type=" + type + " when=" + when
650                        + " when(hex)=" + Long.toHexString(when)
651                        + " whenElapsed=" + whenElapsed + " maxWhen=" + maxWhen
652                        + " interval=" + interval + " op=" + operation
653                        + " standalone=" + isStandalone);
654                rebatchAllAlarmsLocked(false);
655            }
656        }
657
658        rescheduleKernelAlarmsLocked();
659    }
660
661    private final IBinder mService = new IAlarmManager.Stub() {
662        @Override
663        public void set(int type, long triggerAtTime, long windowLength, long interval,
664                PendingIntent operation, WorkSource workSource) {
665            if (workSource != null) {
666                getContext().enforceCallingPermission(
667                        android.Manifest.permission.UPDATE_DEVICE_STATS,
668                        "AlarmManager.set");
669            }
670
671            setImpl(type, triggerAtTime, windowLength, interval, operation,
672                    false, workSource);
673        }
674
675        @Override
676        public boolean setTime(long millis) {
677            getContext().enforceCallingOrSelfPermission(
678                    "android.permission.SET_TIME",
679                    "setTime");
680
681            if (mNativeData == 0) {
682                Slog.w(TAG, "Not setting time since no alarm driver is available.");
683                return false;
684            }
685
686            synchronized (mLock) {
687                return setKernelTime(mNativeData, millis) == 0;
688            }
689        }
690
691        @Override
692        public void setTimeZone(String tz) {
693            getContext().enforceCallingOrSelfPermission(
694                    "android.permission.SET_TIME_ZONE",
695                    "setTimeZone");
696
697            final long oldId = Binder.clearCallingIdentity();
698            try {
699                setTimeZoneImpl(tz);
700            } finally {
701                Binder.restoreCallingIdentity(oldId);
702            }
703        }
704
705        @Override
706        public void remove(PendingIntent operation) {
707            removeImpl(operation);
708
709        }
710
711        @Override
712        protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
713            if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
714                    != PackageManager.PERMISSION_GRANTED) {
715                pw.println("Permission Denial: can't dump AlarmManager from from pid="
716                        + Binder.getCallingPid()
717                        + ", uid=" + Binder.getCallingUid());
718                return;
719            }
720
721            dumpImpl(pw);
722        }
723    };
724
725    void dumpImpl(PrintWriter pw) {
726        synchronized (mLock) {
727            pw.println("Current Alarm Manager state:");
728            final long nowRTC = System.currentTimeMillis();
729            final long nowELAPSED = SystemClock.elapsedRealtime();
730            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
731
732            pw.print("nowRTC="); pw.print(nowRTC);
733            pw.print("="); pw.print(sdf.format(new Date(nowRTC)));
734            pw.print(" nowELAPSED="); pw.println(nowELAPSED);
735
736            long nextWakeupRTC = mNextWakeup + (nowRTC - nowELAPSED);
737            long nextNonWakeupRTC = mNextNonWakeup + (nowRTC - nowELAPSED);
738            pw.print("Next alarm: "); pw.print(mNextNonWakeup);
739                    pw.print(" = "); pw.println(sdf.format(new Date(nextNonWakeupRTC)));
740            pw.print("Next wakeup: "); pw.print(mNextWakeup);
741                    pw.print(" = "); pw.println(sdf.format(new Date(nextWakeupRTC)));
742
743            if (mAlarmBatches.size() > 0) {
744                pw.println();
745                pw.print("Pending alarm batches: ");
746                pw.println(mAlarmBatches.size());
747                for (Batch b : mAlarmBatches) {
748                    pw.print(b); pw.println(':');
749                    dumpAlarmList(pw, b.alarms, "  ", nowELAPSED, nowRTC);
750                }
751            }
752
753            pw.println();
754            pw.print("  Broadcast ref count: "); pw.println(mBroadcastRefCount);
755            pw.println();
756
757            if (mLog.dump(pw, "  Recent problems", "    ")) {
758                pw.println();
759            }
760
761            final FilterStats[] topFilters = new FilterStats[10];
762            final Comparator<FilterStats> comparator = new Comparator<FilterStats>() {
763                @Override
764                public int compare(FilterStats lhs, FilterStats rhs) {
765                    if (lhs.aggregateTime < rhs.aggregateTime) {
766                        return 1;
767                    } else if (lhs.aggregateTime > rhs.aggregateTime) {
768                        return -1;
769                    }
770                    return 0;
771                }
772            };
773            int len = 0;
774            for (int iu=0; iu<mBroadcastStats.size(); iu++) {
775                ArrayMap<String, BroadcastStats> uidStats = mBroadcastStats.valueAt(iu);
776                for (int ip=0; ip<uidStats.size(); ip++) {
777                    BroadcastStats bs = uidStats.valueAt(ip);
778                    for (int is=0; is<bs.filterStats.size(); is++) {
779                        FilterStats fs = bs.filterStats.valueAt(is);
780                        int pos = len > 0
781                                ? Arrays.binarySearch(topFilters, 0, len, fs, comparator) : 0;
782                        if (pos < 0) {
783                            pos = -pos - 1;
784                        }
785                        if (pos < topFilters.length) {
786                            int copylen = topFilters.length - pos - 1;
787                            if (copylen > 0) {
788                                System.arraycopy(topFilters, pos, topFilters, pos+1, copylen);
789                            }
790                            topFilters[pos] = fs;
791                            if (len < topFilters.length) {
792                                len++;
793                            }
794                        }
795                    }
796                }
797            }
798            if (len > 0) {
799                pw.println("  Top Alarms:");
800                for (int i=0; i<len; i++) {
801                    FilterStats fs = topFilters[i];
802                    pw.print("    ");
803                    if (fs.nesting > 0) pw.print("*ACTIVE* ");
804                    TimeUtils.formatDuration(fs.aggregateTime, pw);
805                    pw.print(" running, "); pw.print(fs.numWakeup);
806                    pw.print(" wakeups, "); pw.print(fs.count);
807                    pw.print(" alarms: "); UserHandle.formatUid(pw, fs.mBroadcastStats.mUid);
808                    pw.print(":"); pw.print(fs.mBroadcastStats.mPackageName);
809                    pw.println();
810                    pw.print("      ");
811                    if (fs.mTarget.first != null) {
812                        pw.print(" act="); pw.print(fs.mTarget.first);
813                    }
814                    if (fs.mTarget.second != null) {
815                        pw.print(" cmp="); pw.print(fs.mTarget.second.toShortString());
816                    }
817                    pw.println();
818                }
819            }
820
821            pw.println(" ");
822            pw.println("  Alarm Stats:");
823            final ArrayList<FilterStats> tmpFilters = new ArrayList<FilterStats>();
824            for (int iu=0; iu<mBroadcastStats.size(); iu++) {
825                ArrayMap<String, BroadcastStats> uidStats = mBroadcastStats.valueAt(iu);
826                for (int ip=0; ip<uidStats.size(); ip++) {
827                    BroadcastStats bs = uidStats.valueAt(ip);
828                    pw.print("  ");
829                    if (bs.nesting > 0) pw.print("*ACTIVE* ");
830                    UserHandle.formatUid(pw, bs.mUid);
831                    pw.print(":");
832                    pw.print(bs.mPackageName);
833                    pw.print(" "); TimeUtils.formatDuration(bs.aggregateTime, pw);
834                            pw.print(" running, "); pw.print(bs.numWakeup);
835                            pw.println(" wakeups:");
836                    tmpFilters.clear();
837                    for (int is=0; is<bs.filterStats.size(); is++) {
838                        tmpFilters.add(bs.filterStats.valueAt(is));
839                    }
840                    Collections.sort(tmpFilters, comparator);
841                    for (int i=0; i<tmpFilters.size(); i++) {
842                        FilterStats fs = tmpFilters.get(i);
843                        pw.print("    ");
844                                if (fs.nesting > 0) pw.print("*ACTIVE* ");
845                                TimeUtils.formatDuration(fs.aggregateTime, pw);
846                                pw.print(" "); pw.print(fs.numWakeup);
847                                pw.print(" wakes " ); pw.print(fs.count);
848                                pw.print(" alarms:");
849                                if (fs.mTarget.first != null) {
850                                    pw.print(" act="); pw.print(fs.mTarget.first);
851                                }
852                                if (fs.mTarget.second != null) {
853                                    pw.print(" cmp="); pw.print(fs.mTarget.second.toShortString());
854                                }
855                                pw.println();
856                    }
857                }
858            }
859
860            if (WAKEUP_STATS) {
861                pw.println();
862                pw.println("  Recent Wakeup History:");
863                long last = -1;
864                for (WakeupEvent event : mRecentWakeups) {
865                    pw.print("    "); pw.print(sdf.format(new Date(event.when)));
866                    pw.print('|');
867                    if (last < 0) {
868                        pw.print('0');
869                    } else {
870                        pw.print(event.when - last);
871                    }
872                    last = event.when;
873                    pw.print('|'); pw.print(event.uid);
874                    pw.print('|'); pw.print(event.action);
875                    pw.println();
876                }
877                pw.println();
878            }
879        }
880    }
881
882    private void logBatchesLocked() {
883        ByteArrayOutputStream bs = new ByteArrayOutputStream(2048);
884        PrintWriter pw = new PrintWriter(bs);
885        final long nowRTC = System.currentTimeMillis();
886        final long nowELAPSED = SystemClock.elapsedRealtime();
887        final int NZ = mAlarmBatches.size();
888        for (int iz = 0; iz < NZ; iz++) {
889            Batch bz = mAlarmBatches.get(iz);
890            pw.append("Batch "); pw.print(iz); pw.append(": "); pw.println(bz);
891            dumpAlarmList(pw, bz.alarms, "  ", nowELAPSED, nowRTC);
892            pw.flush();
893            Slog.v(TAG, bs.toString());
894            bs.reset();
895        }
896    }
897
898    private boolean validateConsistencyLocked() {
899        if (DEBUG_VALIDATE) {
900            long lastTime = Long.MIN_VALUE;
901            final int N = mAlarmBatches.size();
902            for (int i = 0; i < N; i++) {
903                Batch b = mAlarmBatches.get(i);
904                if (b.start >= lastTime) {
905                    // duplicate start times are okay because of standalone batches
906                    lastTime = b.start;
907                } else {
908                    Slog.e(TAG, "CONSISTENCY FAILURE: Batch " + i + " is out of order");
909                    logBatchesLocked();
910                    return false;
911                }
912            }
913        }
914        return true;
915    }
916
917    private Batch findFirstWakeupBatchLocked() {
918        final int N = mAlarmBatches.size();
919        for (int i = 0; i < N; i++) {
920            Batch b = mAlarmBatches.get(i);
921            if (b.hasWakeups()) {
922                return b;
923            }
924        }
925        return null;
926    }
927
928    void rescheduleKernelAlarmsLocked() {
929        // Schedule the next upcoming wakeup alarm.  If there is a deliverable batch
930        // prior to that which contains no wakeups, we schedule that as well.
931        if (mAlarmBatches.size() > 0) {
932            final Batch firstWakeup = findFirstWakeupBatchLocked();
933            final Batch firstBatch = mAlarmBatches.get(0);
934            if (firstWakeup != null && mNextWakeup != firstWakeup.start) {
935                mNextWakeup = firstWakeup.start;
936                setLocked(ELAPSED_REALTIME_WAKEUP, firstWakeup.start);
937            }
938            if (firstBatch != firstWakeup && mNextNonWakeup != firstBatch.start) {
939                mNextNonWakeup = firstBatch.start;
940                setLocked(ELAPSED_REALTIME, firstBatch.start);
941            }
942        }
943    }
944
945    private void removeLocked(PendingIntent operation) {
946        boolean didRemove = false;
947        for (int i = mAlarmBatches.size() - 1; i >= 0; i--) {
948            Batch b = mAlarmBatches.get(i);
949            didRemove |= b.remove(operation);
950            if (b.size() == 0) {
951                mAlarmBatches.remove(i);
952            }
953        }
954
955        if (didRemove) {
956            if (DEBUG_BATCH) {
957                Slog.v(TAG, "remove(operation) changed bounds; rebatching");
958            }
959            rebatchAllAlarmsLocked(true);
960            rescheduleKernelAlarmsLocked();
961        }
962    }
963
964    void removeLocked(String packageName) {
965        boolean didRemove = false;
966        for (int i = mAlarmBatches.size() - 1; i >= 0; i--) {
967            Batch b = mAlarmBatches.get(i);
968            didRemove |= b.remove(packageName);
969            if (b.size() == 0) {
970                mAlarmBatches.remove(i);
971            }
972        }
973
974        if (didRemove) {
975            if (DEBUG_BATCH) {
976                Slog.v(TAG, "remove(package) changed bounds; rebatching");
977            }
978            rebatchAllAlarmsLocked(true);
979            rescheduleKernelAlarmsLocked();
980        }
981    }
982
983    void removeUserLocked(int userHandle) {
984        boolean didRemove = false;
985        for (int i = mAlarmBatches.size() - 1; i >= 0; i--) {
986            Batch b = mAlarmBatches.get(i);
987            didRemove |= b.remove(userHandle);
988            if (b.size() == 0) {
989                mAlarmBatches.remove(i);
990            }
991        }
992
993        if (didRemove) {
994            if (DEBUG_BATCH) {
995                Slog.v(TAG, "remove(user) changed bounds; rebatching");
996            }
997            rebatchAllAlarmsLocked(true);
998            rescheduleKernelAlarmsLocked();
999        }
1000    }
1001
1002    boolean lookForPackageLocked(String packageName) {
1003        for (int i = 0; i < mAlarmBatches.size(); i++) {
1004            Batch b = mAlarmBatches.get(i);
1005            if (b.hasPackage(packageName)) {
1006                return true;
1007            }
1008        }
1009        return false;
1010    }
1011
1012    private void setLocked(int type, long when) {
1013        if (mNativeData != 0) {
1014            // The kernel never triggers alarms with negative wakeup times
1015            // so we ensure they are positive.
1016            long alarmSeconds, alarmNanoseconds;
1017            if (when < 0) {
1018                alarmSeconds = 0;
1019                alarmNanoseconds = 0;
1020            } else {
1021                alarmSeconds = when / 1000;
1022                alarmNanoseconds = (when % 1000) * 1000 * 1000;
1023            }
1024
1025            set(mNativeData, type, alarmSeconds, alarmNanoseconds);
1026        } else {
1027            Message msg = Message.obtain();
1028            msg.what = ALARM_EVENT;
1029
1030            mHandler.removeMessages(ALARM_EVENT);
1031            mHandler.sendMessageAtTime(msg, when);
1032        }
1033    }
1034
1035    private static final void dumpAlarmList(PrintWriter pw, ArrayList<Alarm> list,
1036            String prefix, String label, long now) {
1037        for (int i=list.size()-1; i>=0; i--) {
1038            Alarm a = list.get(i);
1039            pw.print(prefix); pw.print(label); pw.print(" #"); pw.print(i);
1040                    pw.print(": "); pw.println(a);
1041            a.dump(pw, prefix + "  ", now);
1042        }
1043    }
1044
1045    private static final String labelForType(int type) {
1046        switch (type) {
1047        case RTC: return "RTC";
1048        case RTC_WAKEUP : return "RTC_WAKEUP";
1049        case ELAPSED_REALTIME : return "ELAPSED";
1050        case ELAPSED_REALTIME_WAKEUP: return "ELAPSED_WAKEUP";
1051        default:
1052            break;
1053        }
1054        return "--unknown--";
1055    }
1056
1057    private static final void dumpAlarmList(PrintWriter pw, ArrayList<Alarm> list,
1058            String prefix, long nowELAPSED, long nowRTC) {
1059        for (int i=list.size()-1; i>=0; i--) {
1060            Alarm a = list.get(i);
1061            final String label = labelForType(a.type);
1062            long now = (a.type <= RTC) ? nowRTC : nowELAPSED;
1063            pw.print(prefix); pw.print(label); pw.print(" #"); pw.print(i);
1064                    pw.print(": "); pw.println(a);
1065            a.dump(pw, prefix + "  ", now);
1066        }
1067    }
1068
1069    private native long init();
1070    private native void close(long nativeData);
1071    private native void set(long nativeData, int type, long seconds, long nanoseconds);
1072    private native int waitForAlarm(long nativeData);
1073    private native int setKernelTime(long nativeData, long millis);
1074    private native int setKernelTimezone(long nativeData, int minuteswest);
1075
1076    void triggerAlarmsLocked(ArrayList<Alarm> triggerList, final long nowELAPSED,
1077            final long nowRTC) {
1078        // batches are temporally sorted, so we need only pull from the
1079        // start of the list until we either empty it or hit a batch
1080        // that is not yet deliverable
1081        while (mAlarmBatches.size() > 0) {
1082            Batch batch = mAlarmBatches.get(0);
1083            if (batch.start > nowELAPSED) {
1084                // Everything else is scheduled for the future
1085                break;
1086            }
1087
1088            // We will (re)schedule some alarms now; don't let that interfere
1089            // with delivery of this current batch
1090            mAlarmBatches.remove(0);
1091
1092            final int N = batch.size();
1093            for (int i = 0; i < N; i++) {
1094                Alarm alarm = batch.get(i);
1095                alarm.count = 1;
1096                triggerList.add(alarm);
1097
1098                // Recurring alarms may have passed several alarm intervals while the
1099                // phone was asleep or off, so pass a trigger count when sending them.
1100                if (alarm.repeatInterval > 0) {
1101                    // this adjustment will be zero if we're late by
1102                    // less than one full repeat interval
1103                    alarm.count += (nowELAPSED - alarm.whenElapsed) / alarm.repeatInterval;
1104
1105                    // Also schedule its next recurrence
1106                    final long delta = alarm.count * alarm.repeatInterval;
1107                    final long nextElapsed = alarm.whenElapsed + delta;
1108                    setImplLocked(alarm.type, alarm.when + delta, nextElapsed, alarm.windowLength,
1109                            maxTriggerTime(nowELAPSED, nextElapsed, alarm.repeatInterval),
1110                            alarm.repeatInterval, alarm.operation, batch.standalone, true,
1111                            alarm.workSource);
1112                }
1113
1114            }
1115        }
1116
1117        Collections.sort(triggerList, mAlarmDispatchComparator);
1118
1119        if (localLOGV) {
1120            for (int i=0; i<triggerList.size(); i++) {
1121                Slog.v(TAG, "Triggering alarm #" + i + ": " + triggerList.get(i));
1122            }
1123        }
1124    }
1125
1126    /**
1127     * This Comparator sorts Alarms into increasing time order.
1128     */
1129    public static class IncreasingTimeOrder implements Comparator<Alarm> {
1130        public int compare(Alarm a1, Alarm a2) {
1131            long when1 = a1.when;
1132            long when2 = a2.when;
1133            if (when1 - when2 > 0) {
1134                return 1;
1135            }
1136            if (when1 - when2 < 0) {
1137                return -1;
1138            }
1139            return 0;
1140        }
1141    }
1142
1143    private static class Alarm {
1144        public final int type;
1145        public final boolean wakeup;
1146        public int count;
1147        public long when;
1148        public long windowLength;
1149        public long whenElapsed;    // 'when' in the elapsed time base
1150        public long maxWhen;        // also in the elapsed time base
1151        public long repeatInterval;
1152        public PendingIntent operation;
1153        public WorkSource workSource;
1154
1155        public Alarm(int _type, long _when, long _whenElapsed, long _windowLength, long _maxWhen,
1156                long _interval, PendingIntent _op, WorkSource _ws) {
1157            type = _type;
1158            wakeup = _type == AlarmManager.ELAPSED_REALTIME_WAKEUP
1159                    || _type == AlarmManager.RTC_WAKEUP;
1160            when = _when;
1161            whenElapsed = _whenElapsed;
1162            windowLength = _windowLength;
1163            maxWhen = _maxWhen;
1164            repeatInterval = _interval;
1165            operation = _op;
1166            workSource = _ws;
1167        }
1168
1169        @Override
1170        public String toString()
1171        {
1172            StringBuilder sb = new StringBuilder(128);
1173            sb.append("Alarm{");
1174            sb.append(Integer.toHexString(System.identityHashCode(this)));
1175            sb.append(" type ");
1176            sb.append(type);
1177            sb.append(" when ");
1178            sb.append(when);
1179            sb.append(" ");
1180            sb.append(operation.getTargetPackage());
1181            sb.append('}');
1182            return sb.toString();
1183        }
1184
1185        public void dump(PrintWriter pw, String prefix, long now) {
1186            pw.print(prefix); pw.print("type="); pw.print(type);
1187                    pw.print(" whenElapsed="); pw.print(whenElapsed);
1188                    pw.print(" when="); TimeUtils.formatDuration(when, now, pw);
1189                    pw.print(" window="); pw.print(windowLength);
1190                    pw.print(" repeatInterval="); pw.print(repeatInterval);
1191                    pw.print(" count="); pw.println(count);
1192            pw.print(prefix); pw.print("operation="); pw.println(operation);
1193        }
1194    }
1195
1196    void recordWakeupAlarms(ArrayList<Batch> batches, long nowELAPSED, long nowRTC) {
1197        final int numBatches = batches.size();
1198        for (int nextBatch = 0; nextBatch < numBatches; nextBatch++) {
1199            Batch b = batches.get(nextBatch);
1200            if (b.start > nowELAPSED) {
1201                break;
1202            }
1203
1204            final int numAlarms = b.alarms.size();
1205            for (int nextAlarm = 0; nextAlarm < numAlarms; nextAlarm++) {
1206                Alarm a = b.alarms.get(nextAlarm);
1207                WakeupEvent e = new WakeupEvent(nowRTC,
1208                        a.operation.getCreatorUid(),
1209                        a.operation.getIntent().getAction());
1210                mRecentWakeups.add(e);
1211            }
1212        }
1213    }
1214
1215    private class AlarmThread extends Thread
1216    {
1217        public AlarmThread()
1218        {
1219            super("AlarmManager");
1220        }
1221
1222        public void run()
1223        {
1224            ArrayList<Alarm> triggerList = new ArrayList<Alarm>();
1225
1226            while (true)
1227            {
1228                int result = waitForAlarm(mNativeData);
1229
1230                triggerList.clear();
1231
1232                if ((result & TIME_CHANGED_MASK) != 0) {
1233                    if (DEBUG_BATCH) {
1234                        Slog.v(TAG, "Time changed notification from kernel; rebatching");
1235                    }
1236                    removeImpl(mTimeTickSender);
1237                    rebatchAllAlarms();
1238                    mClockReceiver.scheduleTimeTickEvent();
1239                    Intent intent = new Intent(Intent.ACTION_TIME_CHANGED);
1240                    intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING
1241                            | Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
1242                    getContext().sendBroadcastAsUser(intent, UserHandle.ALL);
1243                }
1244
1245                synchronized (mLock) {
1246                    final long nowRTC = System.currentTimeMillis();
1247                    final long nowELAPSED = SystemClock.elapsedRealtime();
1248                    if (localLOGV) Slog.v(
1249                        TAG, "Checking for alarms... rtc=" + nowRTC
1250                        + ", elapsed=" + nowELAPSED);
1251
1252                    if (WAKEUP_STATS) {
1253                        if ((result & IS_WAKEUP_MASK) != 0) {
1254                            long newEarliest = nowRTC - RECENT_WAKEUP_PERIOD;
1255                            int n = 0;
1256                            for (WakeupEvent event : mRecentWakeups) {
1257                                if (event.when > newEarliest) break;
1258                                n++; // number of now-stale entries at the list head
1259                            }
1260                            for (int i = 0; i < n; i++) {
1261                                mRecentWakeups.remove();
1262                            }
1263
1264                            recordWakeupAlarms(mAlarmBatches, nowELAPSED, nowRTC);
1265                        }
1266                    }
1267
1268                    triggerAlarmsLocked(triggerList, nowELAPSED, nowRTC);
1269                    rescheduleKernelAlarmsLocked();
1270
1271                    // now deliver the alarm intents
1272                    for (int i=0; i<triggerList.size(); i++) {
1273                        Alarm alarm = triggerList.get(i);
1274                        try {
1275                            if (localLOGV) Slog.v(TAG, "sending alarm " + alarm);
1276                            alarm.operation.send(getContext(), 0,
1277                                    mBackgroundIntent.putExtra(
1278                                            Intent.EXTRA_ALARM_COUNT, alarm.count),
1279                                    mResultReceiver, mHandler);
1280
1281                            // we have an active broadcast so stay awake.
1282                            if (mBroadcastRefCount == 0) {
1283                                setWakelockWorkSource(alarm.operation, alarm.workSource);
1284                                mWakeLock.setUnimportantForLogging(
1285                                        alarm.operation == mTimeTickSender);
1286                                mWakeLock.setHistoryTag(alarm.operation.getTag("*alarm*:"));
1287                                mWakeLock.acquire();
1288                            }
1289                            final InFlight inflight = new InFlight(AlarmManagerService.this,
1290                                    alarm.operation, alarm.workSource);
1291                            mInFlight.add(inflight);
1292                            mBroadcastRefCount++;
1293
1294                            final BroadcastStats bs = inflight.mBroadcastStats;
1295                            bs.count++;
1296                            if (bs.nesting == 0) {
1297                                bs.nesting = 1;
1298                                bs.startTime = nowELAPSED;
1299                            } else {
1300                                bs.nesting++;
1301                            }
1302                            final FilterStats fs = inflight.mFilterStats;
1303                            fs.count++;
1304                            if (fs.nesting == 0) {
1305                                fs.nesting = 1;
1306                                fs.startTime = nowELAPSED;
1307                            } else {
1308                                fs.nesting++;
1309                            }
1310                            if (alarm.type == ELAPSED_REALTIME_WAKEUP
1311                                    || alarm.type == RTC_WAKEUP) {
1312                                bs.numWakeup++;
1313                                fs.numWakeup++;
1314                                if (alarm.workSource != null && alarm.workSource.size() > 0) {
1315                                    for (int wi=0; wi<alarm.workSource.size(); wi++) {
1316                                        ActivityManagerNative.noteWakeupAlarm(
1317                                                alarm.operation, alarm.workSource.get(wi),
1318                                                alarm.workSource.getName(wi));
1319                                    }
1320                                } else {
1321                                    ActivityManagerNative.noteWakeupAlarm(
1322                                            alarm.operation, -1, null);
1323                                }
1324                            }
1325                        } catch (PendingIntent.CanceledException e) {
1326                            if (alarm.repeatInterval > 0) {
1327                                // This IntentSender is no longer valid, but this
1328                                // is a repeating alarm, so toss the hoser.
1329                                removeImpl(alarm.operation);
1330                            }
1331                        } catch (RuntimeException e) {
1332                            Slog.w(TAG, "Failure sending alarm.", e);
1333                        }
1334                    }
1335                }
1336            }
1337        }
1338    }
1339
1340    /**
1341     * Attribute blame for a WakeLock.
1342     * @param pi PendingIntent to attribute blame to if ws is null.
1343     * @param ws WorkSource to attribute blame.
1344     */
1345    void setWakelockWorkSource(PendingIntent pi, WorkSource ws) {
1346        try {
1347            if (ws != null) {
1348                mWakeLock.setWorkSource(ws);
1349                return;
1350            }
1351
1352            final int uid = ActivityManagerNative.getDefault()
1353                    .getUidForIntentSender(pi.getTarget());
1354            if (uid >= 0) {
1355                mWakeLock.setWorkSource(new WorkSource(uid));
1356                return;
1357            }
1358        } catch (Exception e) {
1359        }
1360
1361        // Something went wrong; fall back to attributing the lock to the OS
1362        mWakeLock.setWorkSource(null);
1363    }
1364
1365    private class AlarmHandler extends Handler {
1366        public static final int ALARM_EVENT = 1;
1367        public static final int MINUTE_CHANGE_EVENT = 2;
1368        public static final int DATE_CHANGE_EVENT = 3;
1369
1370        public AlarmHandler() {
1371        }
1372
1373        public void handleMessage(Message msg) {
1374            if (msg.what == ALARM_EVENT) {
1375                ArrayList<Alarm> triggerList = new ArrayList<Alarm>();
1376                synchronized (mLock) {
1377                    final long nowRTC = System.currentTimeMillis();
1378                    final long nowELAPSED = SystemClock.elapsedRealtime();
1379                    triggerAlarmsLocked(triggerList, nowELAPSED, nowRTC);
1380                }
1381
1382                // now trigger the alarms without the lock held
1383                for (int i=0; i<triggerList.size(); i++) {
1384                    Alarm alarm = triggerList.get(i);
1385                    try {
1386                        alarm.operation.send();
1387                    } catch (PendingIntent.CanceledException e) {
1388                        if (alarm.repeatInterval > 0) {
1389                            // This IntentSender is no longer valid, but this
1390                            // is a repeating alarm, so toss the hoser.
1391                            removeImpl(alarm.operation);
1392                        }
1393                    }
1394                }
1395            }
1396        }
1397    }
1398
1399    class ClockReceiver extends BroadcastReceiver {
1400        public ClockReceiver() {
1401            IntentFilter filter = new IntentFilter();
1402            filter.addAction(Intent.ACTION_TIME_TICK);
1403            filter.addAction(Intent.ACTION_DATE_CHANGED);
1404            getContext().registerReceiver(this, filter);
1405        }
1406
1407        @Override
1408        public void onReceive(Context context, Intent intent) {
1409            if (intent.getAction().equals(Intent.ACTION_TIME_TICK)) {
1410                if (DEBUG_BATCH) {
1411                    Slog.v(TAG, "Received TIME_TICK alarm; rescheduling");
1412                }
1413                scheduleTimeTickEvent();
1414            } else if (intent.getAction().equals(Intent.ACTION_DATE_CHANGED)) {
1415                // Since the kernel does not keep track of DST, we need to
1416                // reset the TZ information at the beginning of each day
1417                // based off of the current Zone gmt offset + userspace tracked
1418                // daylight savings information.
1419                TimeZone zone = TimeZone.getTimeZone(SystemProperties.get(TIMEZONE_PROPERTY));
1420                int gmtOffset = zone.getOffset(System.currentTimeMillis());
1421                setKernelTimezone(mNativeData, -(gmtOffset / 60000));
1422                scheduleDateChangedEvent();
1423            }
1424        }
1425
1426        public void scheduleTimeTickEvent() {
1427            final long currentTime = System.currentTimeMillis();
1428            final long nextTime = 60000 * ((currentTime / 60000) + 1);
1429
1430            // Schedule this event for the amount of time that it would take to get to
1431            // the top of the next minute.
1432            final long tickEventDelay = nextTime - currentTime;
1433
1434            final WorkSource workSource = null; // Let system take blame for time tick events.
1435            setImpl(ELAPSED_REALTIME, SystemClock.elapsedRealtime() + tickEventDelay, 0,
1436                    0, mTimeTickSender, true, workSource);
1437        }
1438
1439        public void scheduleDateChangedEvent() {
1440            Calendar calendar = Calendar.getInstance();
1441            calendar.setTimeInMillis(System.currentTimeMillis());
1442            calendar.set(Calendar.HOUR, 0);
1443            calendar.set(Calendar.MINUTE, 0);
1444            calendar.set(Calendar.SECOND, 0);
1445            calendar.set(Calendar.MILLISECOND, 0);
1446            calendar.add(Calendar.DAY_OF_MONTH, 1);
1447
1448            final WorkSource workSource = null; // Let system take blame for date change events.
1449            setImpl(RTC, calendar.getTimeInMillis(), 0, 0, mDateChangeSender, true, workSource);
1450        }
1451    }
1452
1453    class UninstallReceiver extends BroadcastReceiver {
1454        public UninstallReceiver() {
1455            IntentFilter filter = new IntentFilter();
1456            filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
1457            filter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
1458            filter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
1459            filter.addDataScheme("package");
1460            getContext().registerReceiver(this, filter);
1461             // Register for events related to sdcard installation.
1462            IntentFilter sdFilter = new IntentFilter();
1463            sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
1464            sdFilter.addAction(Intent.ACTION_USER_STOPPED);
1465            getContext().registerReceiver(this, sdFilter);
1466        }
1467
1468        @Override
1469        public void onReceive(Context context, Intent intent) {
1470            synchronized (mLock) {
1471                String action = intent.getAction();
1472                String pkgList[] = null;
1473                if (Intent.ACTION_QUERY_PACKAGE_RESTART.equals(action)) {
1474                    pkgList = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);
1475                    for (String packageName : pkgList) {
1476                        if (lookForPackageLocked(packageName)) {
1477                            setResultCode(Activity.RESULT_OK);
1478                            return;
1479                        }
1480                    }
1481                    return;
1482                } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
1483                    pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
1484                } else if (Intent.ACTION_USER_STOPPED.equals(action)) {
1485                    int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
1486                    if (userHandle >= 0) {
1487                        removeUserLocked(userHandle);
1488                    }
1489                } else {
1490                    if (Intent.ACTION_PACKAGE_REMOVED.equals(action)
1491                            && intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
1492                        // This package is being updated; don't kill its alarms.
1493                        return;
1494                    }
1495                    Uri data = intent.getData();
1496                    if (data != null) {
1497                        String pkg = data.getSchemeSpecificPart();
1498                        if (pkg != null) {
1499                            pkgList = new String[]{pkg};
1500                        }
1501                    }
1502                }
1503                if (pkgList != null && (pkgList.length > 0)) {
1504                    for (String pkg : pkgList) {
1505                        removeLocked(pkg);
1506                        for (int i=mBroadcastStats.size()-1; i>=0; i--) {
1507                            ArrayMap<String, BroadcastStats> uidStats = mBroadcastStats.valueAt(i);
1508                            if (uidStats.remove(pkg) != null) {
1509                                if (uidStats.size() <= 0) {
1510                                    mBroadcastStats.removeAt(i);
1511                                }
1512                            }
1513                        }
1514                    }
1515                }
1516            }
1517        }
1518    }
1519
1520    private final BroadcastStats getStatsLocked(PendingIntent pi) {
1521        String pkg = pi.getCreatorPackage();
1522        int uid = pi.getCreatorUid();
1523        ArrayMap<String, BroadcastStats> uidStats = mBroadcastStats.get(uid);
1524        if (uidStats == null) {
1525            uidStats = new ArrayMap<String, BroadcastStats>();
1526            mBroadcastStats.put(uid, uidStats);
1527        }
1528        BroadcastStats bs = uidStats.get(pkg);
1529        if (bs == null) {
1530            bs = new BroadcastStats(uid, pkg);
1531            uidStats.put(pkg, bs);
1532        }
1533        return bs;
1534    }
1535
1536    class ResultReceiver implements PendingIntent.OnFinished {
1537        public void onSendFinished(PendingIntent pi, Intent intent, int resultCode,
1538                String resultData, Bundle resultExtras) {
1539            synchronized (mLock) {
1540                InFlight inflight = null;
1541                for (int i=0; i<mInFlight.size(); i++) {
1542                    if (mInFlight.get(i).mPendingIntent == pi) {
1543                        inflight = mInFlight.remove(i);
1544                        break;
1545                    }
1546                }
1547                if (inflight != null) {
1548                    final long nowELAPSED = SystemClock.elapsedRealtime();
1549                    BroadcastStats bs = inflight.mBroadcastStats;
1550                    bs.nesting--;
1551                    if (bs.nesting <= 0) {
1552                        bs.nesting = 0;
1553                        bs.aggregateTime += nowELAPSED - bs.startTime;
1554                    }
1555                    FilterStats fs = inflight.mFilterStats;
1556                    fs.nesting--;
1557                    if (fs.nesting <= 0) {
1558                        fs.nesting = 0;
1559                        fs.aggregateTime += nowELAPSED - fs.startTime;
1560                    }
1561                } else {
1562                    mLog.w("No in-flight alarm for " + pi + " " + intent);
1563                }
1564                mBroadcastRefCount--;
1565                if (mBroadcastRefCount == 0) {
1566                    mWakeLock.release();
1567                    if (mInFlight.size() > 0) {
1568                        mLog.w("Finished all broadcasts with " + mInFlight.size()
1569                                + " remaining inflights");
1570                        for (int i=0; i<mInFlight.size(); i++) {
1571                            mLog.w("  Remaining #" + i + ": " + mInFlight.get(i));
1572                        }
1573                        mInFlight.clear();
1574                    }
1575                } else {
1576                    // the next of our alarms is now in flight.  reattribute the wakelock.
1577                    if (mInFlight.size() > 0) {
1578                        InFlight inFlight = mInFlight.get(0);
1579                        setWakelockWorkSource(inFlight.mPendingIntent, inFlight.mWorkSource);
1580                    } else {
1581                        // should never happen
1582                        mLog.w("Alarm wakelock still held but sent queue empty");
1583                        mWakeLock.setWorkSource(null);
1584                    }
1585                }
1586            }
1587        }
1588    }
1589}
1590