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