FileDocCategorySizeDatePackage
AlarmManagerService.javaAPI DocAndroid 1.5 API31467Wed May 06 22:42:00 BST 2009com.android.server

AlarmManagerService

public class AlarmManagerService extends IAlarmManager.Stub

Fields Summary
private static final long
LATE_ALARM_THRESHOLD
private static final int
RTC_WAKEUP_MASK
private static final int
RTC_MASK
private static final int
ELAPSED_REALTIME_WAKEUP_MASK
private static final int
ELAPSED_REALTIME_MASK
private static final int
TIME_CHANGED_MASK
private static final String
TAG
private static final String
ClockReceiver_TAG
private static final boolean
localLOGV
private static final int
ALARM_EVENT
private static final String
TIMEZONE_PROPERTY
private static final android.content.Intent
mBackgroundIntent
private final android.content.Context
mContext
private Object
mLock
private final ArrayList
mRtcWakeupAlarms
private final ArrayList
mRtcAlarms
private final ArrayList
mElapsedRealtimeWakeupAlarms
private final ArrayList
mElapsedRealtimeAlarms
private final IncreasingTimeOrder
mIncreasingTimeOrder
private static final long[]
sInexactSlotIntervals
private long[]
mInexactDeliveryTimes
private int
mDescriptor
private int
mBroadcastRefCount
private PowerManager.WakeLock
mWakeLock
private final AlarmThread
mWaitThread
private final AlarmHandler
mHandler
private ClockReceiver
mClockReceiver
private UninstallReceiver
mUninstallReceiver
private final ResultReceiver
mResultReceiver
private final android.app.PendingIntent
mTimeTickSender
private final android.app.PendingIntent
mDateChangeSender
private final HashMap
mBroadcastStats
Constructors Summary
public AlarmManagerService(android.content.Context context)

    
       
        mContext = context;
        mDescriptor = init();
        PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
        mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
        
        mTimeTickSender = PendingIntent.getBroadcast(context, 0,
                new Intent(Intent.ACTION_TIME_TICK).addFlags(
                        Intent.FLAG_RECEIVER_REGISTERED_ONLY), 0);
        mDateChangeSender = PendingIntent.getBroadcast(context, 0,
                new Intent(Intent.ACTION_DATE_CHANGED), 0);
        
        // now that we have initied the driver schedule the alarm
        mClockReceiver= new ClockReceiver();
        mClockReceiver.scheduleTimeTickEvent();
        mClockReceiver.scheduleDateChangedEvent();
        mUninstallReceiver = new UninstallReceiver();
        
        if (mDescriptor != -1) {
            mWaitThread.start();
        } else {
            Log.w(TAG, "Failed to open alarm driver. Falling back to a handler.");
        }
    
Methods Summary
private intaddAlarmLocked(com.android.server.AlarmManagerService$Alarm alarm)

        ArrayList<Alarm> alarmList = getAlarmList(alarm.type);
        
        int index = Collections.binarySearch(alarmList, alarm, mIncreasingTimeOrder);
        if (index < 0) {
            index = 0 - index - 1;
        }
        if (localLOGV) Log.v(TAG, "Adding alarm " + alarm + " at " + index);
        alarmList.add(index, alarm);

        if (localLOGV) {
            // Display the list of alarms for this alarm type
            Log.v(TAG, "alarms: " + alarmList.size() + " type: " + alarm.type);
            int position = 0;
            for (Alarm a : alarmList) {
                Time time = new Time();
                time.set(a.when);
                String timeStr = time.format("%b %d %I:%M:%S %p");
                Log.v(TAG, position + ": " + timeStr
                        + " " + a.operation.getTargetPackage());
                position += 1;
            }
        }
        
        return index;
    
private native voidclose(int fd)

