15621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan/*
25621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan * Copyright (C) 2011, The Android Open Source Project
35621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan *
45621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan * Licensed under the Apache License, Version 2.0 (the "License");
55621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan * you may not use this file except in compliance with the License.
65621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan * You may obtain a copy of the License at
75621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan *
85621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan *     http://www.apache.org/licenses/LICENSE-2.0
95621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan *
105621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan * Unless required by applicable law or agreed to in writing, software
115621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan * distributed under the License is distributed on an "AS IS" BASIS,
125621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
135621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan * See the License for the specific language governing permissions and
145621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan * limitations under the License.
155621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan */
165621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan
175621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyanpackage com.android.server.sip;
185621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan
195621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyanimport android.app.AlarmManager;
205621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyanimport android.app.PendingIntent;
215621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyanimport android.content.BroadcastReceiver;
225621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyanimport android.content.Context;
235621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyanimport android.content.Intent;
245621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyanimport android.content.IntentFilter;
255621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyanimport android.os.SystemClock;
265621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyanimport android.util.Log;
275621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan
285621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyanimport java.io.IOException;
295621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyanimport java.net.DatagramSocket;
305621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyanimport java.net.InetAddress;
315621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyanimport java.net.UnknownHostException;
325621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyanimport java.util.ArrayList;
335621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyanimport java.util.Collection;
345621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyanimport java.util.Comparator;
355621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyanimport java.util.HashMap;
365621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyanimport java.util.Iterator;
375621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyanimport java.util.Map;
385621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyanimport java.util.Timer;
395621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyanimport java.util.TimerTask;
405621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyanimport java.util.TreeSet;
415621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyanimport java.util.concurrent.Executor;
425621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyanimport javax.sip.SipException;
435621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan
445621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan/**
455621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan * Timer that can schedule events to occur even when the device is in sleep.
465621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan */
475621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyanclass SipWakeupTimer extends BroadcastReceiver {
485621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan    private static final String TAG = "_SIP.WkTimer_";
495621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan    private static final String TRIGGER_TIME = "TriggerTime";
505621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan    private static final boolean DEBUG_TIMER = SipService.DEBUG && false;
515621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan
525621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan    private Context mContext;
535621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan    private AlarmManager mAlarmManager;
545621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan
555621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan    // runnable --> time to execute in SystemClock
565621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan    private TreeSet<MyEvent> mEventQueue =
575621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan            new TreeSet<MyEvent>(new MyEventComparator());
585621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan
595621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan    private PendingIntent mPendingIntent;
605621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan
615621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan    private Executor mExecutor;
625621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan
635621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan    public SipWakeupTimer(Context context, Executor executor) {
645621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        mContext = context;
655621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        mAlarmManager = (AlarmManager)
665621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan                context.getSystemService(Context.ALARM_SERVICE);
675621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan
685621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        IntentFilter filter = new IntentFilter(getAction());
695621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        context.registerReceiver(this, filter);
705621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        mExecutor = executor;
715621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan    }
725621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan
735621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan    /**
745621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan     * Stops the timer. No event can be scheduled after this method is called.
755621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan     */
765621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan    public synchronized void stop() {
775621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        mContext.unregisterReceiver(this);
785621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        if (mPendingIntent != null) {
795621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan            mAlarmManager.cancel(mPendingIntent);
805621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan            mPendingIntent = null;
815621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        }
825621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        mEventQueue.clear();
835621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        mEventQueue = null;
845621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan    }
855621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan
86a6cec8feed5c135bb5f4d6193012d13258a067c4Hung-ying Tyan    private boolean stopped() {
875621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        if (mEventQueue == null) {
885621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan            Log.w(TAG, "Timer stopped");
895621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan            return true;
905621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        } else {
915621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan            return false;
925621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        }
935621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan    }
945621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan
955621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan    private void cancelAlarm() {
965621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        mAlarmManager.cancel(mPendingIntent);
975621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        mPendingIntent = null;
985621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan    }
995621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan
1005621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan    private void recalculatePeriods() {
1015621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        if (mEventQueue.isEmpty()) return;
1025621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan
1035621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        MyEvent firstEvent = mEventQueue.first();
1045621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        int minPeriod = firstEvent.mMaxPeriod;
1055621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        long minTriggerTime = firstEvent.mTriggerTime;
1065621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        for (MyEvent e : mEventQueue) {
1075621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan            e.mPeriod = e.mMaxPeriod / minPeriod * minPeriod;
1085621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan            int interval = (int) (e.mLastTriggerTime + e.mMaxPeriod
1095621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan                    - minTriggerTime);
1105621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan            interval = interval / minPeriod * minPeriod;
1115621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan            e.mTriggerTime = minTriggerTime + interval;
1125621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        }
1135621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        TreeSet<MyEvent> newQueue = new TreeSet<MyEvent>(
1145621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan                mEventQueue.comparator());
1155621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        newQueue.addAll((Collection<MyEvent>) mEventQueue);
1165621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        mEventQueue.clear();
1175621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        mEventQueue = newQueue;
1185621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        if (DEBUG_TIMER) {
1195621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan            Log.d(TAG, "queue re-calculated");
1205621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan            printQueue();
1215621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        }
1225621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan    }
1235621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan
1245621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan    // Determines the period and the trigger time of the new event and insert it
1255621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan    // to the queue.
1265621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan    private void insertEvent(MyEvent event) {
1275621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        long now = SystemClock.elapsedRealtime();
1285621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        if (mEventQueue.isEmpty()) {
1295621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan            event.mTriggerTime = now + event.mPeriod;
1305621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan            mEventQueue.add(event);
1315621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan            return;
1325621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        }
1335621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        MyEvent firstEvent = mEventQueue.first();
1345621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        int minPeriod = firstEvent.mPeriod;
1355621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        if (minPeriod <= event.mMaxPeriod) {
1365621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan            event.mPeriod = event.mMaxPeriod / minPeriod * minPeriod;
1375621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan            int interval = event.mMaxPeriod;
1385621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan            interval -= (int) (firstEvent.mTriggerTime - now);
1395621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan            interval = interval / minPeriod * minPeriod;
1405621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan            event.mTriggerTime = firstEvent.mTriggerTime + interval;
1415621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan            mEventQueue.add(event);
1425621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        } else {
1435621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan            long triggerTime = now + event.mPeriod;
1445621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan            if (firstEvent.mTriggerTime < triggerTime) {
1455621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan                event.mTriggerTime = firstEvent.mTriggerTime;
1465621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan                event.mLastTriggerTime -= event.mPeriod;
1475621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan            } else {
1485621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan                event.mTriggerTime = triggerTime;
1495621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan            }
1505621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan            mEventQueue.add(event);
1515621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan            recalculatePeriods();
1525621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        }
1535621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan    }
1545621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan
1555621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan    /**
1565621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan     * Sets a periodic timer.
1575621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan     *
1585621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan     * @param period the timer period; in milli-second
1595621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan     * @param callback is called back when the timer goes off; the same callback
1605621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan     *      can be specified in multiple timer events
1615621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan     */
1625621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan    public synchronized void set(int period, Runnable callback) {
1635621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        if (stopped()) return;
1645621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan
1655621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        long now = SystemClock.elapsedRealtime();
1665621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        MyEvent event = new MyEvent(period, callback, now);
1675621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        insertEvent(event);
1685621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan
1695621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        if (mEventQueue.first() == event) {
1705621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan            if (mEventQueue.size() > 1) cancelAlarm();
1715621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan            scheduleNext();
1725621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        }
1735621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan
1745621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        long triggerTime = event.mTriggerTime;
1755621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        if (DEBUG_TIMER) {
1764af085ff26fbe9e13f7002496fd505dbdb36b282Hung-ying Tyan            Log.d(TAG, " add event " + event + " scheduled on "
1775621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan                    + showTime(triggerTime) + " at " + showTime(now)
1785621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan                    + ", #events=" + mEventQueue.size());
1795621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan            printQueue();
1805621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        }
1815621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan    }
1825621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan
1835621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan    /**
1845621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan     * Cancels all the timer events with the specified callback.
1855621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan     *
1865621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan     * @param callback the callback
1875621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan     */
1885621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan    public synchronized void cancel(Runnable callback) {
1895621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        if (stopped() || mEventQueue.isEmpty()) return;
1905621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        if (DEBUG_TIMER) Log.d(TAG, "cancel:" + callback);
1915621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan
1925621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        MyEvent firstEvent = mEventQueue.first();
1935621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        for (Iterator<MyEvent> iter = mEventQueue.iterator();
1945621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan                iter.hasNext();) {
1955621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan            MyEvent event = iter.next();
1965621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan            if (event.mCallback == callback) {
1975621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan                iter.remove();
1985621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan                if (DEBUG_TIMER) Log.d(TAG, "    cancel found:" + event);
1995621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan            }
2005621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        }
2015621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        if (mEventQueue.isEmpty()) {
2025621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan            cancelAlarm();
2035621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        } else if (mEventQueue.first() != firstEvent) {
2045621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan            cancelAlarm();
2055621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan            firstEvent = mEventQueue.first();
2065621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan            firstEvent.mPeriod = firstEvent.mMaxPeriod;
2075621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan            firstEvent.mTriggerTime = firstEvent.mLastTriggerTime
2085621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan                    + firstEvent.mPeriod;
2095621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan            recalculatePeriods();
2105621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan            scheduleNext();
2115621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        }
2125621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        if (DEBUG_TIMER) {
2135621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan            Log.d(TAG, "after cancel:");
2145621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan            printQueue();
2155621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        }
2165621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan    }
2175621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan
2185621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan    private void scheduleNext() {
2195621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        if (stopped() || mEventQueue.isEmpty()) return;
2205621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan
2215621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        if (mPendingIntent != null) {
2225621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan            throw new RuntimeException("pendingIntent is not null!");
2235621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        }
2245621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan
2255621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        MyEvent event = mEventQueue.first();
2265621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        Intent intent = new Intent(getAction());
2275621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        intent.putExtra(TRIGGER_TIME, event.mTriggerTime);
2285621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        PendingIntent pendingIntent = mPendingIntent =
2295621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan                PendingIntent.getBroadcast(mContext, 0, intent,
2305621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan                        PendingIntent.FLAG_UPDATE_CURRENT);
2315621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
2325621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan                event.mTriggerTime, pendingIntent);
2335621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan    }
2345621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan
2355621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan    @Override
236a6cec8feed5c135bb5f4d6193012d13258a067c4Hung-ying Tyan    public synchronized void onReceive(Context context, Intent intent) {
2375621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        // This callback is already protected by AlarmManager's wake lock.
2385621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        String action = intent.getAction();
2395621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        if (getAction().equals(action)
2405621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan                && intent.getExtras().containsKey(TRIGGER_TIME)) {
2415621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan            mPendingIntent = null;
2425621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan            long triggerTime = intent.getLongExtra(TRIGGER_TIME, -1L);
2435621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan            execute(triggerTime);
2445621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        } else {
2455621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan            Log.d(TAG, "unrecognized intent: " + intent);
2465621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        }
2475621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan    }
2485621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan
2495621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan    private void printQueue() {
2505621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        int count = 0;
2515621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        for (MyEvent event : mEventQueue) {
2525621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan            Log.d(TAG, "     " + event + ": scheduled at "
2535621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan                    + showTime(event.mTriggerTime) + ": last at "
2545621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan                    + showTime(event.mLastTriggerTime));
2555621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan            if (++count >= 5) break;
2565621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        }
2575621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        if (mEventQueue.size() > count) {
2585621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan            Log.d(TAG, "     .....");
2595621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        } else if (count == 0) {
2605621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan            Log.d(TAG, "     <empty>");
2615621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        }
2625621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan    }
2635621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan
264a6cec8feed5c135bb5f4d6193012d13258a067c4Hung-ying Tyan    private void execute(long triggerTime) {
2655621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        if (DEBUG_TIMER) Log.d(TAG, "time's up, triggerTime = "
2665621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan                + showTime(triggerTime) + ": " + mEventQueue.size());
2675621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        if (stopped() || mEventQueue.isEmpty()) return;
2685621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan
2695621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        for (MyEvent event : mEventQueue) {
2704af085ff26fbe9e13f7002496fd505dbdb36b282Hung-ying Tyan            if (event.mTriggerTime != triggerTime) continue;
2715621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan            if (DEBUG_TIMER) Log.d(TAG, "execute " + event);
2725621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan
2734af085ff26fbe9e13f7002496fd505dbdb36b282Hung-ying Tyan            event.mLastTriggerTime = triggerTime;
2745621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan            event.mTriggerTime += event.mPeriod;
2755621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan
2765621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan            // run the callback in the handler thread to prevent deadlock
2775621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan            mExecutor.execute(event.mCallback);
2785621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        }
2795621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        if (DEBUG_TIMER) {
2805621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan            Log.d(TAG, "after timeout execution");
2815621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan            printQueue();
2825621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        }
2835621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        scheduleNext();
2845621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan    }
2855621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan
2865621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan    private String getAction() {
2875621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        return toString();
2885621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan    }
2895621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan
2905621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan    private String showTime(long time) {
2915621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        int ms = (int) (time % 1000);
2925621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        int s = (int) (time / 1000);
2935621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        int m = s / 60;
2945621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        s %= 60;
2955621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        return String.format("%d.%d.%d", m, s, ms);
2965621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan    }
2975621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan
2985621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan    private static class MyEvent {
2995621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        int mPeriod;
3005621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        int mMaxPeriod;
3015621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        long mTriggerTime;
3025621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        long mLastTriggerTime;
3035621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        Runnable mCallback;
3045621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan
3055621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        MyEvent(int period, Runnable callback, long now) {
3065621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan            mPeriod = mMaxPeriod = period;
3075621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan            mCallback = callback;
3085621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan            mLastTriggerTime = now;
3095621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        }
3105621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan
3115621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        @Override
3125621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        public String toString() {
3135621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan            String s = super.toString();
3145621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan            s = s.substring(s.indexOf("@"));
3155621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan            return s + ":" + (mPeriod / 1000) + ":" + (mMaxPeriod / 1000) + ":"
3165621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan                    + toString(mCallback);
3175621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        }
3185621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan
3195621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        private String toString(Object o) {
3205621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan            String s = o.toString();
3215621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan            int index = s.indexOf("$");
3225621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan            if (index > 0) s = s.substring(index + 1);
3235621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan            return s;
3245621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        }
3255621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan    }
3265621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan
3274af085ff26fbe9e13f7002496fd505dbdb36b282Hung-ying Tyan    // Sort the events by mMaxPeriod so that the first event can be used to
3284af085ff26fbe9e13f7002496fd505dbdb36b282Hung-ying Tyan    // align events with larger periods
3295621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan    private static class MyEventComparator implements Comparator<MyEvent> {
3305621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        public int compare(MyEvent e1, MyEvent e2) {
3315621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan            if (e1 == e2) return 0;
3325621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan            int diff = e1.mMaxPeriod - e2.mMaxPeriod;
3335621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan            if (diff == 0) diff = -1;
3345621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan            return diff;
3355621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        }
3365621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan
3375621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        public boolean equals(Object that) {
3385621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan            return (this == that);
3395621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        }
3405621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan    }
3415621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan}
342