SipWakeupTimer.java revision 42cdc7a9d3f94260768f38de4f477ec4e418e19a
14cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan/*
24cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan * Copyright (C) 2011, The Android Open Source Project
34cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan *
44cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan * Licensed under the Apache License, Version 2.0 (the "License");
54cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan * you may not use this file except in compliance with the License.
64cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan * You may obtain a copy of the License at
74cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan *
84cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan *     http://www.apache.org/licenses/LICENSE-2.0
94cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan *
104cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan * Unless required by applicable law or agreed to in writing, software
114cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan * distributed under the License is distributed on an "AS IS" BASIS,
124cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
134cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan * See the License for the specific language governing permissions and
144cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan * limitations under the License.
154cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan */
164cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan
174cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyanpackage com.android.server.sip;
184cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan
194cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyanimport android.app.AlarmManager;
204cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyanimport android.app.PendingIntent;
214cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyanimport android.content.BroadcastReceiver;
224cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyanimport android.content.Context;
234cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyanimport android.content.Intent;
244cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyanimport android.content.IntentFilter;
254cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyanimport android.os.SystemClock;
264cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyanimport android.util.Log;
274cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan
284cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyanimport java.io.IOException;
294cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyanimport java.net.DatagramSocket;
304cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyanimport java.net.InetAddress;
314cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyanimport java.net.UnknownHostException;
324cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyanimport java.util.ArrayList;
334cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyanimport java.util.Collection;
344cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyanimport java.util.Comparator;
354cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyanimport java.util.HashMap;
364cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyanimport java.util.Iterator;
374cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyanimport java.util.Map;
384cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyanimport java.util.Timer;
394cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyanimport java.util.TimerTask;
404cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyanimport java.util.TreeSet;
414cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyanimport java.util.concurrent.Executor;
424cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyanimport javax.sip.SipException;
434cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan
444cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan/**
454cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan * Timer that can schedule events to occur even when the device is in sleep.
464cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan */
474cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyanclass SipWakeupTimer extends BroadcastReceiver {
484cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan    private static final String TAG = "_SIP.WkTimer_";
494cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan    private static final String TRIGGER_TIME = "TriggerTime";
504cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan    private static final boolean DEBUG_TIMER = SipService.DEBUG && false;
514cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan
524cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan    private Context mContext;
534cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan    private AlarmManager mAlarmManager;
544cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan
554cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan    // runnable --> time to execute in SystemClock
564cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan    private TreeSet<MyEvent> mEventQueue =
574cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan            new TreeSet<MyEvent>(new MyEventComparator());
584cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan
594cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan    private PendingIntent mPendingIntent;
604cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan
614cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan    private Executor mExecutor;
624cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan
634cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan    public SipWakeupTimer(Context context, Executor executor) {
644cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        mContext = context;
654cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        mAlarmManager = (AlarmManager)
664cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan                context.getSystemService(Context.ALARM_SERVICE);
674cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan
684cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        IntentFilter filter = new IntentFilter(getAction());
694cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        context.registerReceiver(this, filter);
704cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        mExecutor = executor;
714cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan    }
724cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan
734cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan    /**
744cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan     * Stops the timer. No event can be scheduled after this method is called.
754cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan     */
764cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan    public synchronized void stop() {
774cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        mContext.unregisterReceiver(this);
784cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        if (mPendingIntent != null) {
794cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan            mAlarmManager.cancel(mPendingIntent);
804cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan            mPendingIntent = null;
814cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        }
824cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        mEventQueue.clear();
834cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        mEventQueue = null;
844cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan    }
854cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan
8642cdc7a9d3f94260768f38de4f477ec4e418e19aHung-ying Tyan    private boolean stopped() {
874cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        if (mEventQueue == null) {
884cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan            Log.w(TAG, "Timer stopped");
894cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan            return true;
904cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        } else {
914cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan            return false;
924cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        }
934cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan    }
944cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan
954cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan    private void cancelAlarm() {
964cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        mAlarmManager.cancel(mPendingIntent);
974cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        mPendingIntent = null;
984cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan    }
994cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan
1004cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan    private void recalculatePeriods() {
1014cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        if (mEventQueue.isEmpty()) return;
1024cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan
1034cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        MyEvent firstEvent = mEventQueue.first();
1044cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        int minPeriod = firstEvent.mMaxPeriod;
1054cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        long minTriggerTime = firstEvent.mTriggerTime;
1064cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        for (MyEvent e : mEventQueue) {
1074cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan            e.mPeriod = e.mMaxPeriod / minPeriod * minPeriod;
1084cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan            int interval = (int) (e.mLastTriggerTime + e.mMaxPeriod
1094cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan                    - minTriggerTime);
1104cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan            interval = interval / minPeriod * minPeriod;
1114cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan            e.mTriggerTime = minTriggerTime + interval;
1124cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        }
1134cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        TreeSet<MyEvent> newQueue = new TreeSet<MyEvent>(
1144cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan                mEventQueue.comparator());
1154cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        newQueue.addAll((Collection<MyEvent>) mEventQueue);
1164cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        mEventQueue.clear();
1174cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        mEventQueue = newQueue;
1184cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        if (DEBUG_TIMER) {
1194cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan            Log.d(TAG, "queue re-calculated");
1204cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan            printQueue();
1214cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        }
1224cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan    }
1234cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan
1244cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan    // Determines the period and the trigger time of the new event and insert it
1254cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan    // to the queue.
1264cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan    private void insertEvent(MyEvent event) {
1274cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        long now = SystemClock.elapsedRealtime();
1284cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        if (mEventQueue.isEmpty()) {
1294cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan            event.mTriggerTime = now + event.mPeriod;
1304cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan            mEventQueue.add(event);
1314cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan            return;
1324cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        }
1334cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        MyEvent firstEvent = mEventQueue.first();
1344cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        int minPeriod = firstEvent.mPeriod;
1354cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        if (minPeriod <= event.mMaxPeriod) {
1364cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan            event.mPeriod = event.mMaxPeriod / minPeriod * minPeriod;
1374cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan            int interval = event.mMaxPeriod;
1384cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan            interval -= (int) (firstEvent.mTriggerTime - now);
1394cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan            interval = interval / minPeriod * minPeriod;
1404cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan            event.mTriggerTime = firstEvent.mTriggerTime + interval;
1414cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan            mEventQueue.add(event);
1424cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        } else {
1434cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan            long triggerTime = now + event.mPeriod;
1444cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan            if (firstEvent.mTriggerTime < triggerTime) {
1454cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan                event.mTriggerTime = firstEvent.mTriggerTime;
1464cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan                event.mLastTriggerTime -= event.mPeriod;
1474cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan            } else {
1484cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan                event.mTriggerTime = triggerTime;
1494cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan            }
1504cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan            mEventQueue.add(event);
1514cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan            recalculatePeriods();
1524cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        }
1534cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan    }
1544cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan
1554cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan    /**
1564cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan     * Sets a periodic timer.
1574cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan     *
1584cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan     * @param period the timer period; in milli-second
1594cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan     * @param callback is called back when the timer goes off; the same callback
1604cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan     *      can be specified in multiple timer events
1614cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan     */
1624cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan    public synchronized void set(int period, Runnable callback) {
1634cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        if (stopped()) return;
1644cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan
1654cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        long now = SystemClock.elapsedRealtime();
1664cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        MyEvent event = new MyEvent(period, callback, now);
1674cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        insertEvent(event);
1684cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan
1694cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        if (mEventQueue.first() == event) {
1704cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan            if (mEventQueue.size() > 1) cancelAlarm();
1714cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan            scheduleNext();
1724cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        }
1734cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan
1744cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        long triggerTime = event.mTriggerTime;
1754cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        if (DEBUG_TIMER) {
176f22d0eb0b438a8354144e3abe087f8b13e62cd55Hung-ying Tyan            Log.d(TAG, " add event " + event + " scheduled on "
1774cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan                    + showTime(triggerTime) + " at " + showTime(now)
1784cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan                    + ", #events=" + mEventQueue.size());
1794cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan            printQueue();
1804cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        }
1814cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan    }
1824cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan
1834cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan    /**
1844cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan     * Cancels all the timer events with the specified callback.
1854cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan     *
1864cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan     * @param callback the callback
1874cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan     */
1884cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan    public synchronized void cancel(Runnable callback) {
1894cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        if (stopped() || mEventQueue.isEmpty()) return;
1904cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        if (DEBUG_TIMER) Log.d(TAG, "cancel:" + callback);
1914cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan
1924cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        MyEvent firstEvent = mEventQueue.first();
1934cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        for (Iterator<MyEvent> iter = mEventQueue.iterator();
1944cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan                iter.hasNext();) {
1954cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan            MyEvent event = iter.next();
1964cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan            if (event.mCallback == callback) {
1974cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan                iter.remove();
1984cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan                if (DEBUG_TIMER) Log.d(TAG, "    cancel found:" + event);
1994cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan            }
2004cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        }
2014cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        if (mEventQueue.isEmpty()) {
2024cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan            cancelAlarm();
2034cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        } else if (mEventQueue.first() != firstEvent) {
2044cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan            cancelAlarm();
2054cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan            firstEvent = mEventQueue.first();
2064cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan            firstEvent.mPeriod = firstEvent.mMaxPeriod;
2074cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan            firstEvent.mTriggerTime = firstEvent.mLastTriggerTime
2084cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan                    + firstEvent.mPeriod;
2094cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan            recalculatePeriods();
2104cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan            scheduleNext();
2114cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        }
2124cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        if (DEBUG_TIMER) {
2134cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan            Log.d(TAG, "after cancel:");
2144cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan            printQueue();
2154cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        }
2164cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan    }
2174cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan
2184cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan    private void scheduleNext() {
2194cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        if (stopped() || mEventQueue.isEmpty()) return;
2204cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan
2214cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        if (mPendingIntent != null) {
2224cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan            throw new RuntimeException("pendingIntent is not null!");
2234cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        }
2244cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan
2254cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        MyEvent event = mEventQueue.first();
2264cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        Intent intent = new Intent(getAction());
2274cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        intent.putExtra(TRIGGER_TIME, event.mTriggerTime);
2284cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        PendingIntent pendingIntent = mPendingIntent =
2294cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan                PendingIntent.getBroadcast(mContext, 0, intent,
2304cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan                        PendingIntent.FLAG_UPDATE_CURRENT);
2314cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
2324cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan                event.mTriggerTime, pendingIntent);
2334cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan    }
2344cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan
2354cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan    @Override
23642cdc7a9d3f94260768f38de4f477ec4e418e19aHung-ying Tyan    public synchronized void onReceive(Context context, Intent intent) {
2374cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        // This callback is already protected by AlarmManager's wake lock.
2384cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        String action = intent.getAction();
2394cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        if (getAction().equals(action)
2404cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan                && intent.getExtras().containsKey(TRIGGER_TIME)) {
2414cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan            mPendingIntent = null;
2424cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan            long triggerTime = intent.getLongExtra(TRIGGER_TIME, -1L);
2434cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan            execute(triggerTime);
2444cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        } else {
2454cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan            Log.d(TAG, "unrecognized intent: " + intent);
2464cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        }
2474cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan    }
2484cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan
2494cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan    private void printQueue() {
2504cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        int count = 0;
2514cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        for (MyEvent event : mEventQueue) {
2524cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan            Log.d(TAG, "     " + event + ": scheduled at "
2534cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan                    + showTime(event.mTriggerTime) + ": last at "
2544cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan                    + showTime(event.mLastTriggerTime));
2554cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan            if (++count >= 5) break;
2564cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        }
2574cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        if (mEventQueue.size() > count) {
2584cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan            Log.d(TAG, "     .....");
2594cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        } else if (count == 0) {
2604cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan            Log.d(TAG, "     <empty>");
2614cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        }
2624cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan    }
2634cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan
26442cdc7a9d3f94260768f38de4f477ec4e418e19aHung-ying Tyan    private void execute(long triggerTime) {
2654cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        if (DEBUG_TIMER) Log.d(TAG, "time's up, triggerTime = "
2664cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan                + showTime(triggerTime) + ": " + mEventQueue.size());
2674cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        if (stopped() || mEventQueue.isEmpty()) return;
2684cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan
2694cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        for (MyEvent event : mEventQueue) {
270f22d0eb0b438a8354144e3abe087f8b13e62cd55Hung-ying Tyan            if (event.mTriggerTime != triggerTime) continue;
2714cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan            if (DEBUG_TIMER) Log.d(TAG, "execute " + event);
2724cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan
273f22d0eb0b438a8354144e3abe087f8b13e62cd55Hung-ying Tyan            event.mLastTriggerTime = triggerTime;
2744cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan            event.mTriggerTime += event.mPeriod;
2754cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan
2764cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan            // run the callback in the handler thread to prevent deadlock
2774cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan            mExecutor.execute(event.mCallback);
2784cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        }
2794cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        if (DEBUG_TIMER) {
2804cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan            Log.d(TAG, "after timeout execution");
2814cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan            printQueue();
2824cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        }
2834cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        scheduleNext();
2844cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan    }
2854cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan
2864cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan    private String getAction() {
2874cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        return toString();
2884cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan    }
2894cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan
2904cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan    private String showTime(long time) {
2914cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        int ms = (int) (time % 1000);
2924cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        int s = (int) (time / 1000);
2934cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        int m = s / 60;
2944cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        s %= 60;
2954cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        return String.format("%d.%d.%d", m, s, ms);
2964cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan    }
2974cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan
2984cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan    private static class MyEvent {
2994cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        int mPeriod;
3004cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        int mMaxPeriod;
3014cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        long mTriggerTime;
3024cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        long mLastTriggerTime;
3034cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        Runnable mCallback;
3044cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan
3054cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        MyEvent(int period, Runnable callback, long now) {
3064cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan            mPeriod = mMaxPeriod = period;
3074cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan            mCallback = callback;
3084cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan            mLastTriggerTime = now;
3094cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        }
3104cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan
3114cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        @Override
3124cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        public String toString() {
3134cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan            String s = super.toString();
3144cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan            s = s.substring(s.indexOf("@"));
3154cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan            return s + ":" + (mPeriod / 1000) + ":" + (mMaxPeriod / 1000) + ":"
3164cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan                    + toString(mCallback);
3174cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        }
3184cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan
3194cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        private String toString(Object o) {
3204cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan            String s = o.toString();
3214cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan            int index = s.indexOf("$");
3224cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan            if (index > 0) s = s.substring(index + 1);
3234cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan            return s;
3244cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        }
3254cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan    }
3264cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan
327f22d0eb0b438a8354144e3abe087f8b13e62cd55Hung-ying Tyan    // Sort the events by mMaxPeriod so that the first event can be used to
328f22d0eb0b438a8354144e3abe087f8b13e62cd55Hung-ying Tyan    // align events with larger periods
3294cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan    private static class MyEventComparator implements Comparator<MyEvent> {
3304cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        public int compare(MyEvent e1, MyEvent e2) {
3314cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan            if (e1 == e2) return 0;
3324cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan            int diff = e1.mMaxPeriod - e2.mMaxPeriod;
3334cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan            if (diff == 0) diff = -1;
3344cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan            return diff;
3354cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        }
3364cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan
3374cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        public boolean equals(Object that) {
3384cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan            return (this == that);
3394cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        }
3404cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan    }
3414cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan}
342