protected voiddump(java.io.FileDescriptor fd, java.io.PrintWriter pw, java.lang.String[] args)

        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
                != PackageManager.PERMISSION_GRANTED) {
            pw.println("Permission Denial: can't dump AlarmManager from from pid="
                    + Binder.getCallingPid()
                    + ", uid=" + Binder.getCallingUid());
            return;
        }
        
        synchronized (mLock) {
            pw.println("Current Alarm Manager state:");
            if (mRtcWakeupAlarms.size() > 0) {
                pw.println(" ");
                pw.println("  Realtime wakeup alarms that are scheduled:");
                dumpAlarmList(pw, mRtcWakeupAlarms, "  ", "RTC_WAKEUP");
            }
            if (mRtcAlarms.size() > 0) {
                pw.println(" ");
                pw.println("  Realtime alarms that are scheduled:");
                dumpAlarmList(pw, mRtcAlarms, "  ", "RTC");
            }
            if (mElapsedRealtimeWakeupAlarms.size() > 0) {
                pw.println(" ");
                pw.println("  Elapsed realtime wakeup alarms that are scheduled:");
                dumpAlarmList(pw, mElapsedRealtimeWakeupAlarms, "  ", "ELAPSED_REALTIME_WAKEUP");
            }
            if (mElapsedRealtimeAlarms.size() > 0) {
                pw.println(" ");
                pw.println("  Elapsed realtime alarms that are scheduled:");
                dumpAlarmList(pw, mElapsedRealtimeAlarms, "  ", "ELAPSED_REALTIME");
            }
            
            pw.println(" ");
            pw.println("  Broadcast ref count: " + mBroadcastRefCount);
            
            pw.println(" ");
            pw.println("  Alarm Stats:");
            for (Map.Entry<String, BroadcastStats> be : mBroadcastStats.entrySet()) {
                BroadcastStats bs = be.getValue();
                pw.println("  " + be.getKey());
                pw.println("    " + bs.aggregateTime + "ms running, "
                        + bs.numWakeup + " wakeups");
                for (Map.Entry<Intent.FilterComparison, FilterStats> fe
                        : bs.filterStats.entrySet()) {
                    pw.println("    " + fe.getValue().count + " alarms: "
                            + fe.getKey().getIntent());
                }
            }
        }
    
private static final voiddumpAlarmList(java.io.PrintWriter pw, java.util.ArrayList list, java.lang.String prefix, java.lang.String label)

        for (int i=list.size()-1; i>=0; i--) {
            Alarm a = list.get(i);
            pw.println(prefix + label + " #" + i + ":");
            a.dump(pw, prefix + "  ");
        }
    
protected voidfinalize()

        try {
            close(mDescriptor);
        } finally {
            super.finalize();
        }
    
private java.util.ArrayListgetAlarmList(int type)

        switch (type) {
            case AlarmManager.RTC_WAKEUP:              return mRtcWakeupAlarms;
            case AlarmManager.RTC:                     return mRtcAlarms;
            case AlarmManager.ELAPSED_REALTIME_WAKEUP: return mElapsedRealtimeWakeupAlarms;
            case AlarmManager.ELAPSED_REALTIME:        return mElapsedRealtimeAlarms;
        }
        
        return null;
    
private final com.android.server.AlarmManagerService$BroadcastStatsgetStatsLocked(android.app.PendingIntent pi)

        String pkg = pi.getTargetPackage();
        BroadcastStats bs = mBroadcastStats.get(pkg);
        if (bs == null) {
            bs = new BroadcastStats();
            mBroadcastStats.put(pkg, bs);
        }
        return bs;
    
private native intinit()

public voidremove(android.app.PendingIntent operation)

        if (operation == null) {
            return;
        }
        synchronized (mLock) {
            removeLocked(operation);
        }
    
public voidremoveLocked(java.lang.String packageName)

        removeLocked(mRtcWakeupAlarms, packageName);
        removeLocked(mRtcAlarms, packageName);
        removeLocked(mElapsedRealtimeWakeupAlarms, packageName);
        removeLocked(mElapsedRealtimeAlarms, packageName);
    
private voidremoveLocked(java.util.ArrayList alarmList, java.lang.String packageName)

        if (alarmList.size() <= 0) {
            return;
        }

        // iterator over the list removing any it where the intent match
        Iterator<Alarm> it = alarmList.iterator();
        
        while (it.hasNext()) {
            Alarm alarm = it.next();
            if (alarm.operation.getTargetPackage().equals(packageName)) {
                it.remove();
            }
        }
    
