SipService.java revision 44ccfb03bb349a90546ddb3dc0063cbf4aaddaf1
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;
634cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyanimport java.util.concurrent.Executor;
649c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport javax.sip.SipException;
659c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
669c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan/**
679c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan * @hide
689c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan */
699c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanpublic final class SipService extends ISipService.Stub {
70acedadc1967457ac2f8981c67f884fd8c0ee853cHung-ying Tyan    static final String TAG = "SipService";
71acedadc1967457ac2f8981c67f884fd8c0ee853cHung-ying Tyan    static final boolean DEBUGV = false;
728a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan    static final boolean DEBUG = true;
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;
768a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan    private static final int DEFAULT_KEEPALIVE_INTERVAL = 10; // in seconds
779c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
789c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private Context mContext;
799c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private String mLocalIp;
809c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private String mNetworkType;
819c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private boolean mConnected;
824cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan    private SipWakeupTimer mTimer;
8387e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang    private WifiScanProcess mWifiScanProcess;
849c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private WifiManager.WifiLock mWifiLock;
859c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private boolean mWifiOnly;
86469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang    private IntervalMeasurementProcess mIntervalMeasurementProcess;
879c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
884cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan    private MyExecutor mExecutor = new MyExecutor();
899c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
909c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    // SipProfile URI --> group
919c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private Map<String, SipSessionGroupExt> mSipGroups =
929c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            new HashMap<String, SipSessionGroupExt>();
939c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
949c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    // session ID --> session
959c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private Map<String, ISipSession> mPendingSessions =
969c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            new HashMap<String, ISipSession>();
979c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
989c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private ConnectivityReceiver mConnectivityReceiver;
999abf73f1bde7b1617603c888650dbda9d6a7fddfHung-ying Tyan    private boolean mWifiEnabled;
100acedadc1967457ac2f8981c67f884fd8c0ee853cHung-ying Tyan    private SipWakeLock mMyWakeLock;
101469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang    private int mKeepAliveInterval;
1029c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
1039c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    /**
1049c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan     * Starts the SIP service. Do nothing if the SIP API is not supported on the
1059c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan     * device.
1069c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan     */
1079c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    public static void start(Context context) {
1089c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        if (SipManager.isApiSupported(context)) {
1099c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            ServiceManager.addService("sip", new SipService(context));
11022523a59d879cf47f1e9c202d001d569fad4f69eHung-ying Tyan            context.sendBroadcast(new Intent(SipManager.ACTION_SIP_SERVICE_UP));
111dc7545495e1ef8bad32cd78775f4dc072015eea0Hung-ying Tyan            if (DEBUG) Log.d(TAG, "SIP service started");
1129c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
1139c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
1149c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
1159c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private SipService(Context context) {
1169c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        if (DEBUG) Log.d(TAG, " service started!");
1179c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        mContext = context;
1189c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        mConnectivityReceiver = new ConnectivityReceiver();
119acedadc1967457ac2f8981c67f884fd8c0ee853cHung-ying Tyan        mMyWakeLock = new SipWakeLock((PowerManager)
120257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                context.getSystemService(Context.POWER_SERVICE));
1219c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
1224cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        mTimer = new SipWakeupTimer(context, mExecutor);
1239c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        mWifiOnly = SipManager.isSipWifiOnly(context);
1249c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
1259c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
126dc7545495e1ef8bad32cd78775f4dc072015eea0Hung-ying Tyan    private BroadcastReceiver mWifiStateReceiver = new BroadcastReceiver() {
127f452fd81f1a308428b0bfbae883fddb9caced878Chung-yih Wang        @Override
128f452fd81f1a308428b0bfbae883fddb9caced878Chung-yih Wang        public void onReceive(Context context, Intent intent) {
129f452fd81f1a308428b0bfbae883fddb9caced878Chung-yih Wang            String action = intent.getAction();
1309abf73f1bde7b1617603c888650dbda9d6a7fddfHung-ying Tyan            if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) {
1319abf73f1bde7b1617603c888650dbda9d6a7fddfHung-ying Tyan                int state = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
1329abf73f1bde7b1617603c888650dbda9d6a7fddfHung-ying Tyan                        WifiManager.WIFI_STATE_UNKNOWN);
1339abf73f1bde7b1617603c888650dbda9d6a7fddfHung-ying Tyan                synchronized (SipService.this) {
1349abf73f1bde7b1617603c888650dbda9d6a7fddfHung-ying Tyan                    switch (state) {
1359abf73f1bde7b1617603c888650dbda9d6a7fddfHung-ying Tyan                        case WifiManager.WIFI_STATE_ENABLED:
1369abf73f1bde7b1617603c888650dbda9d6a7fddfHung-ying Tyan                            mWifiEnabled = true;
137617479363dcafe01bfa1fa025e1d9d122864a0ceHung-ying Tyan                            if (anyOpenedToReceiveCalls()) grabWifiLock();
1389abf73f1bde7b1617603c888650dbda9d6a7fddfHung-ying Tyan                            break;
1399abf73f1bde7b1617603c888650dbda9d6a7fddfHung-ying Tyan                        case WifiManager.WIFI_STATE_DISABLED:
1409abf73f1bde7b1617603c888650dbda9d6a7fddfHung-ying Tyan                            mWifiEnabled = false;
1419abf73f1bde7b1617603c888650dbda9d6a7fddfHung-ying Tyan                            releaseWifiLock();
1429abf73f1bde7b1617603c888650dbda9d6a7fddfHung-ying Tyan                            break;
1439abf73f1bde7b1617603c888650dbda9d6a7fddfHung-ying Tyan                    }
1449abf73f1bde7b1617603c888650dbda9d6a7fddfHung-ying Tyan                }
145f452fd81f1a308428b0bfbae883fddb9caced878Chung-yih Wang            }
146f452fd81f1a308428b0bfbae883fddb9caced878Chung-yih Wang        }
147f452fd81f1a308428b0bfbae883fddb9caced878Chung-yih Wang    };
148f452fd81f1a308428b0bfbae883fddb9caced878Chung-yih Wang
149dc7545495e1ef8bad32cd78775f4dc072015eea0Hung-ying Tyan    private void registerReceivers() {
150dc7545495e1ef8bad32cd78775f4dc072015eea0Hung-ying Tyan        mContext.registerReceiver(mConnectivityReceiver,
151dc7545495e1ef8bad32cd78775f4dc072015eea0Hung-ying Tyan                new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
152dc7545495e1ef8bad32cd78775f4dc072015eea0Hung-ying Tyan        mContext.registerReceiver(mWifiStateReceiver,
153dc7545495e1ef8bad32cd78775f4dc072015eea0Hung-ying Tyan                new IntentFilter(WifiManager.WIFI_STATE_CHANGED_ACTION));
154dc7545495e1ef8bad32cd78775f4dc072015eea0Hung-ying Tyan        if (DEBUG) Log.d(TAG, " +++ register receivers");
155dc7545495e1ef8bad32cd78775f4dc072015eea0Hung-ying Tyan    }
156dc7545495e1ef8bad32cd78775f4dc072015eea0Hung-ying Tyan
157dc7545495e1ef8bad32cd78775f4dc072015eea0Hung-ying Tyan    private void unregisterReceivers() {
158dc7545495e1ef8bad32cd78775f4dc072015eea0Hung-ying Tyan        mContext.unregisterReceiver(mConnectivityReceiver);
159dc7545495e1ef8bad32cd78775f4dc072015eea0Hung-ying Tyan        mContext.unregisterReceiver(mWifiStateReceiver);
160dc7545495e1ef8bad32cd78775f4dc072015eea0Hung-ying Tyan        if (DEBUG) Log.d(TAG, " --- unregister receivers");
161dc7545495e1ef8bad32cd78775f4dc072015eea0Hung-ying Tyan    }
162dc7545495e1ef8bad32cd78775f4dc072015eea0Hung-ying Tyan
1639c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    public synchronized SipProfile[] getListOfProfiles() {
1648127b35b75c13f853dc8eb2fed8bba4bf99e3eedHung-ying Tyan        mContext.enforceCallingOrSelfPermission(
1658127b35b75c13f853dc8eb2fed8bba4bf99e3eedHung-ying Tyan                android.Manifest.permission.USE_SIP, null);
166a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        boolean isCallerRadio = isCallerRadio();
167a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        ArrayList<SipProfile> profiles = new ArrayList<SipProfile>();
1689c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        for (SipSessionGroupExt group : mSipGroups.values()) {
169a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan            if (isCallerRadio || isCallerCreator(group)) {
170a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan                profiles.add(group.getLocalProfile());
171a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan            }
1729c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
173a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        return profiles.toArray(new SipProfile[profiles.size()]);
1749c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
1759c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
176dc7545495e1ef8bad32cd78775f4dc072015eea0Hung-ying Tyan    public synchronized void open(SipProfile localProfile) {
1778127b35b75c13f853dc8eb2fed8bba4bf99e3eedHung-ying Tyan        mContext.enforceCallingOrSelfPermission(
1788127b35b75c13f853dc8eb2fed8bba4bf99e3eedHung-ying Tyan                android.Manifest.permission.USE_SIP, null);
1799c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        localProfile.setCallingUid(Binder.getCallingUid());
1809c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        try {
181dc7545495e1ef8bad32cd78775f4dc072015eea0Hung-ying Tyan            boolean addingFirstProfile = mSipGroups.isEmpty();
1829c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            createGroup(localProfile);
183dc7545495e1ef8bad32cd78775f4dc072015eea0Hung-ying Tyan            if (addingFirstProfile && !mSipGroups.isEmpty()) registerReceivers();
1849c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        } catch (SipException e) {
1859c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            Log.e(TAG, "openToMakeCalls()", e);
1869c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            // TODO: how to send the exception back
1879c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
1889c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
1899c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
1909c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    public synchronized void open3(SipProfile localProfile,
191845f7332f04864c5483b3e63da5db076fc7a888aHung-ying Tyan            PendingIntent incomingCallPendingIntent,
192845f7332f04864c5483b3e63da5db076fc7a888aHung-ying Tyan            ISipSessionListener listener) {
1938127b35b75c13f853dc8eb2fed8bba4bf99e3eedHung-ying Tyan        mContext.enforceCallingOrSelfPermission(
1948127b35b75c13f853dc8eb2fed8bba4bf99e3eedHung-ying Tyan                android.Manifest.permission.USE_SIP, null);
1959c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        localProfile.setCallingUid(Binder.getCallingUid());
196845f7332f04864c5483b3e63da5db076fc7a888aHung-ying Tyan        if (incomingCallPendingIntent == null) {
197845f7332f04864c5483b3e63da5db076fc7a888aHung-ying Tyan            Log.w(TAG, "incomingCallPendingIntent cannot be null; "
198845f7332f04864c5483b3e63da5db076fc7a888aHung-ying Tyan                    + "the profile is not opened");
199a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan            return;
2009c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
2019c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        if (DEBUG) Log.d(TAG, "open3: " + localProfile.getUriString() + ": "
202845f7332f04864c5483b3e63da5db076fc7a888aHung-ying Tyan                + incomingCallPendingIntent + ": " + listener);
2039c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        try {
204dc7545495e1ef8bad32cd78775f4dc072015eea0Hung-ying Tyan            boolean addingFirstProfile = mSipGroups.isEmpty();
2059c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            SipSessionGroupExt group = createGroup(localProfile,
206845f7332f04864c5483b3e63da5db076fc7a888aHung-ying Tyan                    incomingCallPendingIntent, listener);
207dc7545495e1ef8bad32cd78775f4dc072015eea0Hung-ying Tyan            if (addingFirstProfile && !mSipGroups.isEmpty()) registerReceivers();
2089c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (localProfile.getAutoRegistration()) {
2099c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                group.openToReceiveCalls();
2109abf73f1bde7b1617603c888650dbda9d6a7fddfHung-ying Tyan                if (mWifiEnabled) grabWifiLock();
2119c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
2129c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        } catch (SipException e) {
2139c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            Log.e(TAG, "openToReceiveCalls()", e);
2149c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            // TODO: how to send the exception back
2159c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
2169c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
2179c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
218a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan    private boolean isCallerCreator(SipSessionGroupExt group) {
219a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        SipProfile profile = group.getLocalProfile();
220a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        return (profile.getCallingUid() == Binder.getCallingUid());
221a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan    }
222a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan
223a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan    private boolean isCallerCreatorOrRadio(SipSessionGroupExt group) {
224a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        return (isCallerRadio() || isCallerCreator(group));
225a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan    }
226a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan
227a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan    private boolean isCallerRadio() {
228a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        return (Binder.getCallingUid() == Process.PHONE_UID);
229a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan    }
230a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan
2319c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    public synchronized void close(String localProfileUri) {
2328127b35b75c13f853dc8eb2fed8bba4bf99e3eedHung-ying Tyan        mContext.enforceCallingOrSelfPermission(
2338127b35b75c13f853dc8eb2fed8bba4bf99e3eedHung-ying Tyan                android.Manifest.permission.USE_SIP, null);
234a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        SipSessionGroupExt group = mSipGroups.get(localProfileUri);
235a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        if (group == null) return;
236a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        if (!isCallerCreatorOrRadio(group)) {
2376d9d99615a30de0675271553552c3c7b49311354Joe Onorato            Log.w(TAG, "only creator or radio can close this profile");
238a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan            return;
2399c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
240a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan
241a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        group = mSipGroups.remove(localProfileUri);
242a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        notifyProfileRemoved(group.getLocalProfile());
243a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        group.close();
244257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan
245617479363dcafe01bfa1fa025e1d9d122864a0ceHung-ying Tyan        if (!anyOpenedToReceiveCalls()) {
246257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan            releaseWifiLock();
247257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan            mMyWakeLock.reset(); // in case there's leak
248257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan        }
249dc7545495e1ef8bad32cd78775f4dc072015eea0Hung-ying Tyan        if (mSipGroups.isEmpty()) unregisterReceivers();
2509c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
2519c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
2529c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    public synchronized boolean isOpened(String localProfileUri) {
2538127b35b75c13f853dc8eb2fed8bba4bf99e3eedHung-ying Tyan        mContext.enforceCallingOrSelfPermission(
2548127b35b75c13f853dc8eb2fed8bba4bf99e3eedHung-ying Tyan                android.Manifest.permission.USE_SIP, null);
2559c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        SipSessionGroupExt group = mSipGroups.get(localProfileUri);
256a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        if (group == null) return false;
257a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        if (isCallerCreatorOrRadio(group)) {
258617479363dcafe01bfa1fa025e1d9d122864a0ceHung-ying Tyan            return true;
259a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        } else {
2606d9d99615a30de0675271553552c3c7b49311354Joe Onorato            Log.w(TAG, "only creator or radio can query on the profile");
261a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan            return false;
262a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        }
2639c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
2649c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
2659c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    public synchronized boolean isRegistered(String localProfileUri) {
2668127b35b75c13f853dc8eb2fed8bba4bf99e3eedHung-ying Tyan        mContext.enforceCallingOrSelfPermission(
2678127b35b75c13f853dc8eb2fed8bba4bf99e3eedHung-ying Tyan                android.Manifest.permission.USE_SIP, null);
2689c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        SipSessionGroupExt group = mSipGroups.get(localProfileUri);
269a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        if (group == null) return false;
270a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        if (isCallerCreatorOrRadio(group)) {
271a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan            return group.isRegistered();
272a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        } else {
2736d9d99615a30de0675271553552c3c7b49311354Joe Onorato            Log.w(TAG, "only creator or radio can query on the profile");
274a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan            return false;
275a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        }
2769c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
2779c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
2789c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    public synchronized void setRegistrationListener(String localProfileUri,
2799c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            ISipSessionListener listener) {
2808127b35b75c13f853dc8eb2fed8bba4bf99e3eedHung-ying Tyan        mContext.enforceCallingOrSelfPermission(
2818127b35b75c13f853dc8eb2fed8bba4bf99e3eedHung-ying Tyan                android.Manifest.permission.USE_SIP, null);
2829c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        SipSessionGroupExt group = mSipGroups.get(localProfileUri);
283a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        if (group == null) return;
284a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        if (isCallerCreator(group)) {
285a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan            group.setListener(listener);
286a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        } else {
2876d9d99615a30de0675271553552c3c7b49311354Joe Onorato            Log.w(TAG, "only creator can set listener on the profile");
288a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        }
2899c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
2909c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
2919c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    public synchronized ISipSession createSession(SipProfile localProfile,
2929c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            ISipSessionListener listener) {
2938127b35b75c13f853dc8eb2fed8bba4bf99e3eedHung-ying Tyan        mContext.enforceCallingOrSelfPermission(
2948127b35b75c13f853dc8eb2fed8bba4bf99e3eedHung-ying Tyan                android.Manifest.permission.USE_SIP, null);
2959c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        localProfile.setCallingUid(Binder.getCallingUid());
2969c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        if (!mConnected) return null;
2979c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        try {
2989c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            SipSessionGroupExt group = createGroup(localProfile);
2999c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return group.createSession(listener);
3009c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        } catch (SipException e) {
3016d9d99615a30de0675271553552c3c7b49311354Joe Onorato            if (DEBUG) Log.d(TAG, "createSession()", e);
3029c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return null;
3039c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
3049c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
3059c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
3069c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    public synchronized ISipSession getPendingSession(String callId) {
3078127b35b75c13f853dc8eb2fed8bba4bf99e3eedHung-ying Tyan        mContext.enforceCallingOrSelfPermission(
3088127b35b75c13f853dc8eb2fed8bba4bf99e3eedHung-ying Tyan                android.Manifest.permission.USE_SIP, null);
3099c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        if (callId == null) return null;
3109c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        return mPendingSessions.get(callId);
3119c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
3129c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
3139c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private String determineLocalIp() {
3149c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        try {
3159c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            DatagramSocket s = new DatagramSocket();
3169c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            s.connect(InetAddress.getByName("192.168.1.1"), 80);
3179c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return s.getLocalAddress().getHostAddress();
3189c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        } catch (IOException e) {
3196d9d99615a30de0675271553552c3c7b49311354Joe Onorato            if (DEBUG) Log.d(TAG, "determineLocalIp()", e);
3209c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            // dont do anything; there should be a connectivity change going
3219c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return null;
3229c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
3239c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
3249c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
3259c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private SipSessionGroupExt createGroup(SipProfile localProfile)
3269c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            throws SipException {
3279c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        String key = localProfile.getUriString();
3289c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        SipSessionGroupExt group = mSipGroups.get(key);
3299c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        if (group == null) {
3309c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            group = new SipSessionGroupExt(localProfile, null, null);
3319c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mSipGroups.put(key, group);
3329c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            notifyProfileAdded(localProfile);
333a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        } else if (!isCallerCreator(group)) {
334a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan            throw new SipException("only creator can access the profile");
3359c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
3369c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        return group;
3379c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
3389c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
3399c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private SipSessionGroupExt createGroup(SipProfile localProfile,
340845f7332f04864c5483b3e63da5db076fc7a888aHung-ying Tyan            PendingIntent incomingCallPendingIntent,
341845f7332f04864c5483b3e63da5db076fc7a888aHung-ying Tyan            ISipSessionListener listener) throws SipException {
3429c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        String key = localProfile.getUriString();
3439c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        SipSessionGroupExt group = mSipGroups.get(key);
3449c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        if (group != null) {
345a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan            if (!isCallerCreator(group)) {
346a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan                throw new SipException("only creator can access the profile");
347a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan            }
348845f7332f04864c5483b3e63da5db076fc7a888aHung-ying Tyan            group.setIncomingCallPendingIntent(incomingCallPendingIntent);
3499c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            group.setListener(listener);
3509c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        } else {
3519c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            group = new SipSessionGroupExt(localProfile,
352845f7332f04864c5483b3e63da5db076fc7a888aHung-ying Tyan                    incomingCallPendingIntent, listener);
3539c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mSipGroups.put(key, group);
3549c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            notifyProfileAdded(localProfile);
3559c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
3569c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        return group;
3579c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
3589c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
3599c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private void notifyProfileAdded(SipProfile localProfile) {
3609c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        if (DEBUG) Log.d(TAG, "notify: profile added: " + localProfile);
3619c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        Intent intent = new Intent(SipManager.ACTION_SIP_ADD_PHONE);
3629c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        intent.putExtra(SipManager.EXTRA_LOCAL_URI, localProfile.getUriString());
3639c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        mContext.sendBroadcast(intent);
3649c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
3659c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
3669c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private void notifyProfileRemoved(SipProfile localProfile) {
3679c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        if (DEBUG) Log.d(TAG, "notify: profile removed: " + localProfile);
3689c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        Intent intent = new Intent(SipManager.ACTION_SIP_REMOVE_PHONE);
3699c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        intent.putExtra(SipManager.EXTRA_LOCAL_URI, localProfile.getUriString());
3709c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        mContext.sendBroadcast(intent);
3719c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
3729c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
373617479363dcafe01bfa1fa025e1d9d122864a0ceHung-ying Tyan    private boolean anyOpenedToReceiveCalls() {
3749c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        for (SipSessionGroupExt group : mSipGroups.values()) {
375617479363dcafe01bfa1fa025e1d9d122864a0ceHung-ying Tyan            if (group.isOpenedToReceiveCalls()) return true;
3769c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
3779c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        return false;
3789c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
3799c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
3809c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private void grabWifiLock() {
3819c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        if (mWifiLock == null) {
3828a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan            if (DEBUG) Log.d(TAG, "acquire wifi lock");
3839c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mWifiLock = ((WifiManager)
3849c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    mContext.getSystemService(Context.WIFI_SERVICE))
3859c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    .createWifiLock(WifiManager.WIFI_MODE_FULL, TAG);
3869c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mWifiLock.acquire();
38787e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang            if (!mConnected) startWifiScanner();
3889c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
3899c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
3909c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
3919c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private void releaseWifiLock() {
3929c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        if (mWifiLock != null) {
3938a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan            if (DEBUG) Log.d(TAG, "release wifi lock");
3949c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mWifiLock.release();
3959c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mWifiLock = null;
39687e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang            stopWifiScanner();
39787e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang        }
39887e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang    }
39987e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang
40087e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang    private synchronized void startWifiScanner() {
40187e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang        if (mWifiScanProcess == null) {
40287e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang            mWifiScanProcess = new WifiScanProcess();
40387e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang        }
40487e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang        mWifiScanProcess.start();
40587e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang    }
40687e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang
40787e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang    private synchronized void stopWifiScanner() {
40887e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang        if (mWifiScanProcess != null) {
40987e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang            mWifiScanProcess.stop();
4109c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
4119c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
4129c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
4139c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private synchronized void onConnectivityChanged(
4149c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            String type, boolean connected) {
4159c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        if (DEBUG) Log.d(TAG, "onConnectivityChanged(): "
4169c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                + mNetworkType + (mConnected? " CONNECTED" : " DISCONNECTED")
4179c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                + " --> " + type + (connected? " CONNECTED" : " DISCONNECTED"));
4189c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
4199c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        boolean sameType = type.equals(mNetworkType);
4209c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        if (!sameType && !connected) return;
4219c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
4229c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        boolean wasWifi = "WIFI".equalsIgnoreCase(mNetworkType);
4239c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        boolean isWifi = "WIFI".equalsIgnoreCase(type);
4249c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        boolean wifiOff = (isWifi && !connected) || (wasWifi && !sameType);
4259c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        boolean wifiOn = isWifi && connected;
4269c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
4279c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        try {
4289c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            boolean wasConnected = mConnected;
4299c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mNetworkType = type;
4309c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mConnected = connected;
4319c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
4329c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (wasConnected) {
4339c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mLocalIp = null;
4349c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                for (SipSessionGroupExt group : mSipGroups.values()) {
4359c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    group.onConnectivityChanged(false);
4369c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                }
4379c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
4389c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
4399c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (connected) {
4409c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mLocalIp = determineLocalIp();
441469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang                mKeepAliveInterval = -1;
4429c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                for (SipSessionGroupExt group : mSipGroups.values()) {
4439c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    group.onConnectivityChanged(true);
4449c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                }
44587e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang                if (isWifi && (mWifiLock != null)) stopWifiScanner();
446257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan            } else {
447257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                mMyWakeLock.reset(); // in case there's a leak
448469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang                stopPortMappingMeasurement();
44987e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang                if (isWifi && (mWifiLock != null)) startWifiScanner();
4509c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
4519c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        } catch (SipException e) {
4529c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            Log.e(TAG, "onConnectivityChanged()", e);
4539c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
4549c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
4559c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
456469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang    private void stopPortMappingMeasurement() {
457469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang        if (mIntervalMeasurementProcess != null) {
458469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang            mIntervalMeasurementProcess.stop();
459469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang            mIntervalMeasurementProcess = null;
460469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang        }
461469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang    }
462469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang
4638a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan    private void startPortMappingLifetimeMeasurement(
4648a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan            SipProfile localProfile) {
46544ccfb03bb349a90546ddb3dc0063cbf4aaddaf1Hung-ying Tyan        startPortMappingLifetimeMeasurement(localProfile, -1);
46644ccfb03bb349a90546ddb3dc0063cbf4aaddaf1Hung-ying Tyan    }
46744ccfb03bb349a90546ddb3dc0063cbf4aaddaf1Hung-ying Tyan
46844ccfb03bb349a90546ddb3dc0063cbf4aaddaf1Hung-ying Tyan    private void startPortMappingLifetimeMeasurement(
46944ccfb03bb349a90546ddb3dc0063cbf4aaddaf1Hung-ying Tyan            SipProfile localProfile, int maxInterval) {
4708a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        if ((mIntervalMeasurementProcess == null)
4718a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                && (mKeepAliveInterval == -1)
4728a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                && isBehindNAT(mLocalIp)) {
4738a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan            Log.d(TAG, "start NAT port mapping timeout measurement on "
4748a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                    + localProfile.getUriString());
4758a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan
47644ccfb03bb349a90546ddb3dc0063cbf4aaddaf1Hung-ying Tyan            mIntervalMeasurementProcess =
47744ccfb03bb349a90546ddb3dc0063cbf4aaddaf1Hung-ying Tyan                    new IntervalMeasurementProcess(localProfile, maxInterval);
4788a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan            mIntervalMeasurementProcess.start();
4798a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        }
480469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang    }
481469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang
48244ccfb03bb349a90546ddb3dc0063cbf4aaddaf1Hung-ying Tyan    private void restartPortMappingLifetimeMeasurement(
48344ccfb03bb349a90546ddb3dc0063cbf4aaddaf1Hung-ying Tyan            SipProfile localProfile, int maxInterval) {
48444ccfb03bb349a90546ddb3dc0063cbf4aaddaf1Hung-ying Tyan        stopPortMappingMeasurement();
48544ccfb03bb349a90546ddb3dc0063cbf4aaddaf1Hung-ying Tyan        mKeepAliveInterval = -1;
48644ccfb03bb349a90546ddb3dc0063cbf4aaddaf1Hung-ying Tyan        startPortMappingLifetimeMeasurement(localProfile, maxInterval);
48744ccfb03bb349a90546ddb3dc0063cbf4aaddaf1Hung-ying Tyan    }
48844ccfb03bb349a90546ddb3dc0063cbf4aaddaf1Hung-ying Tyan
4899c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private synchronized void addPendingSession(ISipSession session) {
4909c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        try {
4913f4d44657ccad3ed02ed0e1354387b14b07dc011Hung-ying Tyan            cleanUpPendingSessions();
4929c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mPendingSessions.put(session.getCallId(), session);
4933f4d44657ccad3ed02ed0e1354387b14b07dc011Hung-ying Tyan            if (DEBUG) Log.d(TAG, "#pending sess=" + mPendingSessions.size());
4949c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        } catch (RemoteException e) {
4959c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            // should not happen with a local call
4969c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            Log.e(TAG, "addPendingSession()", e);
4979c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
4989c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
4999c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
5003f4d44657ccad3ed02ed0e1354387b14b07dc011Hung-ying Tyan    private void cleanUpPendingSessions() throws RemoteException {
5013f4d44657ccad3ed02ed0e1354387b14b07dc011Hung-ying Tyan        Map.Entry<String, ISipSession>[] entries =
5023f4d44657ccad3ed02ed0e1354387b14b07dc011Hung-ying Tyan                mPendingSessions.entrySet().toArray(
5033f4d44657ccad3ed02ed0e1354387b14b07dc011Hung-ying Tyan                new Map.Entry[mPendingSessions.size()]);
5043f4d44657ccad3ed02ed0e1354387b14b07dc011Hung-ying Tyan        for (Map.Entry<String, ISipSession> entry : entries) {
5053f4d44657ccad3ed02ed0e1354387b14b07dc011Hung-ying Tyan            if (entry.getValue().getState() != SipSession.State.INCOMING_CALL) {
5063f4d44657ccad3ed02ed0e1354387b14b07dc011Hung-ying Tyan                mPendingSessions.remove(entry.getKey());
5073f4d44657ccad3ed02ed0e1354387b14b07dc011Hung-ying Tyan            }
5083f4d44657ccad3ed02ed0e1354387b14b07dc011Hung-ying Tyan        }
5093f4d44657ccad3ed02ed0e1354387b14b07dc011Hung-ying Tyan    }
5103f4d44657ccad3ed02ed0e1354387b14b07dc011Hung-ying Tyan
511ed3c0fbbd5457f43ff72cec31b8ab0bb3f7a0047Hung-ying Tyan    private synchronized boolean callingSelf(SipSessionGroupExt ringingGroup,
512ed3c0fbbd5457f43ff72cec31b8ab0bb3f7a0047Hung-ying Tyan            SipSessionGroup.SipSessionImpl ringingSession) {
513ed3c0fbbd5457f43ff72cec31b8ab0bb3f7a0047Hung-ying Tyan        String callId = ringingSession.getCallId();
514ed3c0fbbd5457f43ff72cec31b8ab0bb3f7a0047Hung-ying Tyan        for (SipSessionGroupExt group : mSipGroups.values()) {
515ed3c0fbbd5457f43ff72cec31b8ab0bb3f7a0047Hung-ying Tyan            if ((group != ringingGroup) && group.containsSession(callId)) {
516ed3c0fbbd5457f43ff72cec31b8ab0bb3f7a0047Hung-ying Tyan                if (DEBUG) Log.d(TAG, "call self: "
517ed3c0fbbd5457f43ff72cec31b8ab0bb3f7a0047Hung-ying Tyan                        + ringingSession.getLocalProfile().getUriString()
518ed3c0fbbd5457f43ff72cec31b8ab0bb3f7a0047Hung-ying Tyan                        + " -> " + group.getLocalProfile().getUriString());
519ed3c0fbbd5457f43ff72cec31b8ab0bb3f7a0047Hung-ying Tyan                return true;
520ed3c0fbbd5457f43ff72cec31b8ab0bb3f7a0047Hung-ying Tyan            }
521ed3c0fbbd5457f43ff72cec31b8ab0bb3f7a0047Hung-ying Tyan        }
522ed3c0fbbd5457f43ff72cec31b8ab0bb3f7a0047Hung-ying Tyan        return false;
523ed3c0fbbd5457f43ff72cec31b8ab0bb3f7a0047Hung-ying Tyan    }
524ed3c0fbbd5457f43ff72cec31b8ab0bb3f7a0047Hung-ying Tyan
5258a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan    private synchronized void onKeepAliveIntervalChanged() {
5268a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        for (SipSessionGroupExt group : mSipGroups.values()) {
5278a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan            group.onKeepAliveIntervalChanged();
5288a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        }
5298a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan    }
5308a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan
5318a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan    private int getKeepAliveInterval() {
5328a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        return (mKeepAliveInterval < 0)
5338a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                ? DEFAULT_KEEPALIVE_INTERVAL
5348a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                : mKeepAliveInterval;
5358a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan    }
5368a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan
5378a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan    private boolean isBehindNAT(String address) {
5388a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        try {
5398a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan            byte[] d = InetAddress.getByName(address).getAddress();
5408a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan            if ((d[0] == 10) ||
5418a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                    (((0x000000FF & ((int)d[0])) == 172) &&
5428a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                    ((0x000000F0 & ((int)d[1])) == 16)) ||
5438a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                    (((0x000000FF & ((int)d[0])) == 192) &&
5448a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                    ((0x000000FF & ((int)d[1])) == 168))) {
5458a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                return true;
5468a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan            }
5478a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        } catch (UnknownHostException e) {
5488a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan            Log.e(TAG, "isBehindAT()" + address, e);
5498a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        }
5508a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        return false;
5518a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan    }
552ed3c0fbbd5457f43ff72cec31b8ab0bb3f7a0047Hung-ying Tyan
5539c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private class SipSessionGroupExt extends SipSessionAdapter {
5549c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private SipSessionGroup mSipGroup;
555845f7332f04864c5483b3e63da5db076fc7a888aHung-ying Tyan        private PendingIntent mIncomingCallPendingIntent;
556617479363dcafe01bfa1fa025e1d9d122864a0ceHung-ying Tyan        private boolean mOpenedToReceiveCalls;
5579c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
5589c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private AutoRegistrationProcess mAutoRegistration =
5599c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                new AutoRegistrationProcess();
5609c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
5619c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public SipSessionGroupExt(SipProfile localProfile,
562845f7332f04864c5483b3e63da5db076fc7a888aHung-ying Tyan                PendingIntent incomingCallPendingIntent,
5639c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                ISipSessionListener listener) throws SipException {
5649c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            String password = localProfile.getPassword();
5659c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            SipProfile p = duplicate(localProfile);
5669c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mSipGroup = createSipSessionGroup(mLocalIp, p, password);
567845f7332f04864c5483b3e63da5db076fc7a888aHung-ying Tyan            mIncomingCallPendingIntent = incomingCallPendingIntent;
5689c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mAutoRegistration.setListener(listener);
5699c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
5709c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
5719c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public SipProfile getLocalProfile() {
5729c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return mSipGroup.getLocalProfile();
5739c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
5749c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
575ed3c0fbbd5457f43ff72cec31b8ab0bb3f7a0047Hung-ying Tyan        public boolean containsSession(String callId) {
576ed3c0fbbd5457f43ff72cec31b8ab0bb3f7a0047Hung-ying Tyan            return mSipGroup.containsSession(callId);
577ed3c0fbbd5457f43ff72cec31b8ab0bb3f7a0047Hung-ying Tyan        }
578ed3c0fbbd5457f43ff72cec31b8ab0bb3f7a0047Hung-ying Tyan
5798a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        public void onKeepAliveIntervalChanged() {
5808a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan            mAutoRegistration.onKeepAliveIntervalChanged();
5818a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        }
5828a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan
5838a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        // TODO: remove this method once SipWakeupTimer can better handle variety
5848a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        // of timeout values
5858a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        void setWakeupTimer(SipWakeupTimer timer) {
5868a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan            mSipGroup.setWakeupTimer(timer);
5878a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        }
5888a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan
5899c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        // network connectivity is tricky because network can be disconnected
5909c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        // at any instant so need to deal with exceptions carefully even when
5919c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        // you think you are connected
5929c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private SipSessionGroup createSipSessionGroup(String localIp,
5939c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                SipProfile localProfile, String password) throws SipException {
5949c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            try {
595acedadc1967457ac2f8981c67f884fd8c0ee853cHung-ying Tyan                return new SipSessionGroup(localIp, localProfile, password,
5968a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                        mTimer, mMyWakeLock);
5979c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            } catch (IOException e) {
5989c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                // network disconnected
5999c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                Log.w(TAG, "createSipSessionGroup(): network disconnected?");
6009c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                if (localIp != null) {
6019c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    return createSipSessionGroup(null, localProfile, password);
6029c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                } else {
6039c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    // recursive
6046d9d99615a30de0675271553552c3c7b49311354Joe Onorato                    Log.wtf(TAG, "impossible! recursive!");
6059c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    throw new RuntimeException("createSipSessionGroup");
6069c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                }
6079c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
6089c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
6099c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
6109c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private SipProfile duplicate(SipProfile p) {
6119c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            try {
6129c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                return new SipProfile.Builder(p).setPassword("*").build();
6139c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            } catch (Exception e) {
6149c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                Log.wtf(TAG, "duplicate()", e);
6159c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                throw new RuntimeException("duplicate profile", e);
6169c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
6179c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
6189c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
6199c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public void setListener(ISipSessionListener listener) {
6209c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mAutoRegistration.setListener(listener);
6219c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
6229c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
623845f7332f04864c5483b3e63da5db076fc7a888aHung-ying Tyan        public void setIncomingCallPendingIntent(PendingIntent pIntent) {
624845f7332f04864c5483b3e63da5db076fc7a888aHung-ying Tyan            mIncomingCallPendingIntent = pIntent;
6259c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
6269c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
6279c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public void openToReceiveCalls() throws SipException {
628617479363dcafe01bfa1fa025e1d9d122864a0ceHung-ying Tyan            mOpenedToReceiveCalls = true;
6299c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (mConnected) {
6309c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mSipGroup.openToReceiveCalls(this);
6319c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mAutoRegistration.start(mSipGroup);
6329c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
6339c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (DEBUG) Log.d(TAG, "  openToReceiveCalls: " + getUri() + ": "
634845f7332f04864c5483b3e63da5db076fc7a888aHung-ying Tyan                    + mIncomingCallPendingIntent);
6359c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
6369c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
6379c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public void onConnectivityChanged(boolean connected)
6389c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                throws SipException {
6399c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mSipGroup.onConnectivityChanged();
6409c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (connected) {
6419c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                resetGroup(mLocalIp);
642617479363dcafe01bfa1fa025e1d9d122864a0ceHung-ying Tyan                if (mOpenedToReceiveCalls) openToReceiveCalls();
6439c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            } else {
644617479363dcafe01bfa1fa025e1d9d122864a0ceHung-ying Tyan                // close mSipGroup but remember mOpenedToReceiveCalls
6459c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                if (DEBUG) Log.d(TAG, "  close auto reg temporarily: "
646845f7332f04864c5483b3e63da5db076fc7a888aHung-ying Tyan                        + getUri() + ": " + mIncomingCallPendingIntent);
6479c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mSipGroup.close();
6489c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mAutoRegistration.stop();
6499c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
6509c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
6519c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
6529c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private void resetGroup(String localIp) throws SipException {
6539c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            try {
6549c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mSipGroup.reset(localIp);
6559c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            } catch (IOException e) {
6569c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                // network disconnected
6579c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                Log.w(TAG, "resetGroup(): network disconnected?");
6589c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                if (localIp != null) {
6599c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    resetGroup(null); // reset w/o local IP
6609c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                } else {
6619c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    // recursive
6629c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    Log.wtf(TAG, "impossible!");
6639c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    throw new RuntimeException("resetGroup");
6649c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                }
6659c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
6669c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
6679c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
6689c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public void close() {
669617479363dcafe01bfa1fa025e1d9d122864a0ceHung-ying Tyan            mOpenedToReceiveCalls = false;
6709c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mSipGroup.close();
6719c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mAutoRegistration.stop();
6729c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (DEBUG) Log.d(TAG, "   close: " + getUri() + ": "
673845f7332f04864c5483b3e63da5db076fc7a888aHung-ying Tyan                    + mIncomingCallPendingIntent);
6749c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
6759c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
6769c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public ISipSession createSession(ISipSessionListener listener) {
6779c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return mSipGroup.createSession(listener);
6789c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
6799c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
6809c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        @Override
681845f7332f04864c5483b3e63da5db076fc7a888aHung-ying Tyan        public void onRinging(ISipSession s, SipProfile caller,
6829c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                String sessionDescription) {
683acedadc1967457ac2f8981c67f884fd8c0ee853cHung-ying Tyan            if (DEBUGV) Log.d(TAG, "<<<<< onRinging()");
684845f7332f04864c5483b3e63da5db076fc7a888aHung-ying Tyan            SipSessionGroup.SipSessionImpl session =
685845f7332f04864c5483b3e63da5db076fc7a888aHung-ying Tyan                    (SipSessionGroup.SipSessionImpl) s;
6869c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            synchronized (SipService.this) {
6879c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                try {
688ed3c0fbbd5457f43ff72cec31b8ab0bb3f7a0047Hung-ying Tyan                    if (!isRegistered() || callingSelf(this, session)) {
6899c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        session.endCall();
6909c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        return;
6919c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    }
6929c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
6939c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    // send out incoming call broadcast
6949c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    addPendingSession(session);
6959c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    Intent intent = SipManager.createIncomingCallBroadcast(
696845f7332f04864c5483b3e63da5db076fc7a888aHung-ying Tyan                            session.getCallId(), sessionDescription);
6979c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    if (DEBUG) Log.d(TAG, " ringing~~ " + getUri() + ": "
6989c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            + caller.getUri() + ": " + session.getCallId()
699845f7332f04864c5483b3e63da5db076fc7a888aHung-ying Tyan                            + " " + mIncomingCallPendingIntent);
700845f7332f04864c5483b3e63da5db076fc7a888aHung-ying Tyan                    mIncomingCallPendingIntent.send(mContext,
701845f7332f04864c5483b3e63da5db076fc7a888aHung-ying Tyan                            SipManager.INCOMING_CALL_RESULT_CODE, intent);
702845f7332f04864c5483b3e63da5db076fc7a888aHung-ying Tyan                } catch (PendingIntent.CanceledException e) {
703845f7332f04864c5483b3e63da5db076fc7a888aHung-ying Tyan                    Log.w(TAG, "pendingIntent is canceled, drop incoming call");
704845f7332f04864c5483b3e63da5db076fc7a888aHung-ying Tyan                    session.endCall();
7059c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                }
7069c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
7079c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
7089c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
7099c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        @Override
7109c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public void onError(ISipSession session, int errorCode,
7119c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                String message) {
7129c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (DEBUG) Log.d(TAG, "sip session error: "
7139c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    + SipErrorCode.toString(errorCode) + ": " + message);
7149c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
7159c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
716617479363dcafe01bfa1fa025e1d9d122864a0ceHung-ying Tyan        public boolean isOpenedToReceiveCalls() {
717617479363dcafe01bfa1fa025e1d9d122864a0ceHung-ying Tyan            return mOpenedToReceiveCalls;
7189c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
7199c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
7209c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public boolean isRegistered() {
7219c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return mAutoRegistration.isRegistered();
7229c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
7239c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
7249c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private String getUri() {
7259c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return mSipGroup.getLocalProfileUri();
7269c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
7279c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
7289c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
72987e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang    private class WifiScanProcess implements Runnable {
73087e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang        private static final String TAG = "\\WIFI_SCAN/";
73187e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang        private static final int INTERVAL = 60;
73287e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang        private boolean mRunning = false;
73387e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang
73487e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang        private WifiManager mWifiManager;
73587e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang
73687e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang        public void start() {
73787e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang            if (mRunning) return;
73887e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang            mRunning = true;
73987e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang            mTimer.set(INTERVAL * 1000, this);
74087e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang        }
74187e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang
74287e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang        WifiScanProcess() {
74387e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang            mWifiManager = (WifiManager)
74487e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang                    mContext.getSystemService(Context.WIFI_SERVICE);
74587e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang        }
74687e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang
74787e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang        public void run() {
74887e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang            // scan and associate now
74987e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang            if (DEBUGV) Log.v(TAG, "just wake up here for wifi scanning...");
75087e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang            mWifiManager.startScanActive();
75187e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang        }
75287e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang
75387e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang        public void stop() {
75487e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang            mRunning = false;
75587e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang            mTimer.cancel(this);
75687e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang        }
75787e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang    }
75887e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang
7598a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan    private class IntervalMeasurementProcess implements
7608a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan            SipSessionGroup.KeepAliveProcessCallback {
7618a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        private static final String TAG = "SipKeepAliveInterval";
76244ccfb03bb349a90546ddb3dc0063cbf4aaddaf1Hung-ying Tyan        private static final int MAX_INTERVAL = 120; // in seconds
76344ccfb03bb349a90546ddb3dc0063cbf4aaddaf1Hung-ying Tyan        private static final int MIN_INTERVAL = 10; // in seconds
7648a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        private static final int PASS_THRESHOLD = 10;
76544ccfb03bb349a90546ddb3dc0063cbf4aaddaf1Hung-ying Tyan        private static final int MAX_RETRY_COUNT = 5;
766469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang        private SipSessionGroupExt mGroup;
767469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang        private SipSessionGroup.SipSessionImpl mSession;
768469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang        private boolean mRunning;
7698a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        private int mMinInterval = 10; // in seconds
77044ccfb03bb349a90546ddb3dc0063cbf4aaddaf1Hung-ying Tyan        private int mMaxInterval;
77144ccfb03bb349a90546ddb3dc0063cbf4aaddaf1Hung-ying Tyan        private int mInterval;
77244ccfb03bb349a90546ddb3dc0063cbf4aaddaf1Hung-ying Tyan        private int mPassCount = 0;
77344ccfb03bb349a90546ddb3dc0063cbf4aaddaf1Hung-ying Tyan        private int mErrorCount = 0;
77444ccfb03bb349a90546ddb3dc0063cbf4aaddaf1Hung-ying Tyan
77544ccfb03bb349a90546ddb3dc0063cbf4aaddaf1Hung-ying Tyan        public IntervalMeasurementProcess(SipProfile localProfile, int maxInterval) {
77644ccfb03bb349a90546ddb3dc0063cbf4aaddaf1Hung-ying Tyan            mMaxInterval = (maxInterval < 0) ? MAX_INTERVAL : maxInterval;
77744ccfb03bb349a90546ddb3dc0063cbf4aaddaf1Hung-ying Tyan            mInterval = (mMaxInterval + mMinInterval) / 2;
77844ccfb03bb349a90546ddb3dc0063cbf4aaddaf1Hung-ying Tyan
77944ccfb03bb349a90546ddb3dc0063cbf4aaddaf1Hung-ying Tyan            // Don't start measurement if the interval is too small
78044ccfb03bb349a90546ddb3dc0063cbf4aaddaf1Hung-ying Tyan            if (mInterval < MIN_INTERVAL) {
78144ccfb03bb349a90546ddb3dc0063cbf4aaddaf1Hung-ying Tyan                Log.w(TAG, "interval is too small; measurement aborted; "
78244ccfb03bb349a90546ddb3dc0063cbf4aaddaf1Hung-ying Tyan                        + "maxInterval=" + mMaxInterval);
78344ccfb03bb349a90546ddb3dc0063cbf4aaddaf1Hung-ying Tyan                return;
78444ccfb03bb349a90546ddb3dc0063cbf4aaddaf1Hung-ying Tyan            }
785469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang
786469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang            try {
7878a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                mGroup =  new SipSessionGroupExt(localProfile, null, null);
7888a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                // TODO: remove this line once SipWakeupTimer can better handle
7898a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                // variety of timeout values
7908a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                mGroup.setWakeupTimer(new SipWakeupTimer(mContext, mExecutor));
791469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang                mSession = (SipSessionGroup.SipSessionImpl)
7928a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                        mGroup.createSession(null);
793469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang            } catch (Exception e) {
794469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang                Log.w(TAG, "start interval measurement error: " + e);
795469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang            }
796469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang        }
797469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang
798469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang        public void start() {
7998a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan            synchronized (SipService.this) {
8008a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                try {
8018a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                    mSession.startKeepAliveProcess(mInterval, this);
8028a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                } catch (SipException e) {
8038a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                    Log.e(TAG, "start()", e);
8048a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                }
8058a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan            }
806469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang        }
807469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang
808469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang        public void stop() {
8098a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan            synchronized (SipService.this) {
8108a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                mSession.stopKeepAliveProcess();
811469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang            }
812469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang        }
813469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang
8148a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        private void restart() {
815469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang            synchronized (SipService.this) {
816469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang                try {
8178a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                    mSession.stopKeepAliveProcess();
8188a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                    mSession.startKeepAliveProcess(mInterval, this);
8198a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                } catch (SipException e) {
8208a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                    Log.e(TAG, "restart()", e);
821469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang                }
822469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang            }
823469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang        }
824469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang
8258a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        // SipSessionGroup.KeepAliveProcessCallback
8268a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        @Override
8278a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        public void onResponse(boolean portChanged) {
8289c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            synchronized (SipService.this) {
82944ccfb03bb349a90546ddb3dc0063cbf4aaddaf1Hung-ying Tyan                mErrorCount = 0;
83044ccfb03bb349a90546ddb3dc0063cbf4aaddaf1Hung-ying Tyan
8318a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                if (!portChanged) {
83244ccfb03bb349a90546ddb3dc0063cbf4aaddaf1Hung-ying Tyan                    if (++mPassCount != PASS_THRESHOLD) return;
8338a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                    // update the interval, since the current interval is good to
8348a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                    // keep the port mapping.
8358a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                    mKeepAliveInterval = mMinInterval = mInterval;
8368a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                    if (DEBUG) {
8378a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                        Log.d(TAG, "measured good keepalive interval: "
8388a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                                + mKeepAliveInterval);
839257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                    }
8408a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                    onKeepAliveIntervalChanged();
8418a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                } else {
8428a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                    // Since the rport is changed, shorten the interval.
8438a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                    mMaxInterval = mInterval;
8448a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                }
8458a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                if ((mMaxInterval - mMinInterval) < MIN_INTERVAL) {
8468a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                    // update mKeepAliveInterval and stop measurement.
8478a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                    stop();
8488a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                    mKeepAliveInterval = mMinInterval;
8498a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                    if (DEBUG) {
8508a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                        Log.d(TAG, "measured keepalive interval: "
8518a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                                + mKeepAliveInterval);
852469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang                    }
8538a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                } else {
8548a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                    // calculate the new interval and continue.
8558a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                    mInterval = (mMaxInterval + mMinInterval) / 2;
85644ccfb03bb349a90546ddb3dc0063cbf4aaddaf1Hung-ying Tyan                    mPassCount = 0;
8578a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                    if (DEBUG) {
8588a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                        Log.d(TAG, "current interval: " + mKeepAliveInterval
8598a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                                + ", test new interval: " + mInterval);
8608a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                    }
8618a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                    restart();
8629c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                }
8639c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
8649c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
8659c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
8668a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        // SipSessionGroup.KeepAliveProcessCallback
8678a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        @Override
8688a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        public void onError(int errorCode, String description) {
8698a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan            synchronized (SipService.this) {
8708a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                Log.w(TAG, "interval measurement error: " + description);
87144ccfb03bb349a90546ddb3dc0063cbf4aaddaf1Hung-ying Tyan                if (++mErrorCount < MAX_RETRY_COUNT) {
87244ccfb03bb349a90546ddb3dc0063cbf4aaddaf1Hung-ying Tyan                    Log.w(TAG, "  retry count = " + mErrorCount);
87344ccfb03bb349a90546ddb3dc0063cbf4aaddaf1Hung-ying Tyan                    mPassCount = 0;
87444ccfb03bb349a90546ddb3dc0063cbf4aaddaf1Hung-ying Tyan                    restart();
87544ccfb03bb349a90546ddb3dc0063cbf4aaddaf1Hung-ying Tyan                } else {
87644ccfb03bb349a90546ddb3dc0063cbf4aaddaf1Hung-ying Tyan                    Log.w(TAG, "  max retry count reached; measurement aborted");
87744ccfb03bb349a90546ddb3dc0063cbf4aaddaf1Hung-ying Tyan                }
8788a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan            }
8799c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
8809c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
8819c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
8829c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private class AutoRegistrationProcess extends SipSessionAdapter
8838a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan            implements Runnable, SipSessionGroup.KeepAliveProcessCallback {
8848a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        private String TAG = "SipAudoReg";
8859c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private SipSessionGroup.SipSessionImpl mSession;
8868a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        private SipSessionGroup.SipSessionImpl mKeepAliveSession;
8879c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private SipSessionListenerProxy mProxy = new SipSessionListenerProxy();
8889c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private int mBackoff = 1;
8899c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private boolean mRegistered;
8909c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private long mExpiryTime;
8919c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private int mErrorCode;
8929c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private String mErrorMessage;
893d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan        private boolean mRunning = false;
8949c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
8959c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private String getAction() {
8969c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return toString();
8979c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
8989c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
8999c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public void start(SipSessionGroup group) {
900d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan            if (!mRunning) {
901d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                mRunning = true;
9029c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mBackoff = 1;
9039c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mSession = (SipSessionGroup.SipSessionImpl)
9049c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        group.createSession(this);
9059c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                // return right away if no active network connection.
9069c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                if (mSession == null) return;
9079c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
9089c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                // start unregistration to clear up old registration at server
9099c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                // TODO: when rfc5626 is deployed, use reg-id and sip.instance
9109c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                // in registration to avoid adding duplicate entries to server
911257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                mMyWakeLock.acquire(mSession);
9129c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mSession.unregister();
9138a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                if (DEBUG) TAG = mSession.getLocalProfile().getUriString();
9148a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                if (DEBUG) Log.d(TAG, "start AutoRegistrationProcess");
9159c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
9169c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
9179c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
9188a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        // SipSessionGroup.KeepAliveProcessCallback
9198a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        @Override
9208a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        public void onResponse(boolean portChanged) {
9218a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan            synchronized (SipService.this) {
92244ccfb03bb349a90546ddb3dc0063cbf4aaddaf1Hung-ying Tyan                if (portChanged) {
92344ccfb03bb349a90546ddb3dc0063cbf4aaddaf1Hung-ying Tyan                    restartPortMappingLifetimeMeasurement(
92444ccfb03bb349a90546ddb3dc0063cbf4aaddaf1Hung-ying Tyan                            mSession.getLocalProfile(), getKeepAliveInterval());
92544ccfb03bb349a90546ddb3dc0063cbf4aaddaf1Hung-ying Tyan                } else {
92644ccfb03bb349a90546ddb3dc0063cbf4aaddaf1Hung-ying Tyan                    // Start keep-alive interval measurement on the first
92744ccfb03bb349a90546ddb3dc0063cbf4aaddaf1Hung-ying Tyan                    // successfully kept-alive SipSessionGroup
92844ccfb03bb349a90546ddb3dc0063cbf4aaddaf1Hung-ying Tyan                    startPortMappingLifetimeMeasurement(
92944ccfb03bb349a90546ddb3dc0063cbf4aaddaf1Hung-ying Tyan                            mSession.getLocalProfile());
93044ccfb03bb349a90546ddb3dc0063cbf4aaddaf1Hung-ying Tyan                }
9318a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan
9328a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                if (!mRunning || !portChanged) return;
9339dbe2c72276b9968593054dcfd39fd5135a574aaHung-ying Tyan
9349dbe2c72276b9968593054dcfd39fd5135a574aaHung-ying Tyan                // The keep alive process is stopped when port is changed;
9359dbe2c72276b9968593054dcfd39fd5135a574aaHung-ying Tyan                // Nullify the session so that the process can be restarted
9369dbe2c72276b9968593054dcfd39fd5135a574aaHung-ying Tyan                // again when the re-registration is done
9379dbe2c72276b9968593054dcfd39fd5135a574aaHung-ying Tyan                mKeepAliveSession = null;
9389dbe2c72276b9968593054dcfd39fd5135a574aaHung-ying Tyan
9398a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                // Acquire wake lock for the registration process. The
9408a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                // lock will be released when registration is complete.
9418a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                mMyWakeLock.acquire(mSession);
9428a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                mSession.register(EXPIRY_TIME);
9438a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan            }
9448a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        }
9458a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan
9468a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        // SipSessionGroup.KeepAliveProcessCallback
9478a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        @Override
9488a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        public void onError(int errorCode, String description) {
9498a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan            Log.e(TAG, "keepalive error: " + description);
95044ccfb03bb349a90546ddb3dc0063cbf4aaddaf1Hung-ying Tyan            onResponse(true); // re-register immediately
9518a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        }
9528a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan
9539c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public void stop() {
954d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan            if (!mRunning) return;
955d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan            mRunning = false;
956257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan            mMyWakeLock.release(mSession);
957257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan            if (mSession != null) {
958257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                mSession.setListener(null);
959257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                if (mConnected && mRegistered) mSession.unregister();
960257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan            }
961d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan
9629c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mTimer.cancel(this);
9638a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan            if (mKeepAliveSession != null) {
9648a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                mKeepAliveSession.stopKeepAliveProcess();
9658a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                mKeepAliveSession = null;
9669c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
9679c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
968d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan            mRegistered = false;
969d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan            setListener(mProxy.getListener());
9709c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
9719c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
9728a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        public void onKeepAliveIntervalChanged() {
9738a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan            if (mKeepAliveSession != null) {
9748a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                int newInterval = getKeepAliveInterval();
9758a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                if (DEBUGV) {
9768a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                    Log.v(TAG, "restart keepalive w interval=" + newInterval);
9778a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                }
9788a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                mKeepAliveSession.stopKeepAliveProcess();
9798a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                try {
9808a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                    mKeepAliveSession.startKeepAliveProcess(newInterval, this);
9818a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                } catch (SipException e) {
9828a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                    Log.e(TAG, "onKeepAliveIntervalChanged()", e);
9838a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                }
9848a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan            }
9858a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        }
9868a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan
9879c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public void setListener(ISipSessionListener listener) {
9889c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            synchronized (SipService.this) {
9899c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mProxy.setListener(listener);
9909c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
9919c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                try {
9929c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    int state = (mSession == null)
9939c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            ? SipSession.State.READY_TO_CALL
9949c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            : mSession.getState();
9959c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    if ((state == SipSession.State.REGISTERING)
9969c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            || (state == SipSession.State.DEREGISTERING)) {
9979c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        mProxy.onRegistering(mSession);
9989c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    } else if (mRegistered) {
9999c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        int duration = (int)
10009c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                                (mExpiryTime - SystemClock.elapsedRealtime());
10019c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        mProxy.onRegistrationDone(mSession, duration);
10029c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    } else if (mErrorCode != SipErrorCode.NO_ERROR) {
10039c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        if (mErrorCode == SipErrorCode.TIME_OUT) {
10049c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            mProxy.onRegistrationTimeout(mSession);
10059c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        } else {
10069c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            mProxy.onRegistrationFailed(mSession, mErrorCode,
10079c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                                    mErrorMessage);
10089c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        }
1009d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                    } else if (!mConnected) {
1010d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                        mProxy.onRegistrationFailed(mSession,
1011d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                                SipErrorCode.DATA_CONNECTION_LOST,
1012d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                                "no data connection");
1013d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                    } else if (!mRunning) {
1014d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                        mProxy.onRegistrationFailed(mSession,
1015d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                                SipErrorCode.CLIENT_ERROR,
1016d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                                "registration not running");
1017d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                    } else {
1018d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                        mProxy.onRegistrationFailed(mSession,
1019d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                                SipErrorCode.IN_PROGRESS,
1020d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                                String.valueOf(state));
10219c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    }
10229c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                } catch (Throwable t) {
10239c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    Log.w(TAG, "setListener(): " + t);
10249c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                }
10259c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
10269c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
10279c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
10289c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public boolean isRegistered() {
10299c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return mRegistered;
10309c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
10319c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
1032257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan        // timeout handler: re-register
10338a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        @Override
10349c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public void run() {
1035d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan            synchronized (SipService.this) {
1036d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                if (!mRunning) return;
1037d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan
1038d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                mErrorCode = SipErrorCode.NO_ERROR;
1039d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                mErrorMessage = null;
10408a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                if (DEBUG) Log.d(TAG, "registering");
1041257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                if (mConnected) {
1042257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                    mMyWakeLock.acquire(mSession);
1043257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                    mSession.register(EXPIRY_TIME);
1044257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                }
10459c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
10469c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
10479c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
10489c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private void restart(int duration) {
10499c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (DEBUG) Log.d(TAG, "Refresh registration " + duration + "s later.");
10509c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mTimer.cancel(this);
10519c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mTimer.set(duration * 1000, this);
10529c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
10539c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
10549c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private int backoffDuration() {
10559c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            int duration = SHORT_EXPIRY_TIME * mBackoff;
10569c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (duration > 3600) {
10579c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                duration = 3600;
10589c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            } else {
10599c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mBackoff *= 2;
10609c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
10619c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return duration;
10629c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
10639c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
10649c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        @Override
10659c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public void onRegistering(ISipSession session) {
10669c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (DEBUG) Log.d(TAG, "onRegistering(): " + session);
10679c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            synchronized (SipService.this) {
1068d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                if (notCurrentSession(session)) return;
1069d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan
10709c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mRegistered = false;
10719c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mProxy.onRegistering(session);
10729c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
10739c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
10749c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
1075d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan        private boolean notCurrentSession(ISipSession session) {
1076d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan            if (session != mSession) {
1077d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                ((SipSessionGroup.SipSessionImpl) session).setListener(null);
1078257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                mMyWakeLock.release(session);
1079d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                return true;
1080d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan            }
1081d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan            return !mRunning;
1082d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan        }
1083d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan
10849c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        @Override
10859c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public void onRegistrationDone(ISipSession session, int duration) {
10869c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (DEBUG) Log.d(TAG, "onRegistrationDone(): " + session);
10879c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            synchronized (SipService.this) {
1088d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                if (notCurrentSession(session)) return;
10899c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
10909c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mProxy.onRegistrationDone(session, duration);
10919c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
10929c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                if (duration > 0) {
10939c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    mExpiryTime = SystemClock.elapsedRealtime()
10949c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            + (duration * 1000);
10959c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
10969c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    if (!mRegistered) {
10979c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        mRegistered = true;
10989c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        // allow some overlap to avoid call drop during renew
10999c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        duration -= MIN_EXPIRY_TIME;
11009c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        if (duration < MIN_EXPIRY_TIME) {
11019c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            duration = MIN_EXPIRY_TIME;
11029c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        }
11039c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        restart(duration);
11049c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
11058a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                        SipProfile localProfile = mSession.getLocalProfile();
11068a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                        if ((mKeepAliveSession == null) && (isBehindNAT(mLocalIp)
11078a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                                || localProfile.getSendKeepAlive())) {
11088a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                            mKeepAliveSession = mSession.duplicate();
11098a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                            Log.d(TAG, "start keepalive");
11108a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                            try {
11118a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                                mKeepAliveSession.startKeepAliveProcess(
11128a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                                        getKeepAliveInterval(), this);
11138a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                            } catch (SipException e) {
11148a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                                Log.e(TAG, "AutoRegistrationProcess", e);
11159c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            }
11169c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        }
11179c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    }
1118257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                    mMyWakeLock.release(session);
11199c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                } else {
11209c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    mRegistered = false;
11219c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    mExpiryTime = -1L;
11229c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    if (DEBUG) Log.d(TAG, "Refresh registration immediately");
11239c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    run();
11249c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                }
11259c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
11269c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
11279c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
11289c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        @Override
11299c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public void onRegistrationFailed(ISipSession session, int errorCode,
11309c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                String message) {
11319c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (DEBUG) Log.d(TAG, "onRegistrationFailed(): " + session + ": "
11329c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    + SipErrorCode.toString(errorCode) + ": " + message);
11339c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            synchronized (SipService.this) {
1134d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                if (notCurrentSession(session)) return;
11359c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
1136fcc474636f42243cfce960158373b9c49636a556Hung-ying Tyan                switch (errorCode) {
1137fcc474636f42243cfce960158373b9c49636a556Hung-ying Tyan                    case SipErrorCode.INVALID_CREDENTIALS:
1138fcc474636f42243cfce960158373b9c49636a556Hung-ying Tyan                    case SipErrorCode.SERVER_UNREACHABLE:
1139fcc474636f42243cfce960158373b9c49636a556Hung-ying Tyan                        if (DEBUG) Log.d(TAG, "   pause auto-registration");
1140fcc474636f42243cfce960158373b9c49636a556Hung-ying Tyan                        stop();
11410b88a073712b1510db7c636f89c4e19f0131449aHung-ying Tyan                        break;
1142fcc474636f42243cfce960158373b9c49636a556Hung-ying Tyan                    default:
1143fcc474636f42243cfce960158373b9c49636a556Hung-ying Tyan                        restartLater();
11449c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                }
1145d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan
1146d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                mErrorCode = errorCode;
1147d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                mErrorMessage = message;
1148d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                mProxy.onRegistrationFailed(session, errorCode, message);
1149257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                mMyWakeLock.release(session);
11509c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
11519c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
11529c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
11539c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        @Override
11549c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public void onRegistrationTimeout(ISipSession session) {
11559c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (DEBUG) Log.d(TAG, "onRegistrationTimeout(): " + session);
11569c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            synchronized (SipService.this) {
1157d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                if (notCurrentSession(session)) return;
1158d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan
11599c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mErrorCode = SipErrorCode.TIME_OUT;
11609c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mProxy.onRegistrationTimeout(session);
1161fcc474636f42243cfce960158373b9c49636a556Hung-ying Tyan                restartLater();
1162257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                mMyWakeLock.release(session);
11639c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
11649c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
11659c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
1166fcc474636f42243cfce960158373b9c49636a556Hung-ying Tyan        private void restartLater() {
11679c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mRegistered = false;
11689c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            restart(backoffDuration());
11699c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
11709c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
11719c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
11729c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private class ConnectivityReceiver extends BroadcastReceiver {
11739c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private Timer mTimer = new Timer();
11749c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private MyTimerTask mTask;
11759c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
11769c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        @Override
1177257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan        public void onReceive(final Context context, final Intent intent) {
1178257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan            // Run the handler in MyExecutor to be protected by wake lock
11794cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan            mExecutor.execute(new Runnable() {
1180257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                public void run() {
1181257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                    onReceiveInternal(context, intent);
1182257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                }
1183257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan            });
1184257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan        }
1185257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan
1186257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan        private void onReceiveInternal(Context context, Intent intent) {
11879c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            String action = intent.getAction();
11889c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
11899c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                Bundle b = intent.getExtras();
11909c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                if (b != null) {
11919c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    NetworkInfo netInfo = (NetworkInfo)
11929c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            b.get(ConnectivityManager.EXTRA_NETWORK_INFO);
11939c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    String type = netInfo.getTypeName();
11949c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    NetworkInfo.State state = netInfo.getState();
11959c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
11969c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    if (mWifiOnly && (netInfo.getType() !=
11979c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            ConnectivityManager.TYPE_WIFI)) {
11989c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        if (DEBUG) {
11999c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            Log.d(TAG, "Wifi only, other connectivity ignored: "
12009c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                                    + type);
12019c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        }
12029c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        return;
12039c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    }
12049c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
12059c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    NetworkInfo activeNetInfo = getActiveNetworkInfo();
12069c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    if (DEBUG) {
12079c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        if (activeNetInfo != null) {
12089c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            Log.d(TAG, "active network: "
12099c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                                    + activeNetInfo.getTypeName()
12109c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                                    + ((activeNetInfo.getState() == NetworkInfo.State.CONNECTED)
12119c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                                            ? " CONNECTED" : " DISCONNECTED"));
12129c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        } else {
12139c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            Log.d(TAG, "active network: null");
12149c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        }
12159c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    }
12169c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    if ((state == NetworkInfo.State.CONNECTED)
12179c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            && (activeNetInfo != null)
12189c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            && (activeNetInfo.getType() != netInfo.getType())) {
12199c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        if (DEBUG) Log.d(TAG, "ignore connect event: " + type
12209c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                                + ", active: " + activeNetInfo.getTypeName());
12219c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        return;
12229c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    }
12239c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
12249c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    if (state == NetworkInfo.State.CONNECTED) {
12259c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        if (DEBUG) Log.d(TAG, "Connectivity alert: CONNECTED " + type);
12269c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        onChanged(type, true);
12279c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    } else if (state == NetworkInfo.State.DISCONNECTED) {
12289c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        if (DEBUG) Log.d(TAG, "Connectivity alert: DISCONNECTED " + type);
12299c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        onChanged(type, false);
12309c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    } else {
12319c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        if (DEBUG) Log.d(TAG, "Connectivity alert not processed: "
12329c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                                + state + " " + type);
12339c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    }
12349c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                }
12359c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
12369c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
12379c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
12389c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private NetworkInfo getActiveNetworkInfo() {
12399c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            ConnectivityManager cm = (ConnectivityManager)
12409c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
12419c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return cm.getActiveNetworkInfo();
12429c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
12439c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
12449c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private void onChanged(String type, boolean connected) {
12459c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            synchronized (SipService.this) {
12469c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                // When turning on WIFI, it needs some time for network
12479c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                // connectivity to get stabile so we defer good news (because
12489c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                // we want to skip the interim ones) but deliver bad news
12499c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                // immediately
12509c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                if (connected) {
1251f4a38e8e2f83402d2307edf67ef79b6f74990c8bHung-ying Tyan                    if (mTask != null) {
1252f4a38e8e2f83402d2307edf67ef79b6f74990c8bHung-ying Tyan                        mTask.cancel();
1253f4a38e8e2f83402d2307edf67ef79b6f74990c8bHung-ying Tyan                        mMyWakeLock.release(mTask);
1254f4a38e8e2f83402d2307edf67ef79b6f74990c8bHung-ying Tyan                    }
12559c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    mTask = new MyTimerTask(type, connected);
12569c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    mTimer.schedule(mTask, 2 * 1000L);
1257257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                    // hold wakup lock so that we can finish changes before the
1258257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                    // device goes to sleep
1259257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                    mMyWakeLock.acquire(mTask);
12609c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                } else {
12619c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    if ((mTask != null) && mTask.mNetworkType.equals(type)) {
12629c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        mTask.cancel();
1263257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                        mMyWakeLock.release(mTask);
12649c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    }
12659c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    onConnectivityChanged(type, false);
12669c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                }
12679c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
12689c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
12699c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
12709c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private class MyTimerTask extends TimerTask {
12719c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            private boolean mConnected;
12729c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            private String mNetworkType;
12739c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
12749c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            public MyTimerTask(String type, boolean connected) {
12759c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mNetworkType = type;
12769c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mConnected = connected;
12779c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
12789c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
1279d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan            // timeout handler
12809c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            @Override
12819c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            public void run() {
12829c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                // delegate to mExecutor
12834cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan                mExecutor.execute(new Runnable() {
12849c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    public void run() {
12859c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        realRun();
12869c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    }
12879c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                });
12889c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
12899c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
12909c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            private void realRun() {
12919c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                synchronized (SipService.this) {
12929c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    if (mTask != this) {
12939c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        Log.w(TAG, "  unexpected task: " + mNetworkType
12949c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                                + (mConnected ? " CONNECTED" : "DISCONNECTED"));
1295f4a38e8e2f83402d2307edf67ef79b6f74990c8bHung-ying Tyan                        mMyWakeLock.release(this);
12969c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        return;
12979c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    }
12989c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    mTask = null;
12999c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    if (DEBUG) Log.d(TAG, " deliver change for " + mNetworkType
13009c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            + (mConnected ? " CONNECTED" : "DISCONNECTED"));
13019c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    onConnectivityChanged(mNetworkType, mConnected);
1302257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                    mMyWakeLock.release(this);
13039c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                }
13049c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
13059c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
13069c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
13079c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
1308257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan    private static Looper createLooper() {
1309257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan        HandlerThread thread = new HandlerThread("SipService.Executor");
1310257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan        thread.start();
1311257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan        return thread.getLooper();
1312257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan    }
1313257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan
1314257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan    // Executes immediate tasks in a single thread.
1315257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan    // Hold/release wake lock for running tasks
13164cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan    private class MyExecutor extends Handler implements Executor {
13179c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        MyExecutor() {
13189c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            super(createLooper());
13199c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
13209c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
13214cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        @Override
13224cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        public void execute(Runnable task) {
1323257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan            mMyWakeLock.acquire(task);
13249c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            Message.obtain(this, 0/* don't care */, task).sendToTarget();
13259c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
13269c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
13279c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        @Override
13289c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public void handleMessage(Message msg) {
13299c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (msg.obj instanceof Runnable) {
1330257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                executeInternal((Runnable) msg.obj);
13319c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            } else {
13329c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                Log.w(TAG, "can't handle msg: " + msg);
13339c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
13349c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
1335257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan
1336257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan        private void executeInternal(Runnable task) {
1337257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan            try {
1338257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                task.run();
1339257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan            } catch (Throwable t) {
1340257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                Log.e(TAG, "run task: " + task, t);
1341257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan            } finally {
1342257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                mMyWakeLock.release(task);
1343257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan            }
1344257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan        }
1345257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan    }
13469c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan}
1347