SipService.java revision 469491e7807a46f31681d21c0ddae215f7891094
19c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan/*
29c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan * Copyright (C) 2010, The Android Open Source Project
39c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan *
49c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan * Licensed under the Apache License, Version 2.0 (the "License");
59c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan * you may not use this file except in compliance with the License.
69c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan * You may obtain a copy of the License at
79c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan *
89c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan *     http://www.apache.org/licenses/LICENSE-2.0
99c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan *
109c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan * Unless required by applicable law or agreed to in writing, software
119c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan * distributed under the License is distributed on an "AS IS" BASIS,
129c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
139c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan * See the License for the specific language governing permissions and
149c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan * limitations under the License.
159c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan */
169c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
179c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanpackage com.android.server.sip;
189c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
199c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport android.app.AlarmManager;
209c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport android.app.PendingIntent;
219c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport android.content.BroadcastReceiver;
229c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport android.content.Context;
239c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport android.content.Intent;
249c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport android.content.IntentFilter;
259c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport android.net.ConnectivityManager;
269c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport android.net.NetworkInfo;
279c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport android.net.sip.ISipService;
289c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport android.net.sip.ISipSession;
299c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport android.net.sip.ISipSessionListener;
309c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport android.net.sip.SipErrorCode;
319c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport android.net.sip.SipManager;
329c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport android.net.sip.SipProfile;
339c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport android.net.sip.SipSession;
349c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport android.net.sip.SipSessionAdapter;
359c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport android.net.wifi.WifiManager;
369c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport android.os.Binder;
379c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport android.os.Bundle;
389c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport android.os.Handler;
399c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport android.os.HandlerThread;
409c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport android.os.Looper;
419c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport android.os.Message;
42257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyanimport android.os.PowerManager;
43a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyanimport android.os.Process;
449c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport android.os.RemoteException;
459c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport android.os.ServiceManager;
469c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport android.os.SystemClock;
479c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport android.text.TextUtils;
489c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport android.util.Log;
499c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
509c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport java.io.IOException;
519c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport java.net.DatagramSocket;
529c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport java.net.InetAddress;
539c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport java.net.UnknownHostException;
54a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyanimport java.util.ArrayList;
559c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport java.util.Collection;
569c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport java.util.Comparator;
579c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport java.util.HashMap;
589c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport java.util.Iterator;
599c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport java.util.Map;
609c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport java.util.Timer;
619c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport java.util.TimerTask;
629c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport java.util.TreeSet;
639c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport javax.sip.SipException;
649c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
659c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan/**
669c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan * @hide
679c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan */
689c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanpublic final class SipService extends ISipService.Stub {
69acedadc1967457ac2f8981c67f884fd8c0ee853cHung-ying Tyan    static final String TAG = "SipService";
70acedadc1967457ac2f8981c67f884fd8c0ee853cHung-ying Tyan    static final boolean DEBUGV = false;
716d9d99615a30de0675271553552c3c7b49311354Joe Onorato    private static final boolean DEBUG = false;
729c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private static final boolean DEBUG_TIMER = DEBUG && false;
739c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private static final int EXPIRY_TIME = 3600;
749c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private static final int SHORT_EXPIRY_TIME = 10;
759c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private static final int MIN_EXPIRY_TIME = 60;
769c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
779c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private Context mContext;
789c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private String mLocalIp;
799c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private String mNetworkType;
809c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private boolean mConnected;
819c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private WakeupTimer mTimer;
8287e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang    private WifiScanProcess mWifiScanProcess;
839c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private WifiManager.WifiLock mWifiLock;
849c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private boolean mWifiOnly;
85469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang    private IntervalMeasurementProcess mIntervalMeasurementProcess;
869c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
879c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private MyExecutor mExecutor;
889c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
899c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    // SipProfile URI --> group
909c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private Map<String, SipSessionGroupExt> mSipGroups =
919c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            new HashMap<String, SipSessionGroupExt>();
929c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
939c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    // session ID --> session
949c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private Map<String, ISipSession> mPendingSessions =
959c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            new HashMap<String, ISipSession>();
969c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
979c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private ConnectivityReceiver mConnectivityReceiver;
989abf73f1bde7b1617603c888650dbda9d6a7fddfHung-ying Tyan    private boolean mWifiEnabled;
99acedadc1967457ac2f8981c67f884fd8c0ee853cHung-ying Tyan    private SipWakeLock mMyWakeLock;
100469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang    private int mKeepAliveInterval;
1019c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
1029c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    /**
1039c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan     * Starts the SIP service. Do nothing if the SIP API is not supported on the
1049c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan     * device.
1059c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan     */
1069c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    public static void start(Context context) {
1079c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        if (SipManager.isApiSupported(context)) {
1089c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            ServiceManager.addService("sip", new SipService(context));
10922523a59d879cf47f1e9c202d001d569fad4f69eHung-ying Tyan            context.sendBroadcast(new Intent(SipManager.ACTION_SIP_SERVICE_UP));
110dc7545495e1ef8bad32cd78775f4dc072015eea0Hung-ying Tyan            if (DEBUG) Log.d(TAG, "SIP service started");
1119c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
1129c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
1139c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
1149c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private SipService(Context context) {
1159c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        if (DEBUG) Log.d(TAG, " service started!");
1169c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        mContext = context;
1179c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        mConnectivityReceiver = new ConnectivityReceiver();
118acedadc1967457ac2f8981c67f884fd8c0ee853cHung-ying Tyan        mMyWakeLock = new SipWakeLock((PowerManager)
119257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                context.getSystemService(Context.POWER_SERVICE));
1209c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
1219c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        mTimer = new WakeupTimer(context);
1229c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        mWifiOnly = SipManager.isSipWifiOnly(context);
1239c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
1249c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
125dc7545495e1ef8bad32cd78775f4dc072015eea0Hung-ying Tyan    private BroadcastReceiver mWifiStateReceiver = new BroadcastReceiver() {
126f452fd81f1a308428b0bfbae883fddb9caced878Chung-yih Wang        @Override
127f452fd81f1a308428b0bfbae883fddb9caced878Chung-yih Wang        public void onReceive(Context context, Intent intent) {
128f452fd81f1a308428b0bfbae883fddb9caced878Chung-yih Wang            String action = intent.getAction();
1299abf73f1bde7b1617603c888650dbda9d6a7fddfHung-ying Tyan            if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) {
1309abf73f1bde7b1617603c888650dbda9d6a7fddfHung-ying Tyan                int state = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
1319abf73f1bde7b1617603c888650dbda9d6a7fddfHung-ying Tyan                        WifiManager.WIFI_STATE_UNKNOWN);
1329abf73f1bde7b1617603c888650dbda9d6a7fddfHung-ying Tyan                synchronized (SipService.this) {
1339abf73f1bde7b1617603c888650dbda9d6a7fddfHung-ying Tyan                    switch (state) {
1349abf73f1bde7b1617603c888650dbda9d6a7fddfHung-ying Tyan                        case WifiManager.WIFI_STATE_ENABLED:
1359abf73f1bde7b1617603c888650dbda9d6a7fddfHung-ying Tyan                            mWifiEnabled = true;
136617479363dcafe01bfa1fa025e1d9d122864a0ceHung-ying Tyan                            if (anyOpenedToReceiveCalls()) grabWifiLock();
1379abf73f1bde7b1617603c888650dbda9d6a7fddfHung-ying Tyan                            break;
1389abf73f1bde7b1617603c888650dbda9d6a7fddfHung-ying Tyan                        case WifiManager.WIFI_STATE_DISABLED:
1399abf73f1bde7b1617603c888650dbda9d6a7fddfHung-ying Tyan                            mWifiEnabled = false;
1409abf73f1bde7b1617603c888650dbda9d6a7fddfHung-ying Tyan                            releaseWifiLock();
1419abf73f1bde7b1617603c888650dbda9d6a7fddfHung-ying Tyan                            break;
1429abf73f1bde7b1617603c888650dbda9d6a7fddfHung-ying Tyan                    }
1439abf73f1bde7b1617603c888650dbda9d6a7fddfHung-ying Tyan                }
144f452fd81f1a308428b0bfbae883fddb9caced878Chung-yih Wang            }
145f452fd81f1a308428b0bfbae883fddb9caced878Chung-yih Wang        }
146f452fd81f1a308428b0bfbae883fddb9caced878Chung-yih Wang    };
147f452fd81f1a308428b0bfbae883fddb9caced878Chung-yih Wang
148dc7545495e1ef8bad32cd78775f4dc072015eea0Hung-ying Tyan    private void registerReceivers() {
149dc7545495e1ef8bad32cd78775f4dc072015eea0Hung-ying Tyan        mContext.registerReceiver(mConnectivityReceiver,
150dc7545495e1ef8bad32cd78775f4dc072015eea0Hung-ying Tyan                new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
151dc7545495e1ef8bad32cd78775f4dc072015eea0Hung-ying Tyan        mContext.registerReceiver(mWifiStateReceiver,
152dc7545495e1ef8bad32cd78775f4dc072015eea0Hung-ying Tyan                new IntentFilter(WifiManager.WIFI_STATE_CHANGED_ACTION));
153dc7545495e1ef8bad32cd78775f4dc072015eea0Hung-ying Tyan        if (DEBUG) Log.d(TAG, " +++ register receivers");
154dc7545495e1ef8bad32cd78775f4dc072015eea0Hung-ying Tyan    }
155dc7545495e1ef8bad32cd78775f4dc072015eea0Hung-ying Tyan
156dc7545495e1ef8bad32cd78775f4dc072015eea0Hung-ying Tyan    private void unregisterReceivers() {
157dc7545495e1ef8bad32cd78775f4dc072015eea0Hung-ying Tyan        mContext.unregisterReceiver(mConnectivityReceiver);
158dc7545495e1ef8bad32cd78775f4dc072015eea0Hung-ying Tyan        mContext.unregisterReceiver(mWifiStateReceiver);
159dc7545495e1ef8bad32cd78775f4dc072015eea0Hung-ying Tyan        if (DEBUG) Log.d(TAG, " --- unregister receivers");
160dc7545495e1ef8bad32cd78775f4dc072015eea0Hung-ying Tyan    }
161dc7545495e1ef8bad32cd78775f4dc072015eea0Hung-ying Tyan
1629c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private MyExecutor getExecutor() {
1639c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        // create mExecutor lazily
1649c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        if (mExecutor == null) mExecutor = new MyExecutor();
1659c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        return mExecutor;
1669c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
1679c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
1689c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    public synchronized SipProfile[] getListOfProfiles() {
1698127b35b75c13f853dc8eb2fed8bba4bf99e3eedHung-ying Tyan        mContext.enforceCallingOrSelfPermission(
1708127b35b75c13f853dc8eb2fed8bba4bf99e3eedHung-ying Tyan                android.Manifest.permission.USE_SIP, null);
171a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        boolean isCallerRadio = isCallerRadio();
172a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        ArrayList<SipProfile> profiles = new ArrayList<SipProfile>();
1739c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        for (SipSessionGroupExt group : mSipGroups.values()) {
174a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan            if (isCallerRadio || isCallerCreator(group)) {
175a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan                profiles.add(group.getLocalProfile());
176a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan            }
1779c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
178a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        return profiles.toArray(new SipProfile[profiles.size()]);
1799c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
1809c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
181dc7545495e1ef8bad32cd78775f4dc072015eea0Hung-ying Tyan    public synchronized void open(SipProfile localProfile) {
1828127b35b75c13f853dc8eb2fed8bba4bf99e3eedHung-ying Tyan        mContext.enforceCallingOrSelfPermission(
1838127b35b75c13f853dc8eb2fed8bba4bf99e3eedHung-ying Tyan                android.Manifest.permission.USE_SIP, null);
1849c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        localProfile.setCallingUid(Binder.getCallingUid());
1859c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        try {
186dc7545495e1ef8bad32cd78775f4dc072015eea0Hung-ying Tyan            boolean addingFirstProfile = mSipGroups.isEmpty();
1879c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            createGroup(localProfile);
188dc7545495e1ef8bad32cd78775f4dc072015eea0Hung-ying Tyan            if (addingFirstProfile && !mSipGroups.isEmpty()) registerReceivers();
1899c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        } catch (SipException e) {
1909c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            Log.e(TAG, "openToMakeCalls()", e);
1919c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            // TODO: how to send the exception back
1929c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
1939c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
1949c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
1959c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    public synchronized void open3(SipProfile localProfile,
196845f7332f04864c5483b3e63da5db076fc7a888aHung-ying Tyan            PendingIntent incomingCallPendingIntent,
197845f7332f04864c5483b3e63da5db076fc7a888aHung-ying Tyan            ISipSessionListener listener) {
1988127b35b75c13f853dc8eb2fed8bba4bf99e3eedHung-ying Tyan        mContext.enforceCallingOrSelfPermission(
1998127b35b75c13f853dc8eb2fed8bba4bf99e3eedHung-ying Tyan                android.Manifest.permission.USE_SIP, null);
2009c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        localProfile.setCallingUid(Binder.getCallingUid());
201845f7332f04864c5483b3e63da5db076fc7a888aHung-ying Tyan        if (incomingCallPendingIntent == null) {
202845f7332f04864c5483b3e63da5db076fc7a888aHung-ying Tyan            Log.w(TAG, "incomingCallPendingIntent cannot be null; "
203845f7332f04864c5483b3e63da5db076fc7a888aHung-ying Tyan                    + "the profile is not opened");
204a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan            return;
2059c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
2069c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        if (DEBUG) Log.d(TAG, "open3: " + localProfile.getUriString() + ": "
207845f7332f04864c5483b3e63da5db076fc7a888aHung-ying Tyan                + incomingCallPendingIntent + ": " + listener);
2089c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        try {
209dc7545495e1ef8bad32cd78775f4dc072015eea0Hung-ying Tyan            boolean addingFirstProfile = mSipGroups.isEmpty();
2109c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            SipSessionGroupExt group = createGroup(localProfile,
211845f7332f04864c5483b3e63da5db076fc7a888aHung-ying Tyan                    incomingCallPendingIntent, listener);
212dc7545495e1ef8bad32cd78775f4dc072015eea0Hung-ying Tyan            if (addingFirstProfile && !mSipGroups.isEmpty()) registerReceivers();
2139c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (localProfile.getAutoRegistration()) {
2149c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                group.openToReceiveCalls();
2159abf73f1bde7b1617603c888650dbda9d6a7fddfHung-ying Tyan                if (mWifiEnabled) grabWifiLock();
2169c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
2179c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        } catch (SipException e) {
2189c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            Log.e(TAG, "openToReceiveCalls()", e);
2199c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            // TODO: how to send the exception back
2209c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
2219c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
2229c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
223a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan    private boolean isCallerCreator(SipSessionGroupExt group) {
224a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        SipProfile profile = group.getLocalProfile();
225a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        return (profile.getCallingUid() == Binder.getCallingUid());
226a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan    }
227a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan
228a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan    private boolean isCallerCreatorOrRadio(SipSessionGroupExt group) {
229a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        return (isCallerRadio() || isCallerCreator(group));
230a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan    }
231a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan
232a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan    private boolean isCallerRadio() {
233a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        return (Binder.getCallingUid() == Process.PHONE_UID);
234a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan    }
235a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan
2369c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    public synchronized void close(String localProfileUri) {
2378127b35b75c13f853dc8eb2fed8bba4bf99e3eedHung-ying Tyan        mContext.enforceCallingOrSelfPermission(
2388127b35b75c13f853dc8eb2fed8bba4bf99e3eedHung-ying Tyan                android.Manifest.permission.USE_SIP, null);
239a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        SipSessionGroupExt group = mSipGroups.get(localProfileUri);
240a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        if (group == null) return;
241a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        if (!isCallerCreatorOrRadio(group)) {
2426d9d99615a30de0675271553552c3c7b49311354Joe Onorato            Log.w(TAG, "only creator or radio can close this profile");
243a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan            return;
2449c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
245a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan
246a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        group = mSipGroups.remove(localProfileUri);
247a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        notifyProfileRemoved(group.getLocalProfile());
248a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        group.close();
249257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan
250617479363dcafe01bfa1fa025e1d9d122864a0ceHung-ying Tyan        if (!anyOpenedToReceiveCalls()) {
251257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan            releaseWifiLock();
252257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan            mMyWakeLock.reset(); // in case there's leak
253257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan        }
254dc7545495e1ef8bad32cd78775f4dc072015eea0Hung-ying Tyan        if (mSipGroups.isEmpty()) unregisterReceivers();
2559c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
2569c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
2579c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    public synchronized boolean isOpened(String localProfileUri) {
2588127b35b75c13f853dc8eb2fed8bba4bf99e3eedHung-ying Tyan        mContext.enforceCallingOrSelfPermission(
2598127b35b75c13f853dc8eb2fed8bba4bf99e3eedHung-ying Tyan                android.Manifest.permission.USE_SIP, null);
2609c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        SipSessionGroupExt group = mSipGroups.get(localProfileUri);
261a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        if (group == null) return false;
262a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        if (isCallerCreatorOrRadio(group)) {
263617479363dcafe01bfa1fa025e1d9d122864a0ceHung-ying Tyan            return true;
264a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        } else {
2656d9d99615a30de0675271553552c3c7b49311354Joe Onorato            Log.w(TAG, "only creator or radio can query on the profile");
266a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan            return false;
267a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        }
2689c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
2699c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
2709c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    public synchronized boolean isRegistered(String localProfileUri) {
2718127b35b75c13f853dc8eb2fed8bba4bf99e3eedHung-ying Tyan        mContext.enforceCallingOrSelfPermission(
2728127b35b75c13f853dc8eb2fed8bba4bf99e3eedHung-ying Tyan                android.Manifest.permission.USE_SIP, null);
2739c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        SipSessionGroupExt group = mSipGroups.get(localProfileUri);
274a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        if (group == null) return false;
275a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        if (isCallerCreatorOrRadio(group)) {
276a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan            return group.isRegistered();
277a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        } else {
2786d9d99615a30de0675271553552c3c7b49311354Joe Onorato            Log.w(TAG, "only creator or radio can query on the profile");
279a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan            return false;
280a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        }
2819c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
2829c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
2839c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    public synchronized void setRegistrationListener(String localProfileUri,
2849c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            ISipSessionListener listener) {
2858127b35b75c13f853dc8eb2fed8bba4bf99e3eedHung-ying Tyan        mContext.enforceCallingOrSelfPermission(
2868127b35b75c13f853dc8eb2fed8bba4bf99e3eedHung-ying Tyan                android.Manifest.permission.USE_SIP, null);
2879c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        SipSessionGroupExt group = mSipGroups.get(localProfileUri);
288a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        if (group == null) return;
289a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        if (isCallerCreator(group)) {
290a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan            group.setListener(listener);
291a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        } else {
2926d9d99615a30de0675271553552c3c7b49311354Joe Onorato            Log.w(TAG, "only creator can set listener on the profile");
293a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        }
2949c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
2959c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
2969c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    public synchronized ISipSession createSession(SipProfile localProfile,
2979c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            ISipSessionListener listener) {
2988127b35b75c13f853dc8eb2fed8bba4bf99e3eedHung-ying Tyan        mContext.enforceCallingOrSelfPermission(
2998127b35b75c13f853dc8eb2fed8bba4bf99e3eedHung-ying Tyan                android.Manifest.permission.USE_SIP, null);
3009c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        localProfile.setCallingUid(Binder.getCallingUid());
3019c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        if (!mConnected) return null;
3029c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        try {
3039c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            SipSessionGroupExt group = createGroup(localProfile);
3049c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return group.createSession(listener);
3059c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        } catch (SipException e) {
3066d9d99615a30de0675271553552c3c7b49311354Joe Onorato            if (DEBUG) Log.d(TAG, "createSession()", e);
3079c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return null;
3089c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
3099c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
3109c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
3119c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    public synchronized ISipSession getPendingSession(String callId) {
3128127b35b75c13f853dc8eb2fed8bba4bf99e3eedHung-ying Tyan        mContext.enforceCallingOrSelfPermission(
3138127b35b75c13f853dc8eb2fed8bba4bf99e3eedHung-ying Tyan                android.Manifest.permission.USE_SIP, null);
3149c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        if (callId == null) return null;
3159c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        return mPendingSessions.get(callId);
3169c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
3179c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
3189c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private String determineLocalIp() {
3199c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        try {
3209c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            DatagramSocket s = new DatagramSocket();
3219c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            s.connect(InetAddress.getByName("192.168.1.1"), 80);
3229c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return s.getLocalAddress().getHostAddress();
3239c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        } catch (IOException e) {
3246d9d99615a30de0675271553552c3c7b49311354Joe Onorato            if (DEBUG) Log.d(TAG, "determineLocalIp()", e);
3259c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            // dont do anything; there should be a connectivity change going
3269c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return null;
3279c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
3289c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
3299c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
3309c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private SipSessionGroupExt createGroup(SipProfile localProfile)
3319c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            throws SipException {
3329c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        String key = localProfile.getUriString();
3339c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        SipSessionGroupExt group = mSipGroups.get(key);
3349c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        if (group == null) {
3359c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            group = new SipSessionGroupExt(localProfile, null, null);
3369c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mSipGroups.put(key, group);
3379c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            notifyProfileAdded(localProfile);
338a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        } else if (!isCallerCreator(group)) {
339a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan            throw new SipException("only creator can access the profile");
3409c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
3419c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        return group;
3429c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
3439c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
3449c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private SipSessionGroupExt createGroup(SipProfile localProfile,
345845f7332f04864c5483b3e63da5db076fc7a888aHung-ying Tyan            PendingIntent incomingCallPendingIntent,
346845f7332f04864c5483b3e63da5db076fc7a888aHung-ying Tyan            ISipSessionListener listener) throws SipException {
3479c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        String key = localProfile.getUriString();
3489c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        SipSessionGroupExt group = mSipGroups.get(key);
3499c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        if (group != null) {
350a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan            if (!isCallerCreator(group)) {
351a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan                throw new SipException("only creator can access the profile");
352a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan            }
353845f7332f04864c5483b3e63da5db076fc7a888aHung-ying Tyan            group.setIncomingCallPendingIntent(incomingCallPendingIntent);
3549c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            group.setListener(listener);
3559c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        } else {
3569c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            group = new SipSessionGroupExt(localProfile,
357845f7332f04864c5483b3e63da5db076fc7a888aHung-ying Tyan                    incomingCallPendingIntent, listener);
3589c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mSipGroups.put(key, group);
3599c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            notifyProfileAdded(localProfile);
3609c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
3619c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        return group;
3629c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
3639c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
3649c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private void notifyProfileAdded(SipProfile localProfile) {
3659c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        if (DEBUG) Log.d(TAG, "notify: profile added: " + localProfile);
3669c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        Intent intent = new Intent(SipManager.ACTION_SIP_ADD_PHONE);
3679c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        intent.putExtra(SipManager.EXTRA_LOCAL_URI, localProfile.getUriString());
3689c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        mContext.sendBroadcast(intent);
3699c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
3709c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
3719c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private void notifyProfileRemoved(SipProfile localProfile) {
3729c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        if (DEBUG) Log.d(TAG, "notify: profile removed: " + localProfile);
3739c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        Intent intent = new Intent(SipManager.ACTION_SIP_REMOVE_PHONE);
3749c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        intent.putExtra(SipManager.EXTRA_LOCAL_URI, localProfile.getUriString());
3759c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        mContext.sendBroadcast(intent);
3769c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
3779c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
378617479363dcafe01bfa1fa025e1d9d122864a0ceHung-ying Tyan    private boolean anyOpenedToReceiveCalls() {
3799c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        for (SipSessionGroupExt group : mSipGroups.values()) {
380617479363dcafe01bfa1fa025e1d9d122864a0ceHung-ying Tyan            if (group.isOpenedToReceiveCalls()) return true;
3819c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
3829c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        return false;
3839c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
3849c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
3859c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private void grabWifiLock() {
3869c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        if (mWifiLock == null) {
3879abf73f1bde7b1617603c888650dbda9d6a7fddfHung-ying Tyan            if (DEBUG) Log.d(TAG, "~~~~~~~~~~~~~~~~~~~~~ acquire wifi lock");
3889c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mWifiLock = ((WifiManager)
3899c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    mContext.getSystemService(Context.WIFI_SERVICE))
3909c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    .createWifiLock(WifiManager.WIFI_MODE_FULL, TAG);
3919c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mWifiLock.acquire();
39287e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang            if (!mConnected) startWifiScanner();
3939c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
3949c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
3959c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
3969c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private void releaseWifiLock() {
3979c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        if (mWifiLock != null) {
3989abf73f1bde7b1617603c888650dbda9d6a7fddfHung-ying Tyan            if (DEBUG) Log.d(TAG, "~~~~~~~~~~~~~~~~~~~~~ release wifi lock");
3999c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mWifiLock.release();
4009c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mWifiLock = null;
40187e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang            stopWifiScanner();
40287e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang        }
40387e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang    }
40487e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang
40587e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang    private synchronized void startWifiScanner() {
40687e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang        if (mWifiScanProcess == null) {
40787e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang            mWifiScanProcess = new WifiScanProcess();
40887e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang        }
40987e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang        mWifiScanProcess.start();
41087e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang    }
41187e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang
41287e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang    private synchronized void stopWifiScanner() {
41387e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang        if (mWifiScanProcess != null) {
41487e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang            mWifiScanProcess.stop();
4159c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
4169c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
4179c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
4189c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private synchronized void onConnectivityChanged(
4199c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            String type, boolean connected) {
4209c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        if (DEBUG) Log.d(TAG, "onConnectivityChanged(): "
4219c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                + mNetworkType + (mConnected? " CONNECTED" : " DISCONNECTED")
4229c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                + " --> " + type + (connected? " CONNECTED" : " DISCONNECTED"));
4239c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
4249c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        boolean sameType = type.equals(mNetworkType);
4259c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        if (!sameType && !connected) return;
4269c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
4279c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        boolean wasWifi = "WIFI".equalsIgnoreCase(mNetworkType);
4289c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        boolean isWifi = "WIFI".equalsIgnoreCase(type);
4299c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        boolean wifiOff = (isWifi && !connected) || (wasWifi && !sameType);
4309c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        boolean wifiOn = isWifi && connected;
4319c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
4329c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        try {
4339c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            boolean wasConnected = mConnected;
4349c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mNetworkType = type;
4359c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mConnected = connected;
4369c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
4379c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (wasConnected) {
4389c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mLocalIp = null;
4399c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                for (SipSessionGroupExt group : mSipGroups.values()) {
4409c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    group.onConnectivityChanged(false);
4419c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                }
4429c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
4439c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
4449c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (connected) {
4459c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mLocalIp = determineLocalIp();
446469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang                mKeepAliveInterval = -1;
4479c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                for (SipSessionGroupExt group : mSipGroups.values()) {
4489c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    group.onConnectivityChanged(true);
4499c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                }
45087e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang                if (isWifi && (mWifiLock != null)) stopWifiScanner();
451257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan            } else {
452257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                mMyWakeLock.reset(); // in case there's a leak
453469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang                stopPortMappingMeasurement();
45487e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang                if (isWifi && (mWifiLock != null)) startWifiScanner();
4559c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
4569c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        } catch (SipException e) {
4579c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            Log.e(TAG, "onConnectivityChanged()", e);
4589c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
4599c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
4609c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
461469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang    private void stopPortMappingMeasurement() {
462469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang        if (mIntervalMeasurementProcess != null) {
463469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang            mIntervalMeasurementProcess.stop();
464469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang            mIntervalMeasurementProcess = null;
465469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang        }
466469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang    }
467469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang
468469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang    private void startPortMappingLifetimeMeasurement(SipSessionGroup group) {
469469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang        mIntervalMeasurementProcess = new IntervalMeasurementProcess(group);
470469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang        mIntervalMeasurementProcess.start();
471469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang    }
472469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang
4739c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private synchronized void addPendingSession(ISipSession session) {
4749c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        try {
4753f4d44657ccad3ed02ed0e1354387b14b07dc011Hung-ying Tyan            cleanUpPendingSessions();
4769c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mPendingSessions.put(session.getCallId(), session);
4773f4d44657ccad3ed02ed0e1354387b14b07dc011Hung-ying Tyan            if (DEBUG) Log.d(TAG, "#pending sess=" + mPendingSessions.size());
4789c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        } catch (RemoteException e) {
4799c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            // should not happen with a local call
4809c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            Log.e(TAG, "addPendingSession()", e);
4819c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
4829c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
4839c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
4843f4d44657ccad3ed02ed0e1354387b14b07dc011Hung-ying Tyan    private void cleanUpPendingSessions() throws RemoteException {
4853f4d44657ccad3ed02ed0e1354387b14b07dc011Hung-ying Tyan        Map.Entry<String, ISipSession>[] entries =
4863f4d44657ccad3ed02ed0e1354387b14b07dc011Hung-ying Tyan                mPendingSessions.entrySet().toArray(
4873f4d44657ccad3ed02ed0e1354387b14b07dc011Hung-ying Tyan                new Map.Entry[mPendingSessions.size()]);
4883f4d44657ccad3ed02ed0e1354387b14b07dc011Hung-ying Tyan        for (Map.Entry<String, ISipSession> entry : entries) {
4893f4d44657ccad3ed02ed0e1354387b14b07dc011Hung-ying Tyan            if (entry.getValue().getState() != SipSession.State.INCOMING_CALL) {
4903f4d44657ccad3ed02ed0e1354387b14b07dc011Hung-ying Tyan                mPendingSessions.remove(entry.getKey());
4913f4d44657ccad3ed02ed0e1354387b14b07dc011Hung-ying Tyan            }
4923f4d44657ccad3ed02ed0e1354387b14b07dc011Hung-ying Tyan        }
4933f4d44657ccad3ed02ed0e1354387b14b07dc011Hung-ying Tyan    }
4943f4d44657ccad3ed02ed0e1354387b14b07dc011Hung-ying Tyan
495ed3c0fbbd5457f43ff72cec31b8ab0bb3f7a0047Hung-ying Tyan    private synchronized boolean callingSelf(SipSessionGroupExt ringingGroup,
496ed3c0fbbd5457f43ff72cec31b8ab0bb3f7a0047Hung-ying Tyan            SipSessionGroup.SipSessionImpl ringingSession) {
497ed3c0fbbd5457f43ff72cec31b8ab0bb3f7a0047Hung-ying Tyan        String callId = ringingSession.getCallId();
498ed3c0fbbd5457f43ff72cec31b8ab0bb3f7a0047Hung-ying Tyan        for (SipSessionGroupExt group : mSipGroups.values()) {
499ed3c0fbbd5457f43ff72cec31b8ab0bb3f7a0047Hung-ying Tyan            if ((group != ringingGroup) && group.containsSession(callId)) {
500ed3c0fbbd5457f43ff72cec31b8ab0bb3f7a0047Hung-ying Tyan                if (DEBUG) Log.d(TAG, "call self: "
501ed3c0fbbd5457f43ff72cec31b8ab0bb3f7a0047Hung-ying Tyan                        + ringingSession.getLocalProfile().getUriString()
502ed3c0fbbd5457f43ff72cec31b8ab0bb3f7a0047Hung-ying Tyan                        + " -> " + group.getLocalProfile().getUriString());
503ed3c0fbbd5457f43ff72cec31b8ab0bb3f7a0047Hung-ying Tyan                return true;
504ed3c0fbbd5457f43ff72cec31b8ab0bb3f7a0047Hung-ying Tyan            }
505ed3c0fbbd5457f43ff72cec31b8ab0bb3f7a0047Hung-ying Tyan        }
506ed3c0fbbd5457f43ff72cec31b8ab0bb3f7a0047Hung-ying Tyan        return false;
507ed3c0fbbd5457f43ff72cec31b8ab0bb3f7a0047Hung-ying Tyan    }
508ed3c0fbbd5457f43ff72cec31b8ab0bb3f7a0047Hung-ying Tyan
509ed3c0fbbd5457f43ff72cec31b8ab0bb3f7a0047Hung-ying Tyan
5109c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private class SipSessionGroupExt extends SipSessionAdapter {
5119c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private SipSessionGroup mSipGroup;
512845f7332f04864c5483b3e63da5db076fc7a888aHung-ying Tyan        private PendingIntent mIncomingCallPendingIntent;
513617479363dcafe01bfa1fa025e1d9d122864a0ceHung-ying Tyan        private boolean mOpenedToReceiveCalls;
5149c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
5159c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private AutoRegistrationProcess mAutoRegistration =
5169c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                new AutoRegistrationProcess();
5179c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
5189c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public SipSessionGroupExt(SipProfile localProfile,
519845f7332f04864c5483b3e63da5db076fc7a888aHung-ying Tyan                PendingIntent incomingCallPendingIntent,
5209c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                ISipSessionListener listener) throws SipException {
5219c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            String password = localProfile.getPassword();
5229c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            SipProfile p = duplicate(localProfile);
5239c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mSipGroup = createSipSessionGroup(mLocalIp, p, password);
524845f7332f04864c5483b3e63da5db076fc7a888aHung-ying Tyan            mIncomingCallPendingIntent = incomingCallPendingIntent;
5259c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mAutoRegistration.setListener(listener);
5269c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
5279c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
5289c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public SipProfile getLocalProfile() {
5299c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return mSipGroup.getLocalProfile();
5309c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
5319c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
532ed3c0fbbd5457f43ff72cec31b8ab0bb3f7a0047Hung-ying Tyan        public boolean containsSession(String callId) {
533ed3c0fbbd5457f43ff72cec31b8ab0bb3f7a0047Hung-ying Tyan            return mSipGroup.containsSession(callId);
534ed3c0fbbd5457f43ff72cec31b8ab0bb3f7a0047Hung-ying Tyan        }
535ed3c0fbbd5457f43ff72cec31b8ab0bb3f7a0047Hung-ying Tyan
5369c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        // network connectivity is tricky because network can be disconnected
5379c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        // at any instant so need to deal with exceptions carefully even when
5389c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        // you think you are connected
5399c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private SipSessionGroup createSipSessionGroup(String localIp,
5409c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                SipProfile localProfile, String password) throws SipException {
5419c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            try {
542acedadc1967457ac2f8981c67f884fd8c0ee853cHung-ying Tyan                return new SipSessionGroup(localIp, localProfile, password,
543acedadc1967457ac2f8981c67f884fd8c0ee853cHung-ying Tyan                        mMyWakeLock);
5449c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            } catch (IOException e) {
5459c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                // network disconnected
5469c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                Log.w(TAG, "createSipSessionGroup(): network disconnected?");
5479c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                if (localIp != null) {
5489c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    return createSipSessionGroup(null, localProfile, password);
5499c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                } else {
5509c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    // recursive
5516d9d99615a30de0675271553552c3c7b49311354Joe Onorato                    Log.wtf(TAG, "impossible! recursive!");
5529c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    throw new RuntimeException("createSipSessionGroup");
5539c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                }
5549c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
5559c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
5569c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
5579c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private SipProfile duplicate(SipProfile p) {
5589c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            try {
5599c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                return new SipProfile.Builder(p).setPassword("*").build();
5609c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            } catch (Exception e) {
5619c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                Log.wtf(TAG, "duplicate()", e);
5629c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                throw new RuntimeException("duplicate profile", e);
5639c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
5649c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
5659c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
5669c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public void setListener(ISipSessionListener listener) {
5679c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mAutoRegistration.setListener(listener);
5689c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
5699c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
570845f7332f04864c5483b3e63da5db076fc7a888aHung-ying Tyan        public void setIncomingCallPendingIntent(PendingIntent pIntent) {
571845f7332f04864c5483b3e63da5db076fc7a888aHung-ying Tyan            mIncomingCallPendingIntent = pIntent;
5729c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
5739c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
5749c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public void openToReceiveCalls() throws SipException {
575617479363dcafe01bfa1fa025e1d9d122864a0ceHung-ying Tyan            mOpenedToReceiveCalls = true;
5769c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (mConnected) {
5779c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mSipGroup.openToReceiveCalls(this);
5789c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mAutoRegistration.start(mSipGroup);
5799c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
5809c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (DEBUG) Log.d(TAG, "  openToReceiveCalls: " + getUri() + ": "
581845f7332f04864c5483b3e63da5db076fc7a888aHung-ying Tyan                    + mIncomingCallPendingIntent);
5829c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
5839c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
5849c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public void onConnectivityChanged(boolean connected)
5859c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                throws SipException {
5869c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mSipGroup.onConnectivityChanged();
5879c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (connected) {
5889c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                resetGroup(mLocalIp);
589617479363dcafe01bfa1fa025e1d9d122864a0ceHung-ying Tyan                if (mOpenedToReceiveCalls) openToReceiveCalls();
5909c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            } else {
591617479363dcafe01bfa1fa025e1d9d122864a0ceHung-ying Tyan                // close mSipGroup but remember mOpenedToReceiveCalls
5929c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                if (DEBUG) Log.d(TAG, "  close auto reg temporarily: "
593845f7332f04864c5483b3e63da5db076fc7a888aHung-ying Tyan                        + getUri() + ": " + mIncomingCallPendingIntent);
5949c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mSipGroup.close();
5959c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mAutoRegistration.stop();
5969c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
5979c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
5989c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
5999c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private void resetGroup(String localIp) throws SipException {
6009c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            try {
6019c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mSipGroup.reset(localIp);
6029c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            } catch (IOException e) {
6039c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                // network disconnected
6049c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                Log.w(TAG, "resetGroup(): network disconnected?");
6059c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                if (localIp != null) {
6069c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    resetGroup(null); // reset w/o local IP
6079c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                } else {
6089c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    // recursive
6099c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    Log.wtf(TAG, "impossible!");
6109c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    throw new RuntimeException("resetGroup");
6119c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                }
6129c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
6139c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
6149c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
6159c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public void close() {
616617479363dcafe01bfa1fa025e1d9d122864a0ceHung-ying Tyan            mOpenedToReceiveCalls = false;
6179c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mSipGroup.close();
6189c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mAutoRegistration.stop();
6199c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (DEBUG) Log.d(TAG, "   close: " + getUri() + ": "
620845f7332f04864c5483b3e63da5db076fc7a888aHung-ying Tyan                    + mIncomingCallPendingIntent);
6219c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
6229c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
6239c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public ISipSession createSession(ISipSessionListener listener) {
6249c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return mSipGroup.createSession(listener);
6259c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
6269c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
6279c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        @Override
628845f7332f04864c5483b3e63da5db076fc7a888aHung-ying Tyan        public void onRinging(ISipSession s, SipProfile caller,
6299c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                String sessionDescription) {
630acedadc1967457ac2f8981c67f884fd8c0ee853cHung-ying Tyan            if (DEBUGV) Log.d(TAG, "<<<<< onRinging()");
631845f7332f04864c5483b3e63da5db076fc7a888aHung-ying Tyan            SipSessionGroup.SipSessionImpl session =
632845f7332f04864c5483b3e63da5db076fc7a888aHung-ying Tyan                    (SipSessionGroup.SipSessionImpl) s;
6339c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            synchronized (SipService.this) {
6349c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                try {
635ed3c0fbbd5457f43ff72cec31b8ab0bb3f7a0047Hung-ying Tyan                    if (!isRegistered() || callingSelf(this, session)) {
6369c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        session.endCall();
6379c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        return;
6389c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    }
6399c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
6409c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    // send out incoming call broadcast
6419c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    addPendingSession(session);
6429c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    Intent intent = SipManager.createIncomingCallBroadcast(
643845f7332f04864c5483b3e63da5db076fc7a888aHung-ying Tyan                            session.getCallId(), sessionDescription);
6449c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    if (DEBUG) Log.d(TAG, " ringing~~ " + getUri() + ": "
6459c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            + caller.getUri() + ": " + session.getCallId()
646845f7332f04864c5483b3e63da5db076fc7a888aHung-ying Tyan                            + " " + mIncomingCallPendingIntent);
647845f7332f04864c5483b3e63da5db076fc7a888aHung-ying Tyan                    mIncomingCallPendingIntent.send(mContext,
648845f7332f04864c5483b3e63da5db076fc7a888aHung-ying Tyan                            SipManager.INCOMING_CALL_RESULT_CODE, intent);
649845f7332f04864c5483b3e63da5db076fc7a888aHung-ying Tyan                } catch (PendingIntent.CanceledException e) {
650845f7332f04864c5483b3e63da5db076fc7a888aHung-ying Tyan                    Log.w(TAG, "pendingIntent is canceled, drop incoming call");
651845f7332f04864c5483b3e63da5db076fc7a888aHung-ying Tyan                    session.endCall();
6529c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                }
6539c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
6549c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
6559c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
6569c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        @Override
6579c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public void onError(ISipSession session, int errorCode,
6589c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                String message) {
6599c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (DEBUG) Log.d(TAG, "sip session error: "
6609c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    + SipErrorCode.toString(errorCode) + ": " + message);
6619c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
6629c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
663617479363dcafe01bfa1fa025e1d9d122864a0ceHung-ying Tyan        public boolean isOpenedToReceiveCalls() {
664617479363dcafe01bfa1fa025e1d9d122864a0ceHung-ying Tyan            return mOpenedToReceiveCalls;
6659c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
6669c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
6679c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public boolean isRegistered() {
6689c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return mAutoRegistration.isRegistered();
6699c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
6709c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
6719c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private String getUri() {
6729c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return mSipGroup.getLocalProfileUri();
6739c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
6749c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
6759c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
67687e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang    private class WifiScanProcess implements Runnable {
67787e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang        private static final String TAG = "\\WIFI_SCAN/";
67887e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang        private static final int INTERVAL = 60;
67987e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang        private boolean mRunning = false;
68087e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang
68187e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang        private WifiManager mWifiManager;
68287e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang
68387e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang        public void start() {
68487e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang            if (mRunning) return;
68587e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang            mRunning = true;
68687e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang            mTimer.set(INTERVAL * 1000, this);
68787e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang        }
68887e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang
68987e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang        WifiScanProcess() {
69087e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang            mWifiManager = (WifiManager)
69187e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang                    mContext.getSystemService(Context.WIFI_SERVICE);
69287e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang        }
69387e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang
69487e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang        public void run() {
69587e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang            // scan and associate now
69687e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang            if (DEBUGV) Log.v(TAG, "just wake up here for wifi scanning...");
69787e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang            mWifiManager.startScanActive();
69887e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang        }
69987e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang
70087e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang        public void stop() {
70187e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang            mRunning = false;
70287e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang            mTimer.cancel(this);
70387e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang        }
70487e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang    }
70587e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang
706469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang    private class IntervalMeasurementProcess extends SipSessionAdapter
707469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang            implements Runnable {
708469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang        private static final String TAG = "\\INTERVAL/";
709469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang        private static final int MAX_INTERVAL = 120; // seconds
710469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang        private static final int MIN_INTERVAL = SHORT_EXPIRY_TIME;
711469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang        private static final int PASS_THRESHOLD = 6;
712469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang        private SipSessionGroupExt mGroup;
713469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang        private SipSessionGroup.SipSessionImpl mSession;
714469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang        private boolean mRunning;
715469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang        private int mMinInterval = 10;
716469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang        private int mMaxInterval = MAX_INTERVAL;
717469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang        private int mInterval = MAX_INTERVAL / 2;
718469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang        private int mPassCounter = 0;
719469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang        private WakeupTimer mTimer = new WakeupTimer(mContext);
720469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang
721469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang
722469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang        public IntervalMeasurementProcess(SipSessionGroup group) {
723469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang            try {
724469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang                mGroup =  new SipSessionGroupExt(
725469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang                        group.getLocalProfile(), null, null);
726469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang                mSession = (SipSessionGroup.SipSessionImpl)
727469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang                        mGroup.createSession(this);
728469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang            } catch (Exception e) {
729469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang                Log.w(TAG, "start interval measurement error: " + e);
730469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang            }
731469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang        }
732469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang
733469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang        public void start() {
734469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang            if (mRunning) return;
735469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang            mRunning = true;
736469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang            mTimer.set(mInterval * 1000, this);
737469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang            if (DEBUGV) Log.v(TAG, "start interval measurement");
738469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang            run();
739469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang        }
740469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang
741469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang        public void stop() {
742469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang            mRunning = false;
743469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang            mTimer.cancel(this);
744469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang        }
745469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang
746469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang        private void restart() {
747469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang            mTimer.cancel(this);
748469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang            mTimer.set(mInterval * 1000, this);
749469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang        }
750469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang
751469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang        private void calculateNewInterval() {
752469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang            if (!mSession.isReRegisterRequired()) {
753469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang                if (++mPassCounter != PASS_THRESHOLD) return;
754469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang                // update the interval, since the current interval is good to
755469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang                // keep the port mapping.
756469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang                mKeepAliveInterval = mMinInterval = mInterval;
757469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang            } else {
758469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang                // Since the rport is changed, shorten the interval.
759469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang                mSession.clearReRegisterRequired();
760469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang                mMaxInterval = mInterval;
761469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang            }
762469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang            if ((mMaxInterval - mMinInterval) < MIN_INTERVAL) {
763469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang                // update mKeepAliveInterval and stop measurement.
764469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang                stop();
765469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang                mKeepAliveInterval = mMinInterval;
766469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang                if (DEBUGV) Log.v(TAG, "measured interval: " + mKeepAliveInterval);
767469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang            } else {
768469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang                // calculate the new interval and continue.
769469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang                mInterval = (mMaxInterval + mMinInterval) / 2;
770469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang                mPassCounter = 0;
771469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang                if (DEBUGV) {
772469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang                    Log.v(TAG, " current interval: " + mKeepAliveInterval
773469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang                            + "test new interval: " + mInterval);
774469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang                }
775469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang                restart();
776469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang            }
777469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang        }
778469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang
779469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang        public void run() {
780469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang            synchronized (SipService.this) {
781469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang                if (!mRunning) return;
782469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang                try {
783469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang                    mSession.sendKeepAlive();
784469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang                    calculateNewInterval();
785469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang                } catch (Throwable t) {
786469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang                    stop();
787469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang                    Log.w(TAG, "interval measurement error: " + t);
788469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang                }
789469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang            }
790469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang        }
791469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang    }
792469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang
793d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan    // KeepAliveProcess is controlled by AutoRegistrationProcess.
794257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan    // All methods will be invoked in sync with SipService.this.
7959c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private class KeepAliveProcess implements Runnable {
7969c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private static final String TAG = "\\KEEPALIVE/";
7979c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private static final int INTERVAL = 10;
7989c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private SipSessionGroup.SipSessionImpl mSession;
799d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan        private boolean mRunning = false;
800469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang        private int mInterval = INTERVAL;
8019c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
8029c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public KeepAliveProcess(SipSessionGroup.SipSessionImpl session) {
8039c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mSession = session;
8049c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
8059c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
8069c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public void start() {
807d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan            if (mRunning) return;
808d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan            mRunning = true;
8099c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mTimer.set(INTERVAL * 1000, this);
8109c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
8119c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
812469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang        private void restart(int duration) {
813469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang            if (DEBUG) Log.d(TAG, "Refresh NAT port mapping " + duration + "s later.");
814469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang            mTimer.cancel(this);
815469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang            mTimer.set(duration * 1000, this);
816469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang        }
817469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang
818d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan        // timeout handler
8199c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public void run() {
8209c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            synchronized (SipService.this) {
821257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                if (!mRunning) return;
822d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan
823257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                if (DEBUGV) Log.v(TAG, "~~~ keepalive: "
824257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                        + mSession.getLocalProfile().getUriString());
825257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                SipSessionGroup.SipSessionImpl session = mSession.duplicate();
826257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                try {
827257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                    session.sendKeepAlive();
828257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                    if (session.isReRegisterRequired()) {
829257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                        // Acquire wake lock for the registration process. The
830257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                        // lock will be released when registration is complete.
831257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                        mMyWakeLock.acquire(mSession);
832257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                        mSession.register(EXPIRY_TIME);
833257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                    }
834469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang                    if (mKeepAliveInterval > mInterval) {
835469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang                        mInterval = mKeepAliveInterval;
836469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang                        restart(mInterval);
837469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang                    }
838257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                } catch (Throwable t) {
839257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                    Log.w(TAG, "keepalive error: " + t);
8409c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                }
8419c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
8429c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
8439c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
8449c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public void stop() {
845257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan            if (DEBUGV && (mSession != null)) Log.v(TAG, "stop keepalive:"
846257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                    + mSession.getLocalProfile().getUriString());
847d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan            mRunning = false;
848d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan            mSession = null;
8499c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mTimer.cancel(this);
8509c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
8519c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
8529c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
8539c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private class AutoRegistrationProcess extends SipSessionAdapter
8549c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            implements Runnable {
8559c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private SipSessionGroup.SipSessionImpl mSession;
8569c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private SipSessionListenerProxy mProxy = new SipSessionListenerProxy();
8579c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private KeepAliveProcess mKeepAliveProcess;
8589c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private int mBackoff = 1;
8599c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private boolean mRegistered;
8609c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private long mExpiryTime;
8619c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private int mErrorCode;
8629c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private String mErrorMessage;
863d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan        private boolean mRunning = false;
8649c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
8659c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private String getAction() {
8669c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return toString();
8679c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
8689c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
8699c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public void start(SipSessionGroup group) {
870d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan            if (!mRunning) {
871d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                mRunning = true;
8729c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mBackoff = 1;
8739c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mSession = (SipSessionGroup.SipSessionImpl)
8749c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        group.createSession(this);
8759c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                // return right away if no active network connection.
8769c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                if (mSession == null) return;
8779c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
878469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang                synchronized (SipService.this) {
879469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang                    if (isBehindNAT(mLocalIp)
880469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang                            && (mIntervalMeasurementProcess == null)
881469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang                            && (mKeepAliveInterval == -1)) {
882469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang                        // Start keep-alive interval measurement, here we allow
883469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang                        // the first profile only as the target service provider
884469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang                        // to measure the life time of NAT port mapping.
885469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang                        startPortMappingLifetimeMeasurement(group);
886469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang                    }
887469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang                }
888469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang
8899c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                // start unregistration to clear up old registration at server
8909c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                // TODO: when rfc5626 is deployed, use reg-id and sip.instance
8919c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                // in registration to avoid adding duplicate entries to server
892257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                mMyWakeLock.acquire(mSession);
8939c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mSession.unregister();
8949c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                if (DEBUG) Log.d(TAG, "start AutoRegistrationProcess for "
8959c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        + mSession.getLocalProfile().getUriString());
8969c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
8979c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
8989c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
8999c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public void stop() {
900d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan            if (!mRunning) return;
901d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan            mRunning = false;
902257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan            mMyWakeLock.release(mSession);
903257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan            if (mSession != null) {
904257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                mSession.setListener(null);
905257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                if (mConnected && mRegistered) mSession.unregister();
906257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan            }
907d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan
9089c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mTimer.cancel(this);
9099c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (mKeepAliveProcess != null) {
9109c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mKeepAliveProcess.stop();
9119c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mKeepAliveProcess = null;
9129c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
9139c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
914d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan            mRegistered = false;
915d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan            setListener(mProxy.getListener());
9169c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
9179c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
9189c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public void setListener(ISipSessionListener listener) {
9199c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            synchronized (SipService.this) {
9209c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mProxy.setListener(listener);
9219c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
9229c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                try {
9239c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    int state = (mSession == null)
9249c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            ? SipSession.State.READY_TO_CALL
9259c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            : mSession.getState();
9269c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    if ((state == SipSession.State.REGISTERING)
9279c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            || (state == SipSession.State.DEREGISTERING)) {
9289c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        mProxy.onRegistering(mSession);
9299c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    } else if (mRegistered) {
9309c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        int duration = (int)
9319c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                                (mExpiryTime - SystemClock.elapsedRealtime());
9329c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        mProxy.onRegistrationDone(mSession, duration);
9339c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    } else if (mErrorCode != SipErrorCode.NO_ERROR) {
9349c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        if (mErrorCode == SipErrorCode.TIME_OUT) {
9359c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            mProxy.onRegistrationTimeout(mSession);
9369c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        } else {
9379c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            mProxy.onRegistrationFailed(mSession, mErrorCode,
9389c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                                    mErrorMessage);
9399c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        }
940d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                    } else if (!mConnected) {
941d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                        mProxy.onRegistrationFailed(mSession,
942d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                                SipErrorCode.DATA_CONNECTION_LOST,
943d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                                "no data connection");
944d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                    } else if (!mRunning) {
945d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                        mProxy.onRegistrationFailed(mSession,
946d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                                SipErrorCode.CLIENT_ERROR,
947d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                                "registration not running");
948d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                    } else {
949d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                        mProxy.onRegistrationFailed(mSession,
950d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                                SipErrorCode.IN_PROGRESS,
951d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                                String.valueOf(state));
9529c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    }
9539c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                } catch (Throwable t) {
9549c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    Log.w(TAG, "setListener(): " + t);
9559c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                }
9569c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
9579c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
9589c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
9599c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public boolean isRegistered() {
9609c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return mRegistered;
9619c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
9629c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
963257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan        // timeout handler: re-register
9649c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public void run() {
965d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan            synchronized (SipService.this) {
966d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                if (!mRunning) return;
967d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan
968d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                mErrorCode = SipErrorCode.NO_ERROR;
969d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                mErrorMessage = null;
970d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                if (DEBUG) Log.d(TAG, "~~~ registering");
971257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                if (mConnected) {
972257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                    mMyWakeLock.acquire(mSession);
973257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                    mSession.register(EXPIRY_TIME);
974257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                }
9759c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
9769c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
9779c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
9789c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private boolean isBehindNAT(String address) {
9799c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            try {
9809c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                byte[] d = InetAddress.getByName(address).getAddress();
9819c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                if ((d[0] == 10) ||
9829c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        (((0x000000FF & ((int)d[0])) == 172) &&
9839c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        ((0x000000F0 & ((int)d[1])) == 16)) ||
9849c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        (((0x000000FF & ((int)d[0])) == 192) &&
9859c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        ((0x000000FF & ((int)d[1])) == 168))) {
9869c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    return true;
9879c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                }
9889c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            } catch (UnknownHostException e) {
9899c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                Log.e(TAG, "isBehindAT()" + address, e);
9909c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
9919c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return false;
9929c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
9939c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
9949c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private void restart(int duration) {
9959c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (DEBUG) Log.d(TAG, "Refresh registration " + duration + "s later.");
9969c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mTimer.cancel(this);
9979c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mTimer.set(duration * 1000, this);
9989c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
9999c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
10009c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private int backoffDuration() {
10019c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            int duration = SHORT_EXPIRY_TIME * mBackoff;
10029c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (duration > 3600) {
10039c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                duration = 3600;
10049c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            } else {
10059c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mBackoff *= 2;
10069c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
10079c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return duration;
10089c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
10099c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
10109c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        @Override
10119c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public void onRegistering(ISipSession session) {
10129c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (DEBUG) Log.d(TAG, "onRegistering(): " + session);
10139c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            synchronized (SipService.this) {
1014d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                if (notCurrentSession(session)) return;
1015d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan
10169c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mRegistered = false;
10179c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mProxy.onRegistering(session);
10189c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
10199c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
10209c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
1021d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan        private boolean notCurrentSession(ISipSession session) {
1022d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan            if (session != mSession) {
1023d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                ((SipSessionGroup.SipSessionImpl) session).setListener(null);
1024257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                mMyWakeLock.release(session);
1025d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                return true;
1026d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan            }
1027d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan            return !mRunning;
1028d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan        }
1029d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan
10309c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        @Override
10319c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public void onRegistrationDone(ISipSession session, int duration) {
10329c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (DEBUG) Log.d(TAG, "onRegistrationDone(): " + session);
10339c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            synchronized (SipService.this) {
1034d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                if (notCurrentSession(session)) return;
10359c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
10369c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mProxy.onRegistrationDone(session, duration);
10379c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
10389c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                if (duration > 0) {
10399c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    mSession.clearReRegisterRequired();
10409c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    mExpiryTime = SystemClock.elapsedRealtime()
10419c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            + (duration * 1000);
10429c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
10439c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    if (!mRegistered) {
10449c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        mRegistered = true;
10459c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        // allow some overlap to avoid call drop during renew
10469c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        duration -= MIN_EXPIRY_TIME;
10479c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        if (duration < MIN_EXPIRY_TIME) {
10489c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            duration = MIN_EXPIRY_TIME;
10499c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        }
10509c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        restart(duration);
10519c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
10529c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        if (isBehindNAT(mLocalIp) ||
10539c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                                mSession.getLocalProfile().getSendKeepAlive()) {
10549c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            if (mKeepAliveProcess == null) {
10559c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                                mKeepAliveProcess =
10569c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                                        new KeepAliveProcess(mSession);
10579c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            }
10589c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            mKeepAliveProcess.start();
10599c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        }
10609c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    }
1061257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                    mMyWakeLock.release(session);
10629c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                } else {
10639c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    mRegistered = false;
10649c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    mExpiryTime = -1L;
10659c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    if (DEBUG) Log.d(TAG, "Refresh registration immediately");
10669c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    run();
10679c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                }
10689c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
10699c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
10709c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
10719c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        @Override
10729c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public void onRegistrationFailed(ISipSession session, int errorCode,
10739c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                String message) {
10749c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (DEBUG) Log.d(TAG, "onRegistrationFailed(): " + session + ": "
10759c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    + SipErrorCode.toString(errorCode) + ": " + message);
10769c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            synchronized (SipService.this) {
1077d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                if (notCurrentSession(session)) return;
10789c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
1079fcc474636f42243cfce960158373b9c49636a556Hung-ying Tyan                switch (errorCode) {
1080fcc474636f42243cfce960158373b9c49636a556Hung-ying Tyan                    case SipErrorCode.INVALID_CREDENTIALS:
1081fcc474636f42243cfce960158373b9c49636a556Hung-ying Tyan                    case SipErrorCode.SERVER_UNREACHABLE:
1082fcc474636f42243cfce960158373b9c49636a556Hung-ying Tyan                        if (DEBUG) Log.d(TAG, "   pause auto-registration");
1083fcc474636f42243cfce960158373b9c49636a556Hung-ying Tyan                        stop();
10840b88a073712b1510db7c636f89c4e19f0131449aHung-ying Tyan                        break;
1085fcc474636f42243cfce960158373b9c49636a556Hung-ying Tyan                    default:
1086fcc474636f42243cfce960158373b9c49636a556Hung-ying Tyan                        restartLater();
10879c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                }
1088d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan
1089d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                mErrorCode = errorCode;
1090d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                mErrorMessage = message;
1091d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                mProxy.onRegistrationFailed(session, errorCode, message);
1092257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                mMyWakeLock.release(session);
10939c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
10949c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
10959c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
10969c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        @Override
10979c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public void onRegistrationTimeout(ISipSession session) {
10989c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (DEBUG) Log.d(TAG, "onRegistrationTimeout(): " + session);
10999c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            synchronized (SipService.this) {
1100d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                if (notCurrentSession(session)) return;
1101d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan
11029c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mErrorCode = SipErrorCode.TIME_OUT;
11039c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mProxy.onRegistrationTimeout(session);
1104fcc474636f42243cfce960158373b9c49636a556Hung-ying Tyan                restartLater();
1105257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                mMyWakeLock.release(session);
11069c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
11079c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
11089c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
1109fcc474636f42243cfce960158373b9c49636a556Hung-ying Tyan        private void restartLater() {
11109c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mRegistered = false;
11119c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            restart(backoffDuration());
11129c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (mKeepAliveProcess != null) {
11139c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mKeepAliveProcess.stop();
11149c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mKeepAliveProcess = null;
11159c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
11169c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
11179c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
11189c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
11199c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private class ConnectivityReceiver extends BroadcastReceiver {
11209c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private Timer mTimer = new Timer();
11219c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private MyTimerTask mTask;
11229c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
11239c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        @Override
1124257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan        public void onReceive(final Context context, final Intent intent) {
1125257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan            // Run the handler in MyExecutor to be protected by wake lock
1126257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan            getExecutor().execute(new Runnable() {
1127257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                public void run() {
1128257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                    onReceiveInternal(context, intent);
1129257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                }
1130257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan            });
1131257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan        }
1132257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan
1133257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan        private void onReceiveInternal(Context context, Intent intent) {
11349c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            String action = intent.getAction();
11359c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
11369c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                Bundle b = intent.getExtras();
11379c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                if (b != null) {
11389c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    NetworkInfo netInfo = (NetworkInfo)
11399c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            b.get(ConnectivityManager.EXTRA_NETWORK_INFO);
11409c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    String type = netInfo.getTypeName();
11419c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    NetworkInfo.State state = netInfo.getState();
11429c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
11439c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    if (mWifiOnly && (netInfo.getType() !=
11449c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            ConnectivityManager.TYPE_WIFI)) {
11459c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        if (DEBUG) {
11469c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            Log.d(TAG, "Wifi only, other connectivity ignored: "
11479c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                                    + type);
11489c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        }
11499c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        return;
11509c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    }
11519c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
11529c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    NetworkInfo activeNetInfo = getActiveNetworkInfo();
11539c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    if (DEBUG) {
11549c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        if (activeNetInfo != null) {
11559c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            Log.d(TAG, "active network: "
11569c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                                    + activeNetInfo.getTypeName()
11579c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                                    + ((activeNetInfo.getState() == NetworkInfo.State.CONNECTED)
11589c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                                            ? " CONNECTED" : " DISCONNECTED"));
11599c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        } else {
11609c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            Log.d(TAG, "active network: null");
11619c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        }
11629c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    }
11639c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    if ((state == NetworkInfo.State.CONNECTED)
11649c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            && (activeNetInfo != null)
11659c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            && (activeNetInfo.getType() != netInfo.getType())) {
11669c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        if (DEBUG) Log.d(TAG, "ignore connect event: " + type
11679c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                                + ", active: " + activeNetInfo.getTypeName());
11689c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        return;
11699c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    }
11709c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
11719c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    if (state == NetworkInfo.State.CONNECTED) {
11729c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        if (DEBUG) Log.d(TAG, "Connectivity alert: CONNECTED " + type);
11739c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        onChanged(type, true);
11749c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    } else if (state == NetworkInfo.State.DISCONNECTED) {
11759c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        if (DEBUG) Log.d(TAG, "Connectivity alert: DISCONNECTED " + type);
11769c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        onChanged(type, false);
11779c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    } else {
11789c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        if (DEBUG) Log.d(TAG, "Connectivity alert not processed: "
11799c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                                + state + " " + type);
11809c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    }
11819c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                }
11829c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
11839c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
11849c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
11859c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private NetworkInfo getActiveNetworkInfo() {
11869c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            ConnectivityManager cm = (ConnectivityManager)
11879c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
11889c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return cm.getActiveNetworkInfo();
11899c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
11909c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
11919c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private void onChanged(String type, boolean connected) {
11929c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            synchronized (SipService.this) {
11939c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                // When turning on WIFI, it needs some time for network
11949c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                // connectivity to get stabile so we defer good news (because
11959c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                // we want to skip the interim ones) but deliver bad news
11969c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                // immediately
11979c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                if (connected) {
1198f4a38e8e2f83402d2307edf67ef79b6f74990c8bHung-ying Tyan                    if (mTask != null) {
1199f4a38e8e2f83402d2307edf67ef79b6f74990c8bHung-ying Tyan                        mTask.cancel();
1200f4a38e8e2f83402d2307edf67ef79b6f74990c8bHung-ying Tyan                        mMyWakeLock.release(mTask);
1201f4a38e8e2f83402d2307edf67ef79b6f74990c8bHung-ying Tyan                    }
12029c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    mTask = new MyTimerTask(type, connected);
12039c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    mTimer.schedule(mTask, 2 * 1000L);
1204257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                    // hold wakup lock so that we can finish changes before the
1205257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                    // device goes to sleep
1206257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                    mMyWakeLock.acquire(mTask);
12079c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                } else {
12089c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    if ((mTask != null) && mTask.mNetworkType.equals(type)) {
12099c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        mTask.cancel();
1210257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                        mMyWakeLock.release(mTask);
12119c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    }
12129c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    onConnectivityChanged(type, false);
12139c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                }
12149c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
12159c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
12169c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
12179c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private class MyTimerTask extends TimerTask {
12189c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            private boolean mConnected;
12199c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            private String mNetworkType;
12209c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
12219c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            public MyTimerTask(String type, boolean connected) {
12229c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mNetworkType = type;
12239c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mConnected = connected;
12249c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
12259c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
1226d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan            // timeout handler
12279c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            @Override
12289c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            public void run() {
12299c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                // delegate to mExecutor
1230257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                getExecutor().execute(new Runnable() {
12319c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    public void run() {
12329c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        realRun();
12339c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    }
12349c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                });
12359c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
12369c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
12379c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            private void realRun() {
12389c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                synchronized (SipService.this) {
12399c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    if (mTask != this) {
12409c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        Log.w(TAG, "  unexpected task: " + mNetworkType
12419c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                                + (mConnected ? " CONNECTED" : "DISCONNECTED"));
1242f4a38e8e2f83402d2307edf67ef79b6f74990c8bHung-ying Tyan                        mMyWakeLock.release(this);
12439c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        return;
12449c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    }
12459c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    mTask = null;
12469c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    if (DEBUG) Log.d(TAG, " deliver change for " + mNetworkType
12479c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            + (mConnected ? " CONNECTED" : "DISCONNECTED"));
12489c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    onConnectivityChanged(mNetworkType, mConnected);
1249257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                    mMyWakeLock.release(this);
12509c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                }
12519c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
12529c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
12539c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
12549c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
12559c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    /**
12569c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan     * Timer that can schedule events to occur even when the device is in sleep.
12579c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan     * Only used internally in this package.
12589c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan     */
12599c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    class WakeupTimer extends BroadcastReceiver {
12609c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private static final String TAG = "_SIP.WkTimer_";
12619c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private static final String TRIGGER_TIME = "TriggerTime";
12629c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
12639c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private Context mContext;
12649c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private AlarmManager mAlarmManager;
12659c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
12669c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        // runnable --> time to execute in SystemClock
12679c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private TreeSet<MyEvent> mEventQueue =
12689c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                new TreeSet<MyEvent>(new MyEventComparator());
12699c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
12709c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private PendingIntent mPendingIntent;
12719c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
12729c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public WakeupTimer(Context context) {
12739c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mContext = context;
12749c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mAlarmManager = (AlarmManager)
12759c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    context.getSystemService(Context.ALARM_SERVICE);
12769c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
12779c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            IntentFilter filter = new IntentFilter(getAction());
12789c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            context.registerReceiver(this, filter);
12799c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
12809c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
12819c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        /**
12829c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan         * Stops the timer. No event can be scheduled after this method is called.
12839c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan         */
12849c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public synchronized void stop() {
12859c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mContext.unregisterReceiver(this);
12869c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (mPendingIntent != null) {
12879c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mAlarmManager.cancel(mPendingIntent);
12889c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mPendingIntent = null;
12899c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
12909c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mEventQueue.clear();
12919c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mEventQueue = null;
12929c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
12939c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
12949c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private synchronized boolean stopped() {
12959c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (mEventQueue == null) {
12969c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                Log.w(TAG, "Timer stopped");
12979c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                return true;
12989c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            } else {
12999c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                return false;
13009c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
13019c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
13029c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
13039c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private void cancelAlarm() {
13049c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mAlarmManager.cancel(mPendingIntent);
13059c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mPendingIntent = null;
13069c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
13079c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
13089c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private void recalculatePeriods() {
13099c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (mEventQueue.isEmpty()) return;
13109c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
13119c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            MyEvent firstEvent = mEventQueue.first();
13129c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            int minPeriod = firstEvent.mMaxPeriod;
13139c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            long minTriggerTime = firstEvent.mTriggerTime;
13149c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            for (MyEvent e : mEventQueue) {
13159c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                e.mPeriod = e.mMaxPeriod / minPeriod * minPeriod;
13169c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                int interval = (int) (e.mLastTriggerTime + e.mMaxPeriod
13179c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        - minTriggerTime);
13189c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                interval = interval / minPeriod * minPeriod;
13199c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                e.mTriggerTime = minTriggerTime + interval;
13209c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
13219c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            TreeSet<MyEvent> newQueue = new TreeSet<MyEvent>(
13229c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    mEventQueue.comparator());
13239c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            newQueue.addAll((Collection<MyEvent>) mEventQueue);
13249c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mEventQueue.clear();
13259c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mEventQueue = newQueue;
13269c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (DEBUG_TIMER) {
13279c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                Log.d(TAG, "queue re-calculated");
13289c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                printQueue();
13299c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
13309c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
13319c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
13329c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        // Determines the period and the trigger time of the new event and insert it
13339c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        // to the queue.
13349c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private void insertEvent(MyEvent event) {
13359c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            long now = SystemClock.elapsedRealtime();
13369c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (mEventQueue.isEmpty()) {
13379c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                event.mTriggerTime = now + event.mPeriod;
13389c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mEventQueue.add(event);
13399c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                return;
13409c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
13419c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            MyEvent firstEvent = mEventQueue.first();
13429c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            int minPeriod = firstEvent.mPeriod;
13439c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (minPeriod <= event.mMaxPeriod) {
13449c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                event.mPeriod = event.mMaxPeriod / minPeriod * minPeriod;
13459c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                int interval = event.mMaxPeriod;
13469c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                interval -= (int) (firstEvent.mTriggerTime - now);
13479c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                interval = interval / minPeriod * minPeriod;
13489c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                event.mTriggerTime = firstEvent.mTriggerTime + interval;
13499c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mEventQueue.add(event);
13509c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            } else {
13519c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                long triggerTime = now + event.mPeriod;
13529c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                if (firstEvent.mTriggerTime < triggerTime) {
13539c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    event.mTriggerTime = firstEvent.mTriggerTime;
13549c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    event.mLastTriggerTime -= event.mPeriod;
13559c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                } else {
13569c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    event.mTriggerTime = triggerTime;
13579c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                }
13589c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mEventQueue.add(event);
13599c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                recalculatePeriods();
13609c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
13619c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
13629c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
13639c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        /**
13649c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan         * Sets a periodic timer.
13659c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan         *
13669c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan         * @param period the timer period; in milli-second
13679c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan         * @param callback is called back when the timer goes off; the same callback
13689c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan         *      can be specified in multiple timer events
13699c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan         */
13709c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public synchronized void set(int period, Runnable callback) {
13719c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (stopped()) return;
13729c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
13739c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            long now = SystemClock.elapsedRealtime();
13749c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            MyEvent event = new MyEvent(period, callback, now);
13759c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            insertEvent(event);
13769c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
13779c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (mEventQueue.first() == event) {
13789c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                if (mEventQueue.size() > 1) cancelAlarm();
13799c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                scheduleNext();
13809c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
13819c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
13829c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            long triggerTime = event.mTriggerTime;
13839c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (DEBUG_TIMER) {
13849c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                Log.d(TAG, " add event " + event + " scheduled at "
13859c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        + showTime(triggerTime) + " at " + showTime(now)
13869c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        + ", #events=" + mEventQueue.size());
13879c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                printQueue();
13889c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
13899c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
13909c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
13919c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        /**
13929c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan         * Cancels all the timer events with the specified callback.
13939c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan         *
13949c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan         * @param callback the callback
13959c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan         */
13969c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public synchronized void cancel(Runnable callback) {
13979c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (stopped() || mEventQueue.isEmpty()) return;
13989c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (DEBUG_TIMER) Log.d(TAG, "cancel:" + callback);
13999c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
14009c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            MyEvent firstEvent = mEventQueue.first();
14019c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            for (Iterator<MyEvent> iter = mEventQueue.iterator();
14029c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    iter.hasNext();) {
14039c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                MyEvent event = iter.next();
14049c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                if (event.mCallback == callback) {
14059c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    iter.remove();
14069c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    if (DEBUG_TIMER) Log.d(TAG, "    cancel found:" + event);
14079c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                }
14089c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
14099c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (mEventQueue.isEmpty()) {
14109c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                cancelAlarm();
14119c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            } else if (mEventQueue.first() != firstEvent) {
14129c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                cancelAlarm();
14139c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                firstEvent = mEventQueue.first();
14149c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                firstEvent.mPeriod = firstEvent.mMaxPeriod;
14159c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                firstEvent.mTriggerTime = firstEvent.mLastTriggerTime
14169c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        + firstEvent.mPeriod;
14179c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                recalculatePeriods();
14189c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                scheduleNext();
14199c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
14209c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (DEBUG_TIMER) {
14219c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                Log.d(TAG, "after cancel:");
14229c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                printQueue();
14239c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
14249c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
14259c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
14269c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private void scheduleNext() {
14279c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (stopped() || mEventQueue.isEmpty()) return;
14289c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
14299c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (mPendingIntent != null) {
14309c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                throw new RuntimeException("pendingIntent is not null!");
14319c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
14329c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
14339c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            MyEvent event = mEventQueue.first();
14349c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            Intent intent = new Intent(getAction());
14359c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            intent.putExtra(TRIGGER_TIME, event.mTriggerTime);
14369c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            PendingIntent pendingIntent = mPendingIntent =
14379c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    PendingIntent.getBroadcast(mContext, 0, intent,
14389c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            PendingIntent.FLAG_UPDATE_CURRENT);
14399c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
14409c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    event.mTriggerTime, pendingIntent);
14419c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
14429c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
14439c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        @Override
1444257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan        public void onReceive(Context context, Intent intent) {
1445257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan            // This callback is already protected by AlarmManager's wake lock.
14469c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            String action = intent.getAction();
14479c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (getAction().equals(action)
14489c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    && intent.getExtras().containsKey(TRIGGER_TIME)) {
14499c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mPendingIntent = null;
14509c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                long triggerTime = intent.getLongExtra(TRIGGER_TIME, -1L);
14519c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                execute(triggerTime);
14529c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            } else {
14539c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                Log.d(TAG, "unrecognized intent: " + intent);
14549c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
14559c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
14569c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
14579c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private void printQueue() {
14589c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            int count = 0;
14599c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            for (MyEvent event : mEventQueue) {
14609c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                Log.d(TAG, "     " + event + ": scheduled at "
14619c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        + showTime(event.mTriggerTime) + ": last at "
14629c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        + showTime(event.mLastTriggerTime));
14639c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                if (++count >= 5) break;
14649c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
14659c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (mEventQueue.size() > count) {
14669c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                Log.d(TAG, "     .....");
14679c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            } else if (count == 0) {
14689c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                Log.d(TAG, "     <empty>");
14699c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
14709c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
14719c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
1472257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan        private synchronized void execute(long triggerTime) {
14739c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (DEBUG_TIMER) Log.d(TAG, "time's up, triggerTime = "
14749c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    + showTime(triggerTime) + ": " + mEventQueue.size());
14759c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (stopped() || mEventQueue.isEmpty()) return;
14769c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
14779c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            for (MyEvent event : mEventQueue) {
14789c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                if (event.mTriggerTime != triggerTime) break;
14799c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                if (DEBUG_TIMER) Log.d(TAG, "execute " + event);
14809c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
14819c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                event.mLastTriggerTime = event.mTriggerTime;
14829c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                event.mTriggerTime += event.mPeriod;
14839c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
1484257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                // run the callback in the handler thread to prevent deadlock
1485257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                getExecutor().execute(event.mCallback);
14869c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
14879c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (DEBUG_TIMER) {
14889c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                Log.d(TAG, "after timeout execution");
14899c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                printQueue();
14909c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
14919c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            scheduleNext();
14929c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
14939c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
14949c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private String getAction() {
14959c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return toString();
14969c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
14979c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
14989c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private String showTime(long time) {
14999c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            int ms = (int) (time % 1000);
15009c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            int s = (int) (time / 1000);
15019c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            int m = s / 60;
15029c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            s %= 60;
15039c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return String.format("%d.%d.%d", m, s, ms);
15049c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
15059c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
15069c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
15079c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private static class MyEvent {
15089c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        int mPeriod;
15099c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        int mMaxPeriod;
15109c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        long mTriggerTime;
15119c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        long mLastTriggerTime;
15129c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        Runnable mCallback;
15139c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
15149c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        MyEvent(int period, Runnable callback, long now) {
15159c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mPeriod = mMaxPeriod = period;
15169c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mCallback = callback;
15179c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mLastTriggerTime = now;
15189c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
15199c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
15209c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        @Override
15219c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public String toString() {
15229c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            String s = super.toString();
15239c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            s = s.substring(s.indexOf("@"));
15249c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return s + ":" + (mPeriod / 1000) + ":" + (mMaxPeriod / 1000) + ":"
15259c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    + toString(mCallback);
15269c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
15279c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
15289c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private String toString(Object o) {
15299c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            String s = o.toString();
15309c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            int index = s.indexOf("$");
15319c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (index > 0) s = s.substring(index + 1);
15329c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return s;
15339c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
15349c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
15359c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
15369c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private static class MyEventComparator implements Comparator<MyEvent> {
15379c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public int compare(MyEvent e1, MyEvent e2) {
15389c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (e1 == e2) return 0;
15399c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            int diff = e1.mMaxPeriod - e2.mMaxPeriod;
15409c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (diff == 0) diff = -1;
15419c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return diff;
15429c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
15439c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
15449c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public boolean equals(Object that) {
15459c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return (this == that);
15469c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
15479c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
15489c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
1549257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan    private static Looper createLooper() {
1550257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan        HandlerThread thread = new HandlerThread("SipService.Executor");
1551257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan        thread.start();
1552257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan        return thread.getLooper();
1553257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan    }
1554257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan
1555257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan    // Executes immediate tasks in a single thread.
1556257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan    // Hold/release wake lock for running tasks
1557257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan    private class MyExecutor extends Handler {
15589c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        MyExecutor() {
15599c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            super(createLooper());
15609c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
15619c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
1562257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan        void execute(Runnable task) {
1563257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan            mMyWakeLock.acquire(task);
15649c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            Message.obtain(this, 0/* don't care */, task).sendToTarget();
15659c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
15669c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
15679c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        @Override
15689c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public void handleMessage(Message msg) {
15699c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (msg.obj instanceof Runnable) {
1570257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                executeInternal((Runnable) msg.obj);
15719c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            } else {
15729c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                Log.w(TAG, "can't handle msg: " + msg);
15739c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
15749c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
1575257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan
1576257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan        private void executeInternal(Runnable task) {
1577257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan            try {
1578257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                task.run();
1579257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan            } catch (Throwable t) {
1580257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                Log.e(TAG, "run task: " + task, t);
1581257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan            } finally {
1582257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                mMyWakeLock.release(task);
1583257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan            }
1584257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan        }
1585257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan    }
15869c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan}
1587