public voidremoveLocked(android.app.PendingIntent operation)

        removeLocked(mRtcWakeupAlarms, operation);
        removeLocked(mRtcAlarms, operation);
        removeLocked(mElapsedRealtimeWakeupAlarms, operation);
        removeLocked(mElapsedRealtimeAlarms, operation);
    
private voidremoveLocked(java.util.ArrayList alarmList, android.app.PendingIntent operation)

        if (alarmList.size() <= 0) {
            return;
        }

        // iterator over the list removing any it where the intent match
        Iterator<Alarm> it = alarmList.iterator();
        
        while (it.hasNext()) {
            Alarm alarm = it.next();
            if (alarm.operation.equals(operation)) {
                it.remove();
            }
        }
    
private native voidset(int fd, int type, long nanoseconds)

public voidset(int type, long triggerAtTime, android.app.PendingIntent operation)

        setRepeating(type, triggerAtTime, 0, operation);
    
public voidsetInexactRepeating(int type, long triggerAtTime, long interval, android.app.PendingIntent operation)

        if (operation == null) {
            Log.w(TAG, "setInexactRepeating ignored because there is no intent");
            return;
        }

        // find the slot in the delivery-times array that we will use
        int intervalSlot;
        for (intervalSlot = 0; intervalSlot < sInexactSlotIntervals.length; intervalSlot++) {
            if (sInexactSlotIntervals[intervalSlot] == interval) {
                break;
            }
        }
        
        // Non-bucket intervals just fall back to the less-efficient
        // unbucketed recurring alarm implementation
        if (intervalSlot >= sInexactSlotIntervals.length) {
            setRepeating(type, triggerAtTime, interval, operation);
            return;
        }

        // Align bucketed alarm deliveries by trying to match
        // the shortest-interval bucket already scheduled
        long bucketTime = 0;
        for (int slot = 0; slot < mInexactDeliveryTimes.length; slot++) {
            if (mInexactDeliveryTimes[slot] > 0) {
                bucketTime = mInexactDeliveryTimes[slot];
                break;
            }
        }
        
        if (bucketTime == 0) {
            // If nothing is scheduled yet, just start at the requested time
            bucketTime = triggerAtTime;
        } else {
            // Align the new alarm with the existing bucketed sequence.  To achieve
            // alignment, we slide the start time around by min{interval, slot interval}
            long adjustment = (interval <= sInexactSlotIntervals[intervalSlot])
                    ? interval : sInexactSlotIntervals[intervalSlot];

            // The bucket may have started in the past; adjust
            while (bucketTime < triggerAtTime) {
                bucketTime += adjustment;
            }

            // Or the bucket may be set to start more than an interval beyond
            // our requested trigger time; pull it back to meet our needs
            while (bucketTime > triggerAtTime + adjustment) {
                bucketTime -= adjustment;
            }
        }

        // Remember where this bucket started (reducing the amount of later 
        // fixup required) and set the alarm with the new, bucketed start time.
        if (localLOGV) Log.v(TAG, "setInexactRepeating: interval=" + interval
                + " bucketTime=" + bucketTime);
        mInexactDeliveryTimes[intervalSlot] = bucketTime;
        setRepeating(type, bucketTime, interval, operation);
    
private native intsetKernelTimezone(int fd, int minuteswest)

private voidsetLocked(com.android.server.AlarmManagerService$Alarm alarm)

        if (mDescriptor != -1)
        {
            set(mDescriptor, alarm.type, (alarm.when * 1000 * 1000));
        }
        else
        {
            Message msg = Message.obtain();
            msg.what = ALARM_EVENT;
            
            mHandler.removeMessages(ALARM_EVENT);
            mHandler.sendMessageAtTime(msg, alarm.when);
        }
    
public voidsetRepeating(int type, long triggerAtTime, long interval, android.app.PendingIntent operation)

        if (operation == null) {
            Log.w(TAG, "set/setRepeating ignored because there is no intent");
            return;
        }
        synchronized (mLock) {
            Alarm alarm = new Alarm();
            alarm.type = type;
            alarm.when = triggerAtTime;
            alarm.repeatInterval = interval;
            alarm.operation = operation;

            // Remove this alarm if already scheduled.
            removeLocked(operation);

            if (localLOGV) Log.v(TAG, "set: " + alarm);

            int index = addAlarmLocked(alarm);
            if (index == 0) {
                setLocked(alarm);
            }
        }
    
public voidsetTimeZone(java.lang.String tz)

        mContext.enforceCallingOrSelfPermission(
                "android.permission.SET_TIME_ZONE",
                "setTimeZone");

        if (TextUtils.isEmpty(tz)) return;
        TimeZone zone = TimeZone.getTimeZone(tz);
        // Prevent reentrant calls from stepping on each other when writing
        // the time zone property
        boolean timeZoneWasChanged = false;
        synchronized (this) {
            String current = SystemProperties.get(TIMEZONE_PROPERTY);
            if (current == null || !current.equals(zone.getID())) {
                if (localLOGV) Log.v(TAG, "timezone changed: " + current + ", new=" + zone.getID());
                timeZoneWasChanged = true; 
                SystemProperties.set(TIMEZONE_PROPERTY, zone.getID());
            }
            
            // Update the kernel timezone information
            // Kernel tracks time offsets as 'minutes west of GMT'
            int gmtOffset = (zone.getRawOffset() + zone.getDSTSavings()) / 60000;
            setKernelTimezone(mDescriptor, -(gmtOffset));
        }

        TimeZone.setDefault(null);
        
        if (timeZoneWasChanged) {
            Intent intent = new Intent(Intent.ACTION_TIMEZONE_CHANGED);
            intent.putExtra("time-zone", zone.getID());
            mContext.sendBroadcast(intent);
        }
    
public longtimeToNextAlarm()

        long nextAlarm = 0xfffffffffffffffl;
        synchronized (mLock) {
            for (int i=AlarmManager.RTC_WAKEUP;
                    i<=AlarmManager.ELAPSED_REALTIME; i++) {
                ArrayList<Alarm> alarmList = getAlarmList(i);
                if (alarmList.size() > 0) {
                    Alarm a = alarmList.get(0);
                    if (a.when < nextAlarm) {
                        nextAlarm = a.when;
                    }
                }
            }
        }
        return nextAlarm;
    
private voidtriggerAlarmsLocked(java.util.ArrayList alarmList, java.util.ArrayList triggerList, long now)

        Iterator<Alarm> it = alarmList.iterator();
        ArrayList<Alarm> repeats = new ArrayList<Alarm>();
        
        while (it.hasNext())
        {
            Alarm alarm = it.next();

            if (localLOGV) Log.v(TAG, "Checking active alarm when=" + alarm.when + " " + alarm);

            if (alarm.when > now) {
                // don't fire alarms in the future
                break;
            }
            
            // If the alarm is late, then print a warning message.
            // Note that this can happen if the user creates a new event on
            // the Calendar app with a reminder that is in the past. In that
            // case, the reminder alarm will fire immediately.
            if (localLOGV && now - alarm.when > LATE_ALARM_THRESHOLD) {
                Log.v(TAG, "alarm is late! alarm time: " + alarm.when
                        + " now: " + now + " delay (in seconds): "
                        + (now - alarm.when) / 1000);
            }

            // Recurring alarms may have passed several alarm intervals while the
            // phone was asleep or off, so pass a trigger count when sending them.
            if (localLOGV) Log.v(TAG, "Alarm triggering: " + alarm);
            alarm.count = 1;
            if (alarm.repeatInterval > 0) {
                // this adjustment will be zero if we're late by
                // less than one full repeat interval
                alarm.count += (now - alarm.when) / alarm.repeatInterval;
            }
            triggerList.add(alarm);
            
            // remove the alarm from the list
            it.remove();
            
            // if it repeats queue it up to be read-added to the list
            if (alarm.repeatInterval > 0) {
                repeats.add(alarm);
            }
        }

        // reset any repeating alarms.
        it = repeats.iterator();
        while (it.hasNext()) {
            Alarm alarm = it.next();
            alarm.when += alarm.count * alarm.repeatInterval;
            addAlarmLocked(alarm);
        }
        
        if (alarmList.size() > 0) {
            setLocked(alarmList.get(0));
        }
    
private native intwaitForAlarm(int fd)