SipService.java revision 37f93394fe5c7cfede3a780214fa99a9e2a69133
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
77cd9cb0845a0ad468bbbadadc133bc7a406e40b4aHung-ying Tyan    private static final int DEFAULT_MAX_KEEPALIVE_INTERVAL = 120; // in seconds
789c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
799c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private Context mContext;
809c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private String mLocalIp;
819c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private String mNetworkType;
829c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private boolean mConnected;
834cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan    private SipWakeupTimer mTimer;
8487e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang    private WifiScanProcess mWifiScanProcess;
859c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private WifiManager.WifiLock mWifiLock;
869c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private boolean mWifiOnly;
87987b505b1c190ff0e5a05d2cc20c9b08d2b99b18Hung-ying Tyan    private BroadcastReceiver mWifiStateReceiver = null;
88987b505b1c190ff0e5a05d2cc20c9b08d2b99b18Hung-ying Tyan
89469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang    private IntervalMeasurementProcess mIntervalMeasurementProcess;
909c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
914cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan    private MyExecutor mExecutor = new MyExecutor();
929c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
939c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    // SipProfile URI --> group
949c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private Map<String, SipSessionGroupExt> mSipGroups =
959c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            new HashMap<String, SipSessionGroupExt>();
969c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
979c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    // session ID --> session
989c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private Map<String, ISipSession> mPendingSessions =
999c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            new HashMap<String, ISipSession>();
1009c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
1019c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private ConnectivityReceiver mConnectivityReceiver;
1029abf73f1bde7b1617603c888650dbda9d6a7fddfHung-ying Tyan    private boolean mWifiEnabled;
103acedadc1967457ac2f8981c67f884fd8c0ee853cHung-ying Tyan    private SipWakeLock mMyWakeLock;
104469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang    private int mKeepAliveInterval;
105cd9cb0845a0ad468bbbadadc133bc7a406e40b4aHung-ying Tyan    private int mLastGoodKeepAliveInterval = DEFAULT_KEEPALIVE_INTERVAL;
1069c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
1079c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    /**
1089c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan     * Starts the SIP service. Do nothing if the SIP API is not supported on the
1099c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan     * device.
1109c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan     */
1119c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    public static void start(Context context) {
1129c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        if (SipManager.isApiSupported(context)) {
1139c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            ServiceManager.addService("sip", new SipService(context));
11422523a59d879cf47f1e9c202d001d569fad4f69eHung-ying Tyan            context.sendBroadcast(new Intent(SipManager.ACTION_SIP_SERVICE_UP));
115dc7545495e1ef8bad32cd78775f4dc072015eea0Hung-ying Tyan            if (DEBUG) Log.d(TAG, "SIP service started");
1169c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
1179c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
1189c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
1199c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private SipService(Context context) {
1209c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        if (DEBUG) Log.d(TAG, " service started!");
1219c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        mContext = context;
1229c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        mConnectivityReceiver = new ConnectivityReceiver();
123acedadc1967457ac2f8981c67f884fd8c0ee853cHung-ying Tyan        mMyWakeLock = new SipWakeLock((PowerManager)
124257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                context.getSystemService(Context.POWER_SERVICE));
1259c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
1264cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        mTimer = new SipWakeupTimer(context, mExecutor);
1279c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        mWifiOnly = SipManager.isSipWifiOnly(context);
1289c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
1299c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
130987b505b1c190ff0e5a05d2cc20c9b08d2b99b18Hung-ying Tyan    private BroadcastReceiver createWifiBroadcastReceiver() {
131987b505b1c190ff0e5a05d2cc20c9b08d2b99b18Hung-ying Tyan        return new BroadcastReceiver() {
132987b505b1c190ff0e5a05d2cc20c9b08d2b99b18Hung-ying Tyan            @Override
133987b505b1c190ff0e5a05d2cc20c9b08d2b99b18Hung-ying Tyan            public void onReceive(Context context, Intent intent) {
134987b505b1c190ff0e5a05d2cc20c9b08d2b99b18Hung-ying Tyan                String action = intent.getAction();
135987b505b1c190ff0e5a05d2cc20c9b08d2b99b18Hung-ying Tyan                if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) {
136987b505b1c190ff0e5a05d2cc20c9b08d2b99b18Hung-ying Tyan                    int state = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
137987b505b1c190ff0e5a05d2cc20c9b08d2b99b18Hung-ying Tyan                            WifiManager.WIFI_STATE_UNKNOWN);
138987b505b1c190ff0e5a05d2cc20c9b08d2b99b18Hung-ying Tyan                    synchronized (SipService.this) {
139987b505b1c190ff0e5a05d2cc20c9b08d2b99b18Hung-ying Tyan                        switch (state) {
140987b505b1c190ff0e5a05d2cc20c9b08d2b99b18Hung-ying Tyan                            case WifiManager.WIFI_STATE_ENABLED:
141987b505b1c190ff0e5a05d2cc20c9b08d2b99b18Hung-ying Tyan                                mWifiEnabled = true;
142987b505b1c190ff0e5a05d2cc20c9b08d2b99b18Hung-ying Tyan                                if (anyOpenedToReceiveCalls()) grabWifiLock();
143987b505b1c190ff0e5a05d2cc20c9b08d2b99b18Hung-ying Tyan                                break;
144987b505b1c190ff0e5a05d2cc20c9b08d2b99b18Hung-ying Tyan                            case WifiManager.WIFI_STATE_DISABLED:
145987b505b1c190ff0e5a05d2cc20c9b08d2b99b18Hung-ying Tyan                                mWifiEnabled = false;
146987b505b1c190ff0e5a05d2cc20c9b08d2b99b18Hung-ying Tyan                                releaseWifiLock();
147987b505b1c190ff0e5a05d2cc20c9b08d2b99b18Hung-ying Tyan                                break;
148987b505b1c190ff0e5a05d2cc20c9b08d2b99b18Hung-ying Tyan                        }
1499abf73f1bde7b1617603c888650dbda9d6a7fddfHung-ying Tyan                    }
1509abf73f1bde7b1617603c888650dbda9d6a7fddfHung-ying Tyan                }
151f452fd81f1a308428b0bfbae883fddb9caced878Chung-yih Wang            }
152987b505b1c190ff0e5a05d2cc20c9b08d2b99b18Hung-ying Tyan        };
153f452fd81f1a308428b0bfbae883fddb9caced878Chung-yih Wang    };
154f452fd81f1a308428b0bfbae883fddb9caced878Chung-yih Wang
155dc7545495e1ef8bad32cd78775f4dc072015eea0Hung-ying Tyan    private void registerReceivers() {
156dc7545495e1ef8bad32cd78775f4dc072015eea0Hung-ying Tyan        mContext.registerReceiver(mConnectivityReceiver,
157dc7545495e1ef8bad32cd78775f4dc072015eea0Hung-ying Tyan                new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
158987b505b1c190ff0e5a05d2cc20c9b08d2b99b18Hung-ying Tyan        if (SipManager.isSipWifiOnly(mContext)) {
159987b505b1c190ff0e5a05d2cc20c9b08d2b99b18Hung-ying Tyan            mWifiStateReceiver = createWifiBroadcastReceiver();
160987b505b1c190ff0e5a05d2cc20c9b08d2b99b18Hung-ying Tyan            mContext.registerReceiver(mWifiStateReceiver,
161987b505b1c190ff0e5a05d2cc20c9b08d2b99b18Hung-ying Tyan                    new IntentFilter(WifiManager.WIFI_STATE_CHANGED_ACTION));
162987b505b1c190ff0e5a05d2cc20c9b08d2b99b18Hung-ying Tyan        }
163dc7545495e1ef8bad32cd78775f4dc072015eea0Hung-ying Tyan        if (DEBUG) Log.d(TAG, " +++ register receivers");
164dc7545495e1ef8bad32cd78775f4dc072015eea0Hung-ying Tyan    }
165dc7545495e1ef8bad32cd78775f4dc072015eea0Hung-ying Tyan
166dc7545495e1ef8bad32cd78775f4dc072015eea0Hung-ying Tyan    private void unregisterReceivers() {
167dc7545495e1ef8bad32cd78775f4dc072015eea0Hung-ying Tyan        mContext.unregisterReceiver(mConnectivityReceiver);
168987b505b1c190ff0e5a05d2cc20c9b08d2b99b18Hung-ying Tyan        if (SipManager.isSipWifiOnly(mContext)) {
169987b505b1c190ff0e5a05d2cc20c9b08d2b99b18Hung-ying Tyan            mContext.unregisterReceiver(mWifiStateReceiver);
170987b505b1c190ff0e5a05d2cc20c9b08d2b99b18Hung-ying Tyan        }
171dc7545495e1ef8bad32cd78775f4dc072015eea0Hung-ying Tyan        if (DEBUG) Log.d(TAG, " --- unregister receivers");
172dc7545495e1ef8bad32cd78775f4dc072015eea0Hung-ying Tyan    }
173dc7545495e1ef8bad32cd78775f4dc072015eea0Hung-ying Tyan
1749c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    public synchronized SipProfile[] getListOfProfiles() {
1758127b35b75c13f853dc8eb2fed8bba4bf99e3eedHung-ying Tyan        mContext.enforceCallingOrSelfPermission(
1768127b35b75c13f853dc8eb2fed8bba4bf99e3eedHung-ying Tyan                android.Manifest.permission.USE_SIP, null);
177a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        boolean isCallerRadio = isCallerRadio();
178a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        ArrayList<SipProfile> profiles = new ArrayList<SipProfile>();
1799c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        for (SipSessionGroupExt group : mSipGroups.values()) {
180a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan            if (isCallerRadio || isCallerCreator(group)) {
181a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan                profiles.add(group.getLocalProfile());
182a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan            }
1839c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
184a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        return profiles.toArray(new SipProfile[profiles.size()]);
1859c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
1869c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
187dc7545495e1ef8bad32cd78775f4dc072015eea0Hung-ying Tyan    public synchronized void open(SipProfile localProfile) {
1888127b35b75c13f853dc8eb2fed8bba4bf99e3eedHung-ying Tyan        mContext.enforceCallingOrSelfPermission(
1898127b35b75c13f853dc8eb2fed8bba4bf99e3eedHung-ying Tyan                android.Manifest.permission.USE_SIP, null);
1909c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        localProfile.setCallingUid(Binder.getCallingUid());
1919c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        try {
192dc7545495e1ef8bad32cd78775f4dc072015eea0Hung-ying Tyan            boolean addingFirstProfile = mSipGroups.isEmpty();
1939c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            createGroup(localProfile);
194dc7545495e1ef8bad32cd78775f4dc072015eea0Hung-ying Tyan            if (addingFirstProfile && !mSipGroups.isEmpty()) registerReceivers();
1959c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        } catch (SipException e) {
1969c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            Log.e(TAG, "openToMakeCalls()", e);
1979c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            // TODO: how to send the exception back
1989c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
1999c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
2009c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
2019c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    public synchronized void open3(SipProfile localProfile,
202845f7332f04864c5483b3e63da5db076fc7a888aHung-ying Tyan            PendingIntent incomingCallPendingIntent,
203845f7332f04864c5483b3e63da5db076fc7a888aHung-ying Tyan            ISipSessionListener listener) {
2048127b35b75c13f853dc8eb2fed8bba4bf99e3eedHung-ying Tyan        mContext.enforceCallingOrSelfPermission(
2058127b35b75c13f853dc8eb2fed8bba4bf99e3eedHung-ying Tyan                android.Manifest.permission.USE_SIP, null);
2069c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        localProfile.setCallingUid(Binder.getCallingUid());
207845f7332f04864c5483b3e63da5db076fc7a888aHung-ying Tyan        if (incomingCallPendingIntent == null) {
208845f7332f04864c5483b3e63da5db076fc7a888aHung-ying Tyan            Log.w(TAG, "incomingCallPendingIntent cannot be null; "
209845f7332f04864c5483b3e63da5db076fc7a888aHung-ying Tyan                    + "the profile is not opened");
210a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan            return;
2119c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
2129c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        if (DEBUG) Log.d(TAG, "open3: " + localProfile.getUriString() + ": "
213845f7332f04864c5483b3e63da5db076fc7a888aHung-ying Tyan                + incomingCallPendingIntent + ": " + listener);
2149c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        try {
215dc7545495e1ef8bad32cd78775f4dc072015eea0Hung-ying Tyan            boolean addingFirstProfile = mSipGroups.isEmpty();
2169c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            SipSessionGroupExt group = createGroup(localProfile,
217845f7332f04864c5483b3e63da5db076fc7a888aHung-ying Tyan                    incomingCallPendingIntent, listener);
218dc7545495e1ef8bad32cd78775f4dc072015eea0Hung-ying Tyan            if (addingFirstProfile && !mSipGroups.isEmpty()) registerReceivers();
2199c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (localProfile.getAutoRegistration()) {
2209c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                group.openToReceiveCalls();
2219abf73f1bde7b1617603c888650dbda9d6a7fddfHung-ying Tyan                if (mWifiEnabled) grabWifiLock();
2229c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
2239c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        } catch (SipException e) {
2249c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            Log.e(TAG, "openToReceiveCalls()", e);
2259c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            // TODO: how to send the exception back
2269c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
2279c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
2289c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
229a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan    private boolean isCallerCreator(SipSessionGroupExt group) {
230a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        SipProfile profile = group.getLocalProfile();
231a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        return (profile.getCallingUid() == Binder.getCallingUid());
232a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan    }
233a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan
234a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan    private boolean isCallerCreatorOrRadio(SipSessionGroupExt group) {
235a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        return (isCallerRadio() || isCallerCreator(group));
236a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan    }
237a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan
238a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan    private boolean isCallerRadio() {
239a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        return (Binder.getCallingUid() == Process.PHONE_UID);
240a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan    }
241a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan
2429c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    public synchronized void close(String localProfileUri) {
2438127b35b75c13f853dc8eb2fed8bba4bf99e3eedHung-ying Tyan        mContext.enforceCallingOrSelfPermission(
2448127b35b75c13f853dc8eb2fed8bba4bf99e3eedHung-ying Tyan                android.Manifest.permission.USE_SIP, null);
245a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        SipSessionGroupExt group = mSipGroups.get(localProfileUri);
246a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        if (group == null) return;
247a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        if (!isCallerCreatorOrRadio(group)) {
2486d9d99615a30de0675271553552c3c7b49311354Joe Onorato            Log.w(TAG, "only creator or radio can close this profile");
249a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan            return;
2509c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
251a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan
252a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        group = mSipGroups.remove(localProfileUri);
253a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        notifyProfileRemoved(group.getLocalProfile());
254a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        group.close();
255257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan
256617479363dcafe01bfa1fa025e1d9d122864a0ceHung-ying Tyan        if (!anyOpenedToReceiveCalls()) {
257257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan            releaseWifiLock();
258257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan            mMyWakeLock.reset(); // in case there's leak
259257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan        }
260dc7545495e1ef8bad32cd78775f4dc072015eea0Hung-ying Tyan        if (mSipGroups.isEmpty()) unregisterReceivers();
2619c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
2629c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
2639c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    public synchronized boolean isOpened(String localProfileUri) {
2648127b35b75c13f853dc8eb2fed8bba4bf99e3eedHung-ying Tyan        mContext.enforceCallingOrSelfPermission(
2658127b35b75c13f853dc8eb2fed8bba4bf99e3eedHung-ying Tyan                android.Manifest.permission.USE_SIP, null);
2669c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        SipSessionGroupExt group = mSipGroups.get(localProfileUri);
267a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        if (group == null) return false;
268a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        if (isCallerCreatorOrRadio(group)) {
269617479363dcafe01bfa1fa025e1d9d122864a0ceHung-ying Tyan            return true;
270a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        } else {
2716d9d99615a30de0675271553552c3c7b49311354Joe Onorato            Log.w(TAG, "only creator or radio can query on the profile");
272a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan            return false;
273a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        }
2749c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
2759c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
2769c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    public synchronized boolean isRegistered(String localProfileUri) {
2778127b35b75c13f853dc8eb2fed8bba4bf99e3eedHung-ying Tyan        mContext.enforceCallingOrSelfPermission(
2788127b35b75c13f853dc8eb2fed8bba4bf99e3eedHung-ying Tyan                android.Manifest.permission.USE_SIP, null);
2799c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        SipSessionGroupExt group = mSipGroups.get(localProfileUri);
280a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        if (group == null) return false;
281a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        if (isCallerCreatorOrRadio(group)) {
282a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan            return group.isRegistered();
283a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        } else {
2846d9d99615a30de0675271553552c3c7b49311354Joe Onorato            Log.w(TAG, "only creator or radio can query on the profile");
285a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan            return false;
286a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        }
2879c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
2889c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
2899c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    public synchronized void setRegistrationListener(String localProfileUri,
2909c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            ISipSessionListener listener) {
2918127b35b75c13f853dc8eb2fed8bba4bf99e3eedHung-ying Tyan        mContext.enforceCallingOrSelfPermission(
2928127b35b75c13f853dc8eb2fed8bba4bf99e3eedHung-ying Tyan                android.Manifest.permission.USE_SIP, null);
2939c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        SipSessionGroupExt group = mSipGroups.get(localProfileUri);
294a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        if (group == null) return;
295a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        if (isCallerCreator(group)) {
296a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan            group.setListener(listener);
297a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        } else {
2986d9d99615a30de0675271553552c3c7b49311354Joe Onorato            Log.w(TAG, "only creator can set listener on the profile");
299a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        }
3009c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
3019c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
3029c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    public synchronized ISipSession createSession(SipProfile localProfile,
3039c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            ISipSessionListener listener) {
3048127b35b75c13f853dc8eb2fed8bba4bf99e3eedHung-ying Tyan        mContext.enforceCallingOrSelfPermission(
3058127b35b75c13f853dc8eb2fed8bba4bf99e3eedHung-ying Tyan                android.Manifest.permission.USE_SIP, null);
3069c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        localProfile.setCallingUid(Binder.getCallingUid());
3079c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        if (!mConnected) return null;
3089c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        try {
3099c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            SipSessionGroupExt group = createGroup(localProfile);
3109c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return group.createSession(listener);
3119c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        } catch (SipException e) {
3126d9d99615a30de0675271553552c3c7b49311354Joe Onorato            if (DEBUG) Log.d(TAG, "createSession()", e);
3139c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return null;
3149c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
3159c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
3169c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
3179c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    public synchronized ISipSession getPendingSession(String callId) {
3188127b35b75c13f853dc8eb2fed8bba4bf99e3eedHung-ying Tyan        mContext.enforceCallingOrSelfPermission(
3198127b35b75c13f853dc8eb2fed8bba4bf99e3eedHung-ying Tyan                android.Manifest.permission.USE_SIP, null);
3209c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        if (callId == null) return null;
3219c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        return mPendingSessions.get(callId);
3229c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
3239c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
3249c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private String determineLocalIp() {
3259c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        try {
3269c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            DatagramSocket s = new DatagramSocket();
3279c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            s.connect(InetAddress.getByName("192.168.1.1"), 80);
3289c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return s.getLocalAddress().getHostAddress();
3299c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        } catch (IOException e) {
3306d9d99615a30de0675271553552c3c7b49311354Joe Onorato            if (DEBUG) Log.d(TAG, "determineLocalIp()", e);
3319c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            // dont do anything; there should be a connectivity change going
3329c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return null;
3339c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
3349c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
3359c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
3369c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private SipSessionGroupExt createGroup(SipProfile localProfile)
3379c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            throws SipException {
3389c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        String key = localProfile.getUriString();
3399c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        SipSessionGroupExt group = mSipGroups.get(key);
3409c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        if (group == null) {
3419c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            group = new SipSessionGroupExt(localProfile, null, null);
3429c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mSipGroups.put(key, group);
3439c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            notifyProfileAdded(localProfile);
344a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        } else if (!isCallerCreator(group)) {
345a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan            throw new SipException("only creator can access the profile");
3469c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
3479c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        return group;
3489c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
3499c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
3509c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private SipSessionGroupExt createGroup(SipProfile localProfile,
351845f7332f04864c5483b3e63da5db076fc7a888aHung-ying Tyan            PendingIntent incomingCallPendingIntent,
352845f7332f04864c5483b3e63da5db076fc7a888aHung-ying Tyan            ISipSessionListener listener) throws SipException {
3539c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        String key = localProfile.getUriString();
3549c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        SipSessionGroupExt group = mSipGroups.get(key);
3559c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        if (group != null) {
356a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan            if (!isCallerCreator(group)) {
357a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan                throw new SipException("only creator can access the profile");
358a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan            }
359845f7332f04864c5483b3e63da5db076fc7a888aHung-ying Tyan            group.setIncomingCallPendingIntent(incomingCallPendingIntent);
3609c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            group.setListener(listener);
3619c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        } else {
3629c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            group = new SipSessionGroupExt(localProfile,
363845f7332f04864c5483b3e63da5db076fc7a888aHung-ying Tyan                    incomingCallPendingIntent, listener);
3649c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mSipGroups.put(key, group);
3659c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            notifyProfileAdded(localProfile);
3669c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
3679c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        return group;
3689c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
3699c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
3709c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private void notifyProfileAdded(SipProfile localProfile) {
3719c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        if (DEBUG) Log.d(TAG, "notify: profile added: " + localProfile);
3729c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        Intent intent = new Intent(SipManager.ACTION_SIP_ADD_PHONE);
3739c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        intent.putExtra(SipManager.EXTRA_LOCAL_URI, localProfile.getUriString());
3749c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        mContext.sendBroadcast(intent);
3759c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
3769c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
3779c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private void notifyProfileRemoved(SipProfile localProfile) {
3789c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        if (DEBUG) Log.d(TAG, "notify: profile removed: " + localProfile);
3799c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        Intent intent = new Intent(SipManager.ACTION_SIP_REMOVE_PHONE);
3809c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        intent.putExtra(SipManager.EXTRA_LOCAL_URI, localProfile.getUriString());
3819c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        mContext.sendBroadcast(intent);
3829c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
3839c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
384617479363dcafe01bfa1fa025e1d9d122864a0ceHung-ying Tyan    private boolean anyOpenedToReceiveCalls() {
3859c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        for (SipSessionGroupExt group : mSipGroups.values()) {
386617479363dcafe01bfa1fa025e1d9d122864a0ceHung-ying Tyan            if (group.isOpenedToReceiveCalls()) return true;
3879c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
3889c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        return false;
3899c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
3909c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
3919c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private void grabWifiLock() {
3929c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        if (mWifiLock == null) {
3938a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan            if (DEBUG) Log.d(TAG, "acquire wifi lock");
3949c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mWifiLock = ((WifiManager)
3959c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    mContext.getSystemService(Context.WIFI_SERVICE))
3969c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    .createWifiLock(WifiManager.WIFI_MODE_FULL, TAG);
3979c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mWifiLock.acquire();
39887e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang            if (!mConnected) startWifiScanner();
3999c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
4009c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
4019c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
4029c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private void releaseWifiLock() {
4039c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        if (mWifiLock != null) {
4048a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan            if (DEBUG) Log.d(TAG, "release wifi lock");
4059c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mWifiLock.release();
4069c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mWifiLock = null;
40787e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang            stopWifiScanner();
40887e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang        }
40987e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang    }
41087e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang
41187e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang    private synchronized void startWifiScanner() {
41287e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang        if (mWifiScanProcess == null) {
41387e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang            mWifiScanProcess = new WifiScanProcess();
41487e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang        }
41587e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang        mWifiScanProcess.start();
41687e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang    }
41787e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang
41887e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang    private synchronized void stopWifiScanner() {
41987e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang        if (mWifiScanProcess != null) {
42087e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang            mWifiScanProcess.stop();
4219c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
4229c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
4239c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
4249c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private synchronized void onConnectivityChanged(
4259c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            String type, boolean connected) {
4269c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        if (DEBUG) Log.d(TAG, "onConnectivityChanged(): "
4279c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                + mNetworkType + (mConnected? " CONNECTED" : " DISCONNECTED")
4289c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                + " --> " + type + (connected? " CONNECTED" : " DISCONNECTED"));
4299c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
4309c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        boolean sameType = type.equals(mNetworkType);
4319c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        if (!sameType && !connected) return;
4329c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
4339c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        boolean wasWifi = "WIFI".equalsIgnoreCase(mNetworkType);
4349c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        boolean isWifi = "WIFI".equalsIgnoreCase(type);
4359c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        boolean wifiOff = (isWifi && !connected) || (wasWifi && !sameType);
4369c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        boolean wifiOn = isWifi && connected;
4379c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
4389c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        try {
4399c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            boolean wasConnected = mConnected;
4409c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mNetworkType = type;
4419c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mConnected = connected;
4429c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
4439c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (wasConnected) {
4449c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mLocalIp = null;
44537f93394fe5c7cfede3a780214fa99a9e2a69133Chia-chi Yeh                stopPortMappingMeasurement();
4469c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                for (SipSessionGroupExt group : mSipGroups.values()) {
4479c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    group.onConnectivityChanged(false);
4489c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                }
4499c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
4509c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
4519c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (connected) {
4529c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mLocalIp = determineLocalIp();
453469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang                mKeepAliveInterval = -1;
454cd9cb0845a0ad468bbbadadc133bc7a406e40b4aHung-ying Tyan                mLastGoodKeepAliveInterval = DEFAULT_KEEPALIVE_INTERVAL;
4559c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                for (SipSessionGroupExt group : mSipGroups.values()) {
4569c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    group.onConnectivityChanged(true);
4579c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                }
45887e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang                if (isWifi && (mWifiLock != null)) stopWifiScanner();
459257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan            } else {
460257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                mMyWakeLock.reset(); // in case there's a leak
46187e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang                if (isWifi && (mWifiLock != null)) startWifiScanner();
4629c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
4639c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        } catch (SipException e) {
4649c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            Log.e(TAG, "onConnectivityChanged()", e);
4659c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
4669c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
4679c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
468469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang    private void stopPortMappingMeasurement() {
469469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang        if (mIntervalMeasurementProcess != null) {
470469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang            mIntervalMeasurementProcess.stop();
471469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang            mIntervalMeasurementProcess = null;
472469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang        }
473469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang    }
474469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang
4758a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan    private void startPortMappingLifetimeMeasurement(
4768a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan            SipProfile localProfile) {
477cd9cb0845a0ad468bbbadadc133bc7a406e40b4aHung-ying Tyan        startPortMappingLifetimeMeasurement(localProfile,
478cd9cb0845a0ad468bbbadadc133bc7a406e40b4aHung-ying Tyan                DEFAULT_MAX_KEEPALIVE_INTERVAL);
47944ccfb03bb349a90546ddb3dc0063cbf4aaddaf1Hung-ying Tyan    }
48044ccfb03bb349a90546ddb3dc0063cbf4aaddaf1Hung-ying Tyan
48144ccfb03bb349a90546ddb3dc0063cbf4aaddaf1Hung-ying Tyan    private void startPortMappingLifetimeMeasurement(
48244ccfb03bb349a90546ddb3dc0063cbf4aaddaf1Hung-ying Tyan            SipProfile localProfile, int maxInterval) {
4838a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        if ((mIntervalMeasurementProcess == null)
4848a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                && (mKeepAliveInterval == -1)
4858a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                && isBehindNAT(mLocalIp)) {
4868a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan            Log.d(TAG, "start NAT port mapping timeout measurement on "
4878a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                    + localProfile.getUriString());
4888a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan
489cd9cb0845a0ad468bbbadadc133bc7a406e40b4aHung-ying Tyan            int minInterval = mLastGoodKeepAliveInterval;
490cd9cb0845a0ad468bbbadadc133bc7a406e40b4aHung-ying Tyan            if (minInterval >= maxInterval) {
491cd9cb0845a0ad468bbbadadc133bc7a406e40b4aHung-ying Tyan                // If mLastGoodKeepAliveInterval also does not work, reset it
492cd9cb0845a0ad468bbbadadc133bc7a406e40b4aHung-ying Tyan                // to the default min
493cd9cb0845a0ad468bbbadadc133bc7a406e40b4aHung-ying Tyan                minInterval = mLastGoodKeepAliveInterval
494cd9cb0845a0ad468bbbadadc133bc7a406e40b4aHung-ying Tyan                        = DEFAULT_KEEPALIVE_INTERVAL;
495cd9cb0845a0ad468bbbadadc133bc7a406e40b4aHung-ying Tyan                Log.d(TAG, "  reset min interval to " + minInterval);
496cd9cb0845a0ad468bbbadadc133bc7a406e40b4aHung-ying Tyan            }
497cd9cb0845a0ad468bbbadadc133bc7a406e40b4aHung-ying Tyan            mIntervalMeasurementProcess = new IntervalMeasurementProcess(
498cd9cb0845a0ad468bbbadadc133bc7a406e40b4aHung-ying Tyan                    localProfile, minInterval, maxInterval);
4998a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan            mIntervalMeasurementProcess.start();
5008a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        }
501469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang    }
502469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang
50344ccfb03bb349a90546ddb3dc0063cbf4aaddaf1Hung-ying Tyan    private void restartPortMappingLifetimeMeasurement(
50444ccfb03bb349a90546ddb3dc0063cbf4aaddaf1Hung-ying Tyan            SipProfile localProfile, int maxInterval) {
50544ccfb03bb349a90546ddb3dc0063cbf4aaddaf1Hung-ying Tyan        stopPortMappingMeasurement();
50644ccfb03bb349a90546ddb3dc0063cbf4aaddaf1Hung-ying Tyan        mKeepAliveInterval = -1;
50744ccfb03bb349a90546ddb3dc0063cbf4aaddaf1Hung-ying Tyan        startPortMappingLifetimeMeasurement(localProfile, maxInterval);
50844ccfb03bb349a90546ddb3dc0063cbf4aaddaf1Hung-ying Tyan    }
50944ccfb03bb349a90546ddb3dc0063cbf4aaddaf1Hung-ying Tyan
5109c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private synchronized void addPendingSession(ISipSession session) {
5119c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        try {
5123f4d44657ccad3ed02ed0e1354387b14b07dc011Hung-ying Tyan            cleanUpPendingSessions();
5139c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mPendingSessions.put(session.getCallId(), session);
5143f4d44657ccad3ed02ed0e1354387b14b07dc011Hung-ying Tyan            if (DEBUG) Log.d(TAG, "#pending sess=" + mPendingSessions.size());
5159c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        } catch (RemoteException e) {
5169c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            // should not happen with a local call
5179c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            Log.e(TAG, "addPendingSession()", e);
5189c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
5199c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
5209c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
5213f4d44657ccad3ed02ed0e1354387b14b07dc011Hung-ying Tyan    private void cleanUpPendingSessions() throws RemoteException {
5223f4d44657ccad3ed02ed0e1354387b14b07dc011Hung-ying Tyan        Map.Entry<String, ISipSession>[] entries =
5233f4d44657ccad3ed02ed0e1354387b14b07dc011Hung-ying Tyan                mPendingSessions.entrySet().toArray(
5243f4d44657ccad3ed02ed0e1354387b14b07dc011Hung-ying Tyan                new Map.Entry[mPendingSessions.size()]);
5253f4d44657ccad3ed02ed0e1354387b14b07dc011Hung-ying Tyan        for (Map.Entry<String, ISipSession> entry : entries) {
5263f4d44657ccad3ed02ed0e1354387b14b07dc011Hung-ying Tyan            if (entry.getValue().getState() != SipSession.State.INCOMING_CALL) {
5273f4d44657ccad3ed02ed0e1354387b14b07dc011Hung-ying Tyan                mPendingSessions.remove(entry.getKey());
5283f4d44657ccad3ed02ed0e1354387b14b07dc011Hung-ying Tyan            }
5293f4d44657ccad3ed02ed0e1354387b14b07dc011Hung-ying Tyan        }
5303f4d44657ccad3ed02ed0e1354387b14b07dc011Hung-ying Tyan    }
5313f4d44657ccad3ed02ed0e1354387b14b07dc011Hung-ying Tyan
532ed3c0fbbd5457f43ff72cec31b8ab0bb3f7a0047Hung-ying Tyan    private synchronized boolean callingSelf(SipSessionGroupExt ringingGroup,
533ed3c0fbbd5457f43ff72cec31b8ab0bb3f7a0047Hung-ying Tyan            SipSessionGroup.SipSessionImpl ringingSession) {
534ed3c0fbbd5457f43ff72cec31b8ab0bb3f7a0047Hung-ying Tyan        String callId = ringingSession.getCallId();
535ed3c0fbbd5457f43ff72cec31b8ab0bb3f7a0047Hung-ying Tyan        for (SipSessionGroupExt group : mSipGroups.values()) {
536ed3c0fbbd5457f43ff72cec31b8ab0bb3f7a0047Hung-ying Tyan            if ((group != ringingGroup) && group.containsSession(callId)) {
537ed3c0fbbd5457f43ff72cec31b8ab0bb3f7a0047Hung-ying Tyan                if (DEBUG) Log.d(TAG, "call self: "
538ed3c0fbbd5457f43ff72cec31b8ab0bb3f7a0047Hung-ying Tyan                        + ringingSession.getLocalProfile().getUriString()
539ed3c0fbbd5457f43ff72cec31b8ab0bb3f7a0047Hung-ying Tyan                        + " -> " + group.getLocalProfile().getUriString());
540ed3c0fbbd5457f43ff72cec31b8ab0bb3f7a0047Hung-ying Tyan                return true;
541ed3c0fbbd5457f43ff72cec31b8ab0bb3f7a0047Hung-ying Tyan            }
542ed3c0fbbd5457f43ff72cec31b8ab0bb3f7a0047Hung-ying Tyan        }
543ed3c0fbbd5457f43ff72cec31b8ab0bb3f7a0047Hung-ying Tyan        return false;
544ed3c0fbbd5457f43ff72cec31b8ab0bb3f7a0047Hung-ying Tyan    }
545ed3c0fbbd5457f43ff72cec31b8ab0bb3f7a0047Hung-ying Tyan
5468a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan    private synchronized void onKeepAliveIntervalChanged() {
5478a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        for (SipSessionGroupExt group : mSipGroups.values()) {
5488a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan            group.onKeepAliveIntervalChanged();
5498a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        }
5508a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan    }
5518a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan
5528a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan    private int getKeepAliveInterval() {
5538a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        return (mKeepAliveInterval < 0)
554cd9cb0845a0ad468bbbadadc133bc7a406e40b4aHung-ying Tyan                ? mLastGoodKeepAliveInterval
5558a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                : mKeepAliveInterval;
5568a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan    }
5578a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan
5588a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan    private boolean isBehindNAT(String address) {
5598a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        try {
5608a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan            byte[] d = InetAddress.getByName(address).getAddress();
5618a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan            if ((d[0] == 10) ||
5628a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                    (((0x000000FF & ((int)d[0])) == 172) &&
5638a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                    ((0x000000F0 & ((int)d[1])) == 16)) ||
5648a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                    (((0x000000FF & ((int)d[0])) == 192) &&
5658a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                    ((0x000000FF & ((int)d[1])) == 168))) {
5668a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                return true;
5678a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan            }
5688a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        } catch (UnknownHostException e) {
5698a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan            Log.e(TAG, "isBehindAT()" + address, e);
5708a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        }
5718a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        return false;
5728a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan    }
573ed3c0fbbd5457f43ff72cec31b8ab0bb3f7a0047Hung-ying Tyan
5749c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private class SipSessionGroupExt extends SipSessionAdapter {
5759c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private SipSessionGroup mSipGroup;
576845f7332f04864c5483b3e63da5db076fc7a888aHung-ying Tyan        private PendingIntent mIncomingCallPendingIntent;
577617479363dcafe01bfa1fa025e1d9d122864a0ceHung-ying Tyan        private boolean mOpenedToReceiveCalls;
5789c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
5799c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private AutoRegistrationProcess mAutoRegistration =
5809c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                new AutoRegistrationProcess();
5819c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
5829c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public SipSessionGroupExt(SipProfile localProfile,
583845f7332f04864c5483b3e63da5db076fc7a888aHung-ying Tyan                PendingIntent incomingCallPendingIntent,
5849c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                ISipSessionListener listener) throws SipException {
5859c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            String password = localProfile.getPassword();
5869c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            SipProfile p = duplicate(localProfile);
5879c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mSipGroup = createSipSessionGroup(mLocalIp, p, password);
588845f7332f04864c5483b3e63da5db076fc7a888aHung-ying Tyan            mIncomingCallPendingIntent = incomingCallPendingIntent;
5899c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mAutoRegistration.setListener(listener);
5909c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
5919c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
5929c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public SipProfile getLocalProfile() {
5939c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return mSipGroup.getLocalProfile();
5949c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
5959c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
596ed3c0fbbd5457f43ff72cec31b8ab0bb3f7a0047Hung-ying Tyan        public boolean containsSession(String callId) {
597ed3c0fbbd5457f43ff72cec31b8ab0bb3f7a0047Hung-ying Tyan            return mSipGroup.containsSession(callId);
598ed3c0fbbd5457f43ff72cec31b8ab0bb3f7a0047Hung-ying Tyan        }
599ed3c0fbbd5457f43ff72cec31b8ab0bb3f7a0047Hung-ying Tyan
6008a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        public void onKeepAliveIntervalChanged() {
6018a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan            mAutoRegistration.onKeepAliveIntervalChanged();
6028a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        }
6038a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan
6048a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        // TODO: remove this method once SipWakeupTimer can better handle variety
6058a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        // of timeout values
6068a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        void setWakeupTimer(SipWakeupTimer timer) {
6078a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan            mSipGroup.setWakeupTimer(timer);
6088a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        }
6098a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan
6109c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        // network connectivity is tricky because network can be disconnected
6119c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        // at any instant so need to deal with exceptions carefully even when
6129c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        // you think you are connected
6139c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private SipSessionGroup createSipSessionGroup(String localIp,
6149c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                SipProfile localProfile, String password) throws SipException {
6159c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            try {
616acedadc1967457ac2f8981c67f884fd8c0ee853cHung-ying Tyan                return new SipSessionGroup(localIp, localProfile, password,
6178a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                        mTimer, mMyWakeLock);
6189c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            } catch (IOException e) {
6199c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                // network disconnected
6209c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                Log.w(TAG, "createSipSessionGroup(): network disconnected?");
6219c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                if (localIp != null) {
6229c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    return createSipSessionGroup(null, localProfile, password);
6239c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                } else {
6249c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    // recursive
6256d9d99615a30de0675271553552c3c7b49311354Joe Onorato                    Log.wtf(TAG, "impossible! recursive!");
6269c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    throw new RuntimeException("createSipSessionGroup");
6279c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                }
6289c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
6299c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
6309c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
6319c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private SipProfile duplicate(SipProfile p) {
6329c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            try {
6339c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                return new SipProfile.Builder(p).setPassword("*").build();
6349c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            } catch (Exception e) {
6359c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                Log.wtf(TAG, "duplicate()", e);
6369c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                throw new RuntimeException("duplicate profile", e);
6379c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
6389c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
6399c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
6409c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public void setListener(ISipSessionListener listener) {
6419c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mAutoRegistration.setListener(listener);
6429c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
6439c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
644845f7332f04864c5483b3e63da5db076fc7a888aHung-ying Tyan        public void setIncomingCallPendingIntent(PendingIntent pIntent) {
645845f7332f04864c5483b3e63da5db076fc7a888aHung-ying Tyan            mIncomingCallPendingIntent = pIntent;
6469c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
6479c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
6489c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public void openToReceiveCalls() throws SipException {
649617479363dcafe01bfa1fa025e1d9d122864a0ceHung-ying Tyan            mOpenedToReceiveCalls = true;
6509c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (mConnected) {
6519c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mSipGroup.openToReceiveCalls(this);
6529c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mAutoRegistration.start(mSipGroup);
6539c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
6549c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (DEBUG) Log.d(TAG, "  openToReceiveCalls: " + getUri() + ": "
655845f7332f04864c5483b3e63da5db076fc7a888aHung-ying Tyan                    + mIncomingCallPendingIntent);
6569c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
6579c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
6589c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public void onConnectivityChanged(boolean connected)
6599c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                throws SipException {
6609c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mSipGroup.onConnectivityChanged();
6619c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (connected) {
6629c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                resetGroup(mLocalIp);
663617479363dcafe01bfa1fa025e1d9d122864a0ceHung-ying Tyan                if (mOpenedToReceiveCalls) openToReceiveCalls();
6649c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            } else {
665617479363dcafe01bfa1fa025e1d9d122864a0ceHung-ying Tyan                // close mSipGroup but remember mOpenedToReceiveCalls
6669c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                if (DEBUG) Log.d(TAG, "  close auto reg temporarily: "
667845f7332f04864c5483b3e63da5db076fc7a888aHung-ying Tyan                        + getUri() + ": " + mIncomingCallPendingIntent);
6689c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mSipGroup.close();
6699c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mAutoRegistration.stop();
6709c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
6719c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
6729c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
6739c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private void resetGroup(String localIp) throws SipException {
6749c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            try {
6759c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mSipGroup.reset(localIp);
6769c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            } catch (IOException e) {
6779c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                // network disconnected
6789c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                Log.w(TAG, "resetGroup(): network disconnected?");
6799c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                if (localIp != null) {
6809c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    resetGroup(null); // reset w/o local IP
6819c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                } else {
6829c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    // recursive
6839c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    Log.wtf(TAG, "impossible!");
6849c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    throw new RuntimeException("resetGroup");
6859c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                }
6869c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
6879c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
6889c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
6899c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public void close() {
690617479363dcafe01bfa1fa025e1d9d122864a0ceHung-ying Tyan            mOpenedToReceiveCalls = false;
6919c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mSipGroup.close();
6929c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mAutoRegistration.stop();
6939c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (DEBUG) Log.d(TAG, "   close: " + getUri() + ": "
694845f7332f04864c5483b3e63da5db076fc7a888aHung-ying Tyan                    + mIncomingCallPendingIntent);
6959c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
6969c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
6979c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public ISipSession createSession(ISipSessionListener listener) {
6989c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return mSipGroup.createSession(listener);
6999c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
7009c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
7019c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        @Override
702845f7332f04864c5483b3e63da5db076fc7a888aHung-ying Tyan        public void onRinging(ISipSession s, SipProfile caller,
7039c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                String sessionDescription) {
704acedadc1967457ac2f8981c67f884fd8c0ee853cHung-ying Tyan            if (DEBUGV) Log.d(TAG, "<<<<< onRinging()");
705845f7332f04864c5483b3e63da5db076fc7a888aHung-ying Tyan            SipSessionGroup.SipSessionImpl session =
706845f7332f04864c5483b3e63da5db076fc7a888aHung-ying Tyan                    (SipSessionGroup.SipSessionImpl) s;
7079c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            synchronized (SipService.this) {
7089c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                try {
709ed3c0fbbd5457f43ff72cec31b8ab0bb3f7a0047Hung-ying Tyan                    if (!isRegistered() || callingSelf(this, session)) {
7109c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        session.endCall();
7119c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        return;
7129c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    }
7139c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
7149c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    // send out incoming call broadcast
7159c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    addPendingSession(session);
7169c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    Intent intent = SipManager.createIncomingCallBroadcast(
717845f7332f04864c5483b3e63da5db076fc7a888aHung-ying Tyan                            session.getCallId(), sessionDescription);
7189c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    if (DEBUG) Log.d(TAG, " ringing~~ " + getUri() + ": "
7199c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            + caller.getUri() + ": " + session.getCallId()
720845f7332f04864c5483b3e63da5db076fc7a888aHung-ying Tyan                            + " " + mIncomingCallPendingIntent);
721845f7332f04864c5483b3e63da5db076fc7a888aHung-ying Tyan                    mIncomingCallPendingIntent.send(mContext,
722845f7332f04864c5483b3e63da5db076fc7a888aHung-ying Tyan                            SipManager.INCOMING_CALL_RESULT_CODE, intent);
723845f7332f04864c5483b3e63da5db076fc7a888aHung-ying Tyan                } catch (PendingIntent.CanceledException e) {
724845f7332f04864c5483b3e63da5db076fc7a888aHung-ying Tyan                    Log.w(TAG, "pendingIntent is canceled, drop incoming call");
725845f7332f04864c5483b3e63da5db076fc7a888aHung-ying Tyan                    session.endCall();
7269c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                }
7279c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
7289c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
7299c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
7309c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        @Override
7319c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public void onError(ISipSession session, int errorCode,
7329c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                String message) {
7339c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (DEBUG) Log.d(TAG, "sip session error: "
7349c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    + SipErrorCode.toString(errorCode) + ": " + message);
7359c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
7369c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
737617479363dcafe01bfa1fa025e1d9d122864a0ceHung-ying Tyan        public boolean isOpenedToReceiveCalls() {
738617479363dcafe01bfa1fa025e1d9d122864a0ceHung-ying Tyan            return mOpenedToReceiveCalls;
7399c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
7409c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
7419c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public boolean isRegistered() {
7429c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return mAutoRegistration.isRegistered();
7439c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
7449c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
7459c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private String getUri() {
7469c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return mSipGroup.getLocalProfileUri();
7479c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
7489c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
7499c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
75087e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang    private class WifiScanProcess implements Runnable {
75187e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang        private static final String TAG = "\\WIFI_SCAN/";
75287e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang        private static final int INTERVAL = 60;
75387e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang        private boolean mRunning = false;
75487e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang
75587e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang        private WifiManager mWifiManager;
75687e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang
75787e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang        public void start() {
75887e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang            if (mRunning) return;
75987e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang            mRunning = true;
76087e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang            mTimer.set(INTERVAL * 1000, this);
76187e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang        }
76287e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang
76387e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang        WifiScanProcess() {
76487e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang            mWifiManager = (WifiManager)
76587e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang                    mContext.getSystemService(Context.WIFI_SERVICE);
76687e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang        }
76787e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang
76887e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang        public void run() {
76987e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang            // scan and associate now
77087e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang            if (DEBUGV) Log.v(TAG, "just wake up here for wifi scanning...");
77187e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang            mWifiManager.startScanActive();
77287e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang        }
77387e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang
77487e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang        public void stop() {
77587e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang            mRunning = false;
77687e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang            mTimer.cancel(this);
77787e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang        }
77887e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang    }
77987e304ce2e55588dfbec18406bf910e5adcfa3a4Chung-yih Wang
7807d2904eaae91bd096eccef637dce430a41923424Hung-ying Tyan    private class IntervalMeasurementProcess implements Runnable,
7818a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan            SipSessionGroup.KeepAliveProcessCallback {
7828a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        private static final String TAG = "SipKeepAliveInterval";
7837d2904eaae91bd096eccef637dce430a41923424Hung-ying Tyan        private static final int MIN_INTERVAL = 5; // in seconds
7848a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        private static final int PASS_THRESHOLD = 10;
78544ccfb03bb349a90546ddb3dc0063cbf4aaddaf1Hung-ying Tyan        private static final int MAX_RETRY_COUNT = 5;
7867d2904eaae91bd096eccef637dce430a41923424Hung-ying Tyan        private static final int NAT_MEASUREMENT_RETRY_INTERVAL = 120; // in seconds
78737f93394fe5c7cfede3a780214fa99a9e2a69133Chia-chi Yeh        private SipProfile mLocalProfile;
788469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang        private SipSessionGroupExt mGroup;
789469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang        private SipSessionGroup.SipSessionImpl mSession;
790cd9cb0845a0ad468bbbadadc133bc7a406e40b4aHung-ying Tyan        private int mMinInterval;
79144ccfb03bb349a90546ddb3dc0063cbf4aaddaf1Hung-ying Tyan        private int mMaxInterval;
79244ccfb03bb349a90546ddb3dc0063cbf4aaddaf1Hung-ying Tyan        private int mInterval;
79337f93394fe5c7cfede3a780214fa99a9e2a69133Chia-chi Yeh        private int mPassCount;
79444ccfb03bb349a90546ddb3dc0063cbf4aaddaf1Hung-ying Tyan
795cd9cb0845a0ad468bbbadadc133bc7a406e40b4aHung-ying Tyan        public IntervalMeasurementProcess(SipProfile localProfile,
796cd9cb0845a0ad468bbbadadc133bc7a406e40b4aHung-ying Tyan                int minInterval, int maxInterval) {
797cd9cb0845a0ad468bbbadadc133bc7a406e40b4aHung-ying Tyan            mMaxInterval = maxInterval;
798cd9cb0845a0ad468bbbadadc133bc7a406e40b4aHung-ying Tyan            mMinInterval = minInterval;
79937f93394fe5c7cfede3a780214fa99a9e2a69133Chia-chi Yeh            mLocalProfile = localProfile;
800469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang        }
801469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang
802469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang        public void start() {
8038a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan            synchronized (SipService.this) {
80437f93394fe5c7cfede3a780214fa99a9e2a69133Chia-chi Yeh                if (mSession != null) {
80537f93394fe5c7cfede3a780214fa99a9e2a69133Chia-chi Yeh                    return;
8067d2904eaae91bd096eccef637dce430a41923424Hung-ying Tyan                }
80737f93394fe5c7cfede3a780214fa99a9e2a69133Chia-chi Yeh
80837f93394fe5c7cfede3a780214fa99a9e2a69133Chia-chi Yeh                mInterval = (mMaxInterval + mMinInterval) / 2;
80937f93394fe5c7cfede3a780214fa99a9e2a69133Chia-chi Yeh                mPassCount = 0;
81037f93394fe5c7cfede3a780214fa99a9e2a69133Chia-chi Yeh
81137f93394fe5c7cfede3a780214fa99a9e2a69133Chia-chi Yeh                // Don't start measurement if the interval is too small
81237f93394fe5c7cfede3a780214fa99a9e2a69133Chia-chi Yeh                if (mInterval < DEFAULT_KEEPALIVE_INTERVAL || checkTermination()) {
81337f93394fe5c7cfede3a780214fa99a9e2a69133Chia-chi Yeh                    Log.w(TAG, "measurement aborted; interval=[" +
81437f93394fe5c7cfede3a780214fa99a9e2a69133Chia-chi Yeh                            mMinInterval + "," + mMaxInterval + "]");
81537f93394fe5c7cfede3a780214fa99a9e2a69133Chia-chi Yeh                    return;
81637f93394fe5c7cfede3a780214fa99a9e2a69133Chia-chi Yeh                }
81737f93394fe5c7cfede3a780214fa99a9e2a69133Chia-chi Yeh
8188a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                try {
81937f93394fe5c7cfede3a780214fa99a9e2a69133Chia-chi Yeh                    Log.d(TAG, "start measurement w interval=" + mInterval);
82037f93394fe5c7cfede3a780214fa99a9e2a69133Chia-chi Yeh
82137f93394fe5c7cfede3a780214fa99a9e2a69133Chia-chi Yeh                    mGroup = new SipSessionGroupExt(mLocalProfile, null, null);
82237f93394fe5c7cfede3a780214fa99a9e2a69133Chia-chi Yeh                    // TODO: remove this line once SipWakeupTimer can better handle
82337f93394fe5c7cfede3a780214fa99a9e2a69133Chia-chi Yeh                    // variety of timeout values
82437f93394fe5c7cfede3a780214fa99a9e2a69133Chia-chi Yeh                    mGroup.setWakeupTimer(new SipWakeupTimer(mContext, mExecutor));
82537f93394fe5c7cfede3a780214fa99a9e2a69133Chia-chi Yeh
82637f93394fe5c7cfede3a780214fa99a9e2a69133Chia-chi Yeh                    mSession = (SipSessionGroup.SipSessionImpl)
82737f93394fe5c7cfede3a780214fa99a9e2a69133Chia-chi Yeh                            mGroup.createSession(null);
8288a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                    mSession.startKeepAliveProcess(mInterval, this);
82937f93394fe5c7cfede3a780214fa99a9e2a69133Chia-chi Yeh                } catch (Throwable t) {
83037f93394fe5c7cfede3a780214fa99a9e2a69133Chia-chi Yeh                    onError(SipErrorCode.CLIENT_ERROR, t.toString());
8318a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                }
8328a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan            }
833469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang        }
834469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang
835469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang        public void stop() {
8368a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan            synchronized (SipService.this) {
8377d2904eaae91bd096eccef637dce430a41923424Hung-ying Tyan                if (mSession != null) {
8387d2904eaae91bd096eccef637dce430a41923424Hung-ying Tyan                    mSession.stopKeepAliveProcess();
8397d2904eaae91bd096eccef637dce430a41923424Hung-ying Tyan                    mSession = null;
8407d2904eaae91bd096eccef637dce430a41923424Hung-ying Tyan                }
84137f93394fe5c7cfede3a780214fa99a9e2a69133Chia-chi Yeh                if (mGroup != null) {
84237f93394fe5c7cfede3a780214fa99a9e2a69133Chia-chi Yeh                    mGroup.close();
84337f93394fe5c7cfede3a780214fa99a9e2a69133Chia-chi Yeh                    mGroup = null;
84437f93394fe5c7cfede3a780214fa99a9e2a69133Chia-chi Yeh                }
8457d2904eaae91bd096eccef637dce430a41923424Hung-ying Tyan                mTimer.cancel(this);
846469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang            }
847469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang        }
848469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang
8498a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        private void restart() {
850469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang            synchronized (SipService.this) {
8517d2904eaae91bd096eccef637dce430a41923424Hung-ying Tyan                // Return immediately if the measurement process is stopped
8527d2904eaae91bd096eccef637dce430a41923424Hung-ying Tyan                if (mSession == null) return;
8537d2904eaae91bd096eccef637dce430a41923424Hung-ying Tyan
8547d2904eaae91bd096eccef637dce430a41923424Hung-ying Tyan                Log.d(TAG, "restart measurement w interval=" + mInterval);
855469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang                try {
8568a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                    mSession.stopKeepAliveProcess();
8577d2904eaae91bd096eccef637dce430a41923424Hung-ying Tyan                    mPassCount = 0;
8588a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                    mSession.startKeepAliveProcess(mInterval, this);
8598a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                } catch (SipException e) {
8608a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                    Log.e(TAG, "restart()", e);
861469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang                }
862469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang            }
863469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang        }
864469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang
865cd9cb0845a0ad468bbbadadc133bc7a406e40b4aHung-ying Tyan        private boolean checkTermination() {
866cd9cb0845a0ad468bbbadadc133bc7a406e40b4aHung-ying Tyan            return ((mMaxInterval - mMinInterval) < MIN_INTERVAL);
867cd9cb0845a0ad468bbbadadc133bc7a406e40b4aHung-ying Tyan        }
868cd9cb0845a0ad468bbbadadc133bc7a406e40b4aHung-ying Tyan
8698a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        // SipSessionGroup.KeepAliveProcessCallback
8708a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        @Override
8718a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        public void onResponse(boolean portChanged) {
8729c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            synchronized (SipService.this) {
8738a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                if (!portChanged) {
87444ccfb03bb349a90546ddb3dc0063cbf4aaddaf1Hung-ying Tyan                    if (++mPassCount != PASS_THRESHOLD) return;
8758a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                    // update the interval, since the current interval is good to
8768a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                    // keep the port mapping.
877cd9cb0845a0ad468bbbadadc133bc7a406e40b4aHung-ying Tyan                    if (mKeepAliveInterval > 0) {
878cd9cb0845a0ad468bbbadadc133bc7a406e40b4aHung-ying Tyan                        mLastGoodKeepAliveInterval = mKeepAliveInterval;
879cd9cb0845a0ad468bbbadadc133bc7a406e40b4aHung-ying Tyan                    }
8808a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                    mKeepAliveInterval = mMinInterval = mInterval;
8818a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                    if (DEBUG) {
8828a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                        Log.d(TAG, "measured good keepalive interval: "
8838a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                                + mKeepAliveInterval);
884257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                    }
8858a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                    onKeepAliveIntervalChanged();
8868a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                } else {
8878a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                    // Since the rport is changed, shorten the interval.
8888a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                    mMaxInterval = mInterval;
8898a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                }
890cd9cb0845a0ad468bbbadadc133bc7a406e40b4aHung-ying Tyan                if (checkTermination()) {
8918a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                    // update mKeepAliveInterval and stop measurement.
8928a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                    stop();
893cd9cb0845a0ad468bbbadadc133bc7a406e40b4aHung-ying Tyan                    // If all the measurements failed, we still set it to
894cd9cb0845a0ad468bbbadadc133bc7a406e40b4aHung-ying Tyan                    // mMinInterval; If mMinInterval still doesn't work, a new
895cd9cb0845a0ad468bbbadadc133bc7a406e40b4aHung-ying Tyan                    // measurement with min interval=DEFAULT_KEEPALIVE_INTERVAL
896cd9cb0845a0ad468bbbadadc133bc7a406e40b4aHung-ying Tyan                    // will be conducted.
8978a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                    mKeepAliveInterval = mMinInterval;
8988a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                    if (DEBUG) {
8998a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                        Log.d(TAG, "measured keepalive interval: "
9008a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                                + mKeepAliveInterval);
901469491e7807a46f31681d21c0ddae215f7891094Chung-yih Wang                    }
9028a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                } else {
9038a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                    // calculate the new interval and continue.
9048a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                    mInterval = (mMaxInterval + mMinInterval) / 2;
9058a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                    if (DEBUG) {
9068a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                        Log.d(TAG, "current interval: " + mKeepAliveInterval
9078a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                                + ", test new interval: " + mInterval);
9088a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                    }
9098a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                    restart();
9109c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                }
9119c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
9129c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
9139c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
9148a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        // SipSessionGroup.KeepAliveProcessCallback
9158a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        @Override
9168a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        public void onError(int errorCode, String description) {
9177d2904eaae91bd096eccef637dce430a41923424Hung-ying Tyan            Log.w(TAG, "interval measurement error: " + description);
9187d2904eaae91bd096eccef637dce430a41923424Hung-ying Tyan            restartLater();
9197d2904eaae91bd096eccef637dce430a41923424Hung-ying Tyan        }
9207d2904eaae91bd096eccef637dce430a41923424Hung-ying Tyan
9217d2904eaae91bd096eccef637dce430a41923424Hung-ying Tyan        // timeout handler
9227d2904eaae91bd096eccef637dce430a41923424Hung-ying Tyan        @Override
9237d2904eaae91bd096eccef637dce430a41923424Hung-ying Tyan        public void run() {
9247d2904eaae91bd096eccef637dce430a41923424Hung-ying Tyan            mTimer.cancel(this);
9257d2904eaae91bd096eccef637dce430a41923424Hung-ying Tyan            restart();
9267d2904eaae91bd096eccef637dce430a41923424Hung-ying Tyan        }
9277d2904eaae91bd096eccef637dce430a41923424Hung-ying Tyan
9287d2904eaae91bd096eccef637dce430a41923424Hung-ying Tyan        private void restartLater() {
9298a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan            synchronized (SipService.this) {
9307d2904eaae91bd096eccef637dce430a41923424Hung-ying Tyan                int interval = NAT_MEASUREMENT_RETRY_INTERVAL;
9317d2904eaae91bd096eccef637dce430a41923424Hung-ying Tyan                Log.d(TAG, "Retry measurement " + interval + "s later.");
9327d2904eaae91bd096eccef637dce430a41923424Hung-ying Tyan                mTimer.cancel(this);
9337d2904eaae91bd096eccef637dce430a41923424Hung-ying Tyan                mTimer.set(interval * 1000, this);
9348a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan            }
9359c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
9369c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
9379c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
9389c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private class AutoRegistrationProcess extends SipSessionAdapter
9398a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan            implements Runnable, SipSessionGroup.KeepAliveProcessCallback {
9407d2904eaae91bd096eccef637dce430a41923424Hung-ying Tyan        private static final int MIN_KEEPALIVE_SUCCESS_COUNT = 10;
9418a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        private String TAG = "SipAudoReg";
9427d2904eaae91bd096eccef637dce430a41923424Hung-ying Tyan
9439c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private SipSessionGroup.SipSessionImpl mSession;
9448a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        private SipSessionGroup.SipSessionImpl mKeepAliveSession;
9459c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private SipSessionListenerProxy mProxy = new SipSessionListenerProxy();
9469c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private int mBackoff = 1;
9479c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private boolean mRegistered;
9489c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private long mExpiryTime;
9499c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private int mErrorCode;
9509c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private String mErrorMessage;
951d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan        private boolean mRunning = false;
9529c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
9537d2904eaae91bd096eccef637dce430a41923424Hung-ying Tyan        private int mKeepAliveSuccessCount = 0;
9547d2904eaae91bd096eccef637dce430a41923424Hung-ying Tyan
9559c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private String getAction() {
9569c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return toString();
9579c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
9589c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
9599c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public void start(SipSessionGroup group) {
960d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan            if (!mRunning) {
961d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                mRunning = true;
9629c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mBackoff = 1;
9639c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mSession = (SipSessionGroup.SipSessionImpl)
9649c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        group.createSession(this);
9659c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                // return right away if no active network connection.
9669c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                if (mSession == null) return;
9679c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
9689c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                // start unregistration to clear up old registration at server
9699c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                // TODO: when rfc5626 is deployed, use reg-id and sip.instance
9709c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                // in registration to avoid adding duplicate entries to server
971257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                mMyWakeLock.acquire(mSession);
9729c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mSession.unregister();
9738a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                if (DEBUG) TAG = mSession.getLocalProfile().getUriString();
9748a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                if (DEBUG) Log.d(TAG, "start AutoRegistrationProcess");
9759c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
9769c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
9779c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
9787d2904eaae91bd096eccef637dce430a41923424Hung-ying Tyan        private void startKeepAliveProcess(int interval) {
9797d2904eaae91bd096eccef637dce430a41923424Hung-ying Tyan            Log.d(TAG, "start keepalive w interval=" + interval);
9807d2904eaae91bd096eccef637dce430a41923424Hung-ying Tyan            if (mKeepAliveSession == null) {
9817d2904eaae91bd096eccef637dce430a41923424Hung-ying Tyan                mKeepAliveSession = mSession.duplicate();
9827d2904eaae91bd096eccef637dce430a41923424Hung-ying Tyan            } else {
9837d2904eaae91bd096eccef637dce430a41923424Hung-ying Tyan                mKeepAliveSession.stopKeepAliveProcess();
9847d2904eaae91bd096eccef637dce430a41923424Hung-ying Tyan            }
9857d2904eaae91bd096eccef637dce430a41923424Hung-ying Tyan            try {
9867d2904eaae91bd096eccef637dce430a41923424Hung-ying Tyan                mKeepAliveSession.startKeepAliveProcess(interval, this);
9877d2904eaae91bd096eccef637dce430a41923424Hung-ying Tyan            } catch (SipException e) {
9887d2904eaae91bd096eccef637dce430a41923424Hung-ying Tyan                Log.e(TAG, "failed to start keepalive w interval=" + interval,
9897d2904eaae91bd096eccef637dce430a41923424Hung-ying Tyan                        e);
9907d2904eaae91bd096eccef637dce430a41923424Hung-ying Tyan            }
9917d2904eaae91bd096eccef637dce430a41923424Hung-ying Tyan        }
9927d2904eaae91bd096eccef637dce430a41923424Hung-ying Tyan
9937d2904eaae91bd096eccef637dce430a41923424Hung-ying Tyan        private void stopKeepAliveProcess() {
9947d2904eaae91bd096eccef637dce430a41923424Hung-ying Tyan            if (mKeepAliveSession != null) {
9957d2904eaae91bd096eccef637dce430a41923424Hung-ying Tyan                mKeepAliveSession.stopKeepAliveProcess();
9967d2904eaae91bd096eccef637dce430a41923424Hung-ying Tyan                mKeepAliveSession = null;
9977d2904eaae91bd096eccef637dce430a41923424Hung-ying Tyan            }
9987d2904eaae91bd096eccef637dce430a41923424Hung-ying Tyan            mKeepAliveSuccessCount = 0;
9997d2904eaae91bd096eccef637dce430a41923424Hung-ying Tyan        }
10007d2904eaae91bd096eccef637dce430a41923424Hung-ying Tyan
10018a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        // SipSessionGroup.KeepAliveProcessCallback
10028a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        @Override
10038a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        public void onResponse(boolean portChanged) {
10048a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan            synchronized (SipService.this) {
100544ccfb03bb349a90546ddb3dc0063cbf4aaddaf1Hung-ying Tyan                if (portChanged) {
10067d2904eaae91bd096eccef637dce430a41923424Hung-ying Tyan                    int interval = getKeepAliveInterval();
10077d2904eaae91bd096eccef637dce430a41923424Hung-ying Tyan                    if (mKeepAliveSuccessCount < MIN_KEEPALIVE_SUCCESS_COUNT) {
10087d2904eaae91bd096eccef637dce430a41923424Hung-ying Tyan                        Log.i(TAG, "keepalive doesn't work with interval "
10097d2904eaae91bd096eccef637dce430a41923424Hung-ying Tyan                                + interval + ", past success count="
10107d2904eaae91bd096eccef637dce430a41923424Hung-ying Tyan                                + mKeepAliveSuccessCount);
10117d2904eaae91bd096eccef637dce430a41923424Hung-ying Tyan                        if (interval > DEFAULT_KEEPALIVE_INTERVAL) {
10127d2904eaae91bd096eccef637dce430a41923424Hung-ying Tyan                            restartPortMappingLifetimeMeasurement(
10137d2904eaae91bd096eccef637dce430a41923424Hung-ying Tyan                                    mSession.getLocalProfile(), interval);
10147d2904eaae91bd096eccef637dce430a41923424Hung-ying Tyan                            mKeepAliveSuccessCount = 0;
10157d2904eaae91bd096eccef637dce430a41923424Hung-ying Tyan                        }
10167d2904eaae91bd096eccef637dce430a41923424Hung-ying Tyan                    } else {
10177d2904eaae91bd096eccef637dce430a41923424Hung-ying Tyan                        Log.i(TAG, "keep keepalive going with interval "
10187d2904eaae91bd096eccef637dce430a41923424Hung-ying Tyan                                + interval + ", past success count="
10197d2904eaae91bd096eccef637dce430a41923424Hung-ying Tyan                                + mKeepAliveSuccessCount);
10207d2904eaae91bd096eccef637dce430a41923424Hung-ying Tyan                        mKeepAliveSuccessCount /= 2;
10217d2904eaae91bd096eccef637dce430a41923424Hung-ying Tyan                    }
102244ccfb03bb349a90546ddb3dc0063cbf4aaddaf1Hung-ying Tyan                } else {
102344ccfb03bb349a90546ddb3dc0063cbf4aaddaf1Hung-ying Tyan                    // Start keep-alive interval measurement on the first
102444ccfb03bb349a90546ddb3dc0063cbf4aaddaf1Hung-ying Tyan                    // successfully kept-alive SipSessionGroup
102544ccfb03bb349a90546ddb3dc0063cbf4aaddaf1Hung-ying Tyan                    startPortMappingLifetimeMeasurement(
102644ccfb03bb349a90546ddb3dc0063cbf4aaddaf1Hung-ying Tyan                            mSession.getLocalProfile());
10277d2904eaae91bd096eccef637dce430a41923424Hung-ying Tyan                    mKeepAliveSuccessCount++;
102844ccfb03bb349a90546ddb3dc0063cbf4aaddaf1Hung-ying Tyan                }
10298a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan
10308a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                if (!mRunning || !portChanged) return;
10319dbe2c72276b9968593054dcfd39fd5135a574aaHung-ying Tyan
10329dbe2c72276b9968593054dcfd39fd5135a574aaHung-ying Tyan                // The keep alive process is stopped when port is changed;
10339dbe2c72276b9968593054dcfd39fd5135a574aaHung-ying Tyan                // Nullify the session so that the process can be restarted
10349dbe2c72276b9968593054dcfd39fd5135a574aaHung-ying Tyan                // again when the re-registration is done
10359dbe2c72276b9968593054dcfd39fd5135a574aaHung-ying Tyan                mKeepAliveSession = null;
10369dbe2c72276b9968593054dcfd39fd5135a574aaHung-ying Tyan
10378a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                // Acquire wake lock for the registration process. The
10388a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                // lock will be released when registration is complete.
10398a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                mMyWakeLock.acquire(mSession);
10408a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                mSession.register(EXPIRY_TIME);
10418a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan            }
10428a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        }
10438a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan
10448a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        // SipSessionGroup.KeepAliveProcessCallback
10458a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        @Override
10468a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        public void onError(int errorCode, String description) {
10478a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan            Log.e(TAG, "keepalive error: " + description);
104844ccfb03bb349a90546ddb3dc0063cbf4aaddaf1Hung-ying Tyan            onResponse(true); // re-register immediately
10498a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        }
10508a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan
10519c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public void stop() {
1052d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan            if (!mRunning) return;
1053d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan            mRunning = false;
1054257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan            mMyWakeLock.release(mSession);
1055257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan            if (mSession != null) {
1056257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                mSession.setListener(null);
1057257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                if (mConnected && mRegistered) mSession.unregister();
1058257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan            }
1059d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan
10609c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mTimer.cancel(this);
10617d2904eaae91bd096eccef637dce430a41923424Hung-ying Tyan            stopKeepAliveProcess();
10629c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
1063d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan            mRegistered = false;
1064d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan            setListener(mProxy.getListener());
10659c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
10669c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
10678a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        public void onKeepAliveIntervalChanged() {
10688a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan            if (mKeepAliveSession != null) {
10698a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                int newInterval = getKeepAliveInterval();
10708a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                if (DEBUGV) {
10718a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                    Log.v(TAG, "restart keepalive w interval=" + newInterval);
10728a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                }
10737d2904eaae91bd096eccef637dce430a41923424Hung-ying Tyan                mKeepAliveSuccessCount = 0;
10747d2904eaae91bd096eccef637dce430a41923424Hung-ying Tyan                startKeepAliveProcess(newInterval);
10758a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan            }
10768a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        }
10778a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan
10789c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public void setListener(ISipSessionListener listener) {
10799c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            synchronized (SipService.this) {
10809c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mProxy.setListener(listener);
10819c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
10829c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                try {
10839c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    int state = (mSession == null)
10849c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            ? SipSession.State.READY_TO_CALL
10859c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            : mSession.getState();
10869c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    if ((state == SipSession.State.REGISTERING)
10879c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            || (state == SipSession.State.DEREGISTERING)) {
10889c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        mProxy.onRegistering(mSession);
10899c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    } else if (mRegistered) {
10909c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        int duration = (int)
10919c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                                (mExpiryTime - SystemClock.elapsedRealtime());
10929c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        mProxy.onRegistrationDone(mSession, duration);
10939c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    } else if (mErrorCode != SipErrorCode.NO_ERROR) {
10949c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        if (mErrorCode == SipErrorCode.TIME_OUT) {
10959c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            mProxy.onRegistrationTimeout(mSession);
10969c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        } else {
10979c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            mProxy.onRegistrationFailed(mSession, mErrorCode,
10989c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                                    mErrorMessage);
10999c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        }
1100d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                    } else if (!mConnected) {
1101d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                        mProxy.onRegistrationFailed(mSession,
1102d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                                SipErrorCode.DATA_CONNECTION_LOST,
1103d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                                "no data connection");
1104d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                    } else if (!mRunning) {
1105d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                        mProxy.onRegistrationFailed(mSession,
1106d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                                SipErrorCode.CLIENT_ERROR,
1107d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                                "registration not running");
1108d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                    } else {
1109d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                        mProxy.onRegistrationFailed(mSession,
1110d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                                SipErrorCode.IN_PROGRESS,
1111d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                                String.valueOf(state));
11129c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    }
11139c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                } catch (Throwable t) {
11149c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    Log.w(TAG, "setListener(): " + t);
11159c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                }
11169c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
11179c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
11189c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
11199c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public boolean isRegistered() {
11209c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return mRegistered;
11219c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
11229c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
1123257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan        // timeout handler: re-register
11248a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        @Override
11259c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public void run() {
1126d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan            synchronized (SipService.this) {
1127d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                if (!mRunning) return;
1128d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan
1129d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                mErrorCode = SipErrorCode.NO_ERROR;
1130d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                mErrorMessage = null;
11318a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                if (DEBUG) Log.d(TAG, "registering");
1132257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                if (mConnected) {
1133257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                    mMyWakeLock.acquire(mSession);
1134257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                    mSession.register(EXPIRY_TIME);
1135257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                }
11369c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
11379c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
11389c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
11399c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private void restart(int duration) {
11409c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (DEBUG) Log.d(TAG, "Refresh registration " + duration + "s later.");
11419c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mTimer.cancel(this);
11429c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mTimer.set(duration * 1000, this);
11439c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
11449c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
11459c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private int backoffDuration() {
11469c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            int duration = SHORT_EXPIRY_TIME * mBackoff;
11479c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (duration > 3600) {
11489c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                duration = 3600;
11499c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            } else {
11509c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mBackoff *= 2;
11519c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
11529c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return duration;
11539c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
11549c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
11559c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        @Override
11569c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public void onRegistering(ISipSession session) {
11579c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (DEBUG) Log.d(TAG, "onRegistering(): " + session);
11589c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            synchronized (SipService.this) {
1159d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                if (notCurrentSession(session)) return;
1160d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan
11619c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mRegistered = false;
11629c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mProxy.onRegistering(session);
11639c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
11649c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
11659c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
1166d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan        private boolean notCurrentSession(ISipSession session) {
1167d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan            if (session != mSession) {
1168d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                ((SipSessionGroup.SipSessionImpl) session).setListener(null);
1169257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                mMyWakeLock.release(session);
1170d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                return true;
1171d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan            }
1172d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan            return !mRunning;
1173d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan        }
1174d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan
11759c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        @Override
11769c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public void onRegistrationDone(ISipSession session, int duration) {
11779c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (DEBUG) Log.d(TAG, "onRegistrationDone(): " + session);
11789c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            synchronized (SipService.this) {
1179d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                if (notCurrentSession(session)) return;
11809c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
11819c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mProxy.onRegistrationDone(session, duration);
11829c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
11839c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                if (duration > 0) {
11849c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    mExpiryTime = SystemClock.elapsedRealtime()
11859c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            + (duration * 1000);
11869c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
11879c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    if (!mRegistered) {
11889c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        mRegistered = true;
11899c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        // allow some overlap to avoid call drop during renew
11909c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        duration -= MIN_EXPIRY_TIME;
11919c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        if (duration < MIN_EXPIRY_TIME) {
11929c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            duration = MIN_EXPIRY_TIME;
11939c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        }
11949c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        restart(duration);
11959c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
11968a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                        SipProfile localProfile = mSession.getLocalProfile();
11978a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                        if ((mKeepAliveSession == null) && (isBehindNAT(mLocalIp)
11988a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                                || localProfile.getSendKeepAlive())) {
11997d2904eaae91bd096eccef637dce430a41923424Hung-ying Tyan                            startKeepAliveProcess(getKeepAliveInterval());
12009c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        }
12019c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    }
1202257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                    mMyWakeLock.release(session);
12039c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                } else {
12049c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    mRegistered = false;
12059c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    mExpiryTime = -1L;
12069c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    if (DEBUG) Log.d(TAG, "Refresh registration immediately");
12079c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    run();
12089c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                }
12099c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
12109c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
12119c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
12129c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        @Override
12139c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public void onRegistrationFailed(ISipSession session, int errorCode,
12149c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                String message) {
12159c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (DEBUG) Log.d(TAG, "onRegistrationFailed(): " + session + ": "
12169c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    + SipErrorCode.toString(errorCode) + ": " + message);
12179c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            synchronized (SipService.this) {
1218d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                if (notCurrentSession(session)) return;
12199c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
1220fcc474636f42243cfce960158373b9c49636a556Hung-ying Tyan                switch (errorCode) {
1221fcc474636f42243cfce960158373b9c49636a556Hung-ying Tyan                    case SipErrorCode.INVALID_CREDENTIALS:
1222fcc474636f42243cfce960158373b9c49636a556Hung-ying Tyan                    case SipErrorCode.SERVER_UNREACHABLE:
1223fcc474636f42243cfce960158373b9c49636a556Hung-ying Tyan                        if (DEBUG) Log.d(TAG, "   pause auto-registration");
1224fcc474636f42243cfce960158373b9c49636a556Hung-ying Tyan                        stop();
12250b88a073712b1510db7c636f89c4e19f0131449aHung-ying Tyan                        break;
1226fcc474636f42243cfce960158373b9c49636a556Hung-ying Tyan                    default:
1227fcc474636f42243cfce960158373b9c49636a556Hung-ying Tyan                        restartLater();
12289c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                }
1229d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan
1230d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                mErrorCode = errorCode;
1231d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                mErrorMessage = message;
1232d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                mProxy.onRegistrationFailed(session, errorCode, message);
1233257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                mMyWakeLock.release(session);
12349c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
12359c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
12369c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
12379c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        @Override
12389c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public void onRegistrationTimeout(ISipSession session) {
12399c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (DEBUG) Log.d(TAG, "onRegistrationTimeout(): " + session);
12409c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            synchronized (SipService.this) {
1241d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                if (notCurrentSession(session)) return;
1242d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan
12439c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mErrorCode = SipErrorCode.TIME_OUT;
12449c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mProxy.onRegistrationTimeout(session);
1245fcc474636f42243cfce960158373b9c49636a556Hung-ying Tyan                restartLater();
1246257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                mMyWakeLock.release(session);
12479c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
12489c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
12499c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
1250fcc474636f42243cfce960158373b9c49636a556Hung-ying Tyan        private void restartLater() {
12519c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mRegistered = false;
12529c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            restart(backoffDuration());
12539c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
12549c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
12559c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
12569c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private class ConnectivityReceiver extends BroadcastReceiver {
12579c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private Timer mTimer = new Timer();
12589c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private MyTimerTask mTask;
12599c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
12609c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        @Override
1261257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan        public void onReceive(final Context context, final Intent intent) {
1262257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan            // Run the handler in MyExecutor to be protected by wake lock
12634cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan            mExecutor.execute(new Runnable() {
1264257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                public void run() {
1265257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                    onReceiveInternal(context, intent);
1266257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                }
1267257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan            });
1268257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan        }
1269257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan
1270257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan        private void onReceiveInternal(Context context, Intent intent) {
12719c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            String action = intent.getAction();
12729c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
12739c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                Bundle b = intent.getExtras();
12749c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                if (b != null) {
12759c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    NetworkInfo netInfo = (NetworkInfo)
12769c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            b.get(ConnectivityManager.EXTRA_NETWORK_INFO);
12779c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    String type = netInfo.getTypeName();
12789c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    NetworkInfo.State state = netInfo.getState();
12799c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
12809c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    if (mWifiOnly && (netInfo.getType() !=
12819c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            ConnectivityManager.TYPE_WIFI)) {
12829c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        if (DEBUG) {
12839c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            Log.d(TAG, "Wifi only, other connectivity ignored: "
12849c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                                    + type);
12859c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        }
12869c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        return;
12879c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    }
12889c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
12899c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    NetworkInfo activeNetInfo = getActiveNetworkInfo();
12909c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    if (DEBUG) {
12919c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        if (activeNetInfo != null) {
12929c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            Log.d(TAG, "active network: "
12939c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                                    + activeNetInfo.getTypeName()
12949c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                                    + ((activeNetInfo.getState() == NetworkInfo.State.CONNECTED)
12959c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                                            ? " CONNECTED" : " DISCONNECTED"));
12969c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        } else {
12979c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            Log.d(TAG, "active network: null");
12989c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        }
12999c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    }
13009c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    if ((state == NetworkInfo.State.CONNECTED)
13019c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            && (activeNetInfo != null)
13029c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            && (activeNetInfo.getType() != netInfo.getType())) {
13039c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        if (DEBUG) Log.d(TAG, "ignore connect event: " + type
13049c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                                + ", active: " + activeNetInfo.getTypeName());
13059c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        return;
13069c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    }
13079c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
13089c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    if (state == NetworkInfo.State.CONNECTED) {
13099c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        if (DEBUG) Log.d(TAG, "Connectivity alert: CONNECTED " + type);
13109c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        onChanged(type, true);
13119c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    } else if (state == NetworkInfo.State.DISCONNECTED) {
13129c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        if (DEBUG) Log.d(TAG, "Connectivity alert: DISCONNECTED " + type);
13139c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        onChanged(type, false);
13149c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    } else {
13159c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        if (DEBUG) Log.d(TAG, "Connectivity alert not processed: "
13169c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                                + state + " " + type);
13179c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    }
13189c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                }
13199c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
13209c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
13219c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
13229c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private NetworkInfo getActiveNetworkInfo() {
13239c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            ConnectivityManager cm = (ConnectivityManager)
13249c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
13259c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return cm.getActiveNetworkInfo();
13269c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
13279c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
13289c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private void onChanged(String type, boolean connected) {
13299c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            synchronized (SipService.this) {
13309c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                // When turning on WIFI, it needs some time for network
13319c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                // connectivity to get stabile so we defer good news (because
13329c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                // we want to skip the interim ones) but deliver bad news
13339c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                // immediately
13349c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                if (connected) {
1335f4a38e8e2f83402d2307edf67ef79b6f74990c8bHung-ying Tyan                    if (mTask != null) {
1336f4a38e8e2f83402d2307edf67ef79b6f74990c8bHung-ying Tyan                        mTask.cancel();
1337f4a38e8e2f83402d2307edf67ef79b6f74990c8bHung-ying Tyan                        mMyWakeLock.release(mTask);
1338f4a38e8e2f83402d2307edf67ef79b6f74990c8bHung-ying Tyan                    }
13399c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    mTask = new MyTimerTask(type, connected);
13409c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    mTimer.schedule(mTask, 2 * 1000L);
1341257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                    // hold wakup lock so that we can finish changes before the
1342257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                    // device goes to sleep
1343257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                    mMyWakeLock.acquire(mTask);
13449c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                } else {
13459c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    if ((mTask != null) && mTask.mNetworkType.equals(type)) {
13469c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        mTask.cancel();
1347257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                        mMyWakeLock.release(mTask);
13489c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    }
13499c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    onConnectivityChanged(type, false);
13509c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                }
13519c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
13529c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
13539c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
13549c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private class MyTimerTask extends TimerTask {
13559c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            private boolean mConnected;
13569c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            private String mNetworkType;
13579c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
13589c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            public MyTimerTask(String type, boolean connected) {
13599c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mNetworkType = type;
13609c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mConnected = connected;
13619c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
13629c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
1363d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan            // timeout handler
13649c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            @Override
13659c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            public void run() {
13669c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                // delegate to mExecutor
13674cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan                mExecutor.execute(new Runnable() {
13689c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    public void run() {
13699c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        realRun();
13709c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    }
13719c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                });
13729c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
13739c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
13749c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            private void realRun() {
13759c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                synchronized (SipService.this) {
13769c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    if (mTask != this) {
13779c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        Log.w(TAG, "  unexpected task: " + mNetworkType
13789c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                                + (mConnected ? " CONNECTED" : "DISCONNECTED"));
1379f4a38e8e2f83402d2307edf67ef79b6f74990c8bHung-ying Tyan                        mMyWakeLock.release(this);
13809c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        return;
13819c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    }
13829c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    mTask = null;
13839c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    if (DEBUG) Log.d(TAG, " deliver change for " + mNetworkType
13849c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            + (mConnected ? " CONNECTED" : "DISCONNECTED"));
13859c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    onConnectivityChanged(mNetworkType, mConnected);
1386257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                    mMyWakeLock.release(this);
13879c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                }
13889c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
13899c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
13909c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
13919c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
1392257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan    private static Looper createLooper() {
1393257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan        HandlerThread thread = new HandlerThread("SipService.Executor");
1394257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan        thread.start();
1395257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan        return thread.getLooper();
1396257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan    }
1397257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan
1398257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan    // Executes immediate tasks in a single thread.
1399257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan    // Hold/release wake lock for running tasks
14004cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan    private class MyExecutor extends Handler implements Executor {
14019c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        MyExecutor() {
14029c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            super(createLooper());
14039c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
14049c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
14054cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        @Override
14064cbf2d2f94aaa094dda5b93357c93234ebccb120Hung-ying Tyan        public void execute(Runnable task) {
1407257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan            mMyWakeLock.acquire(task);
14089c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            Message.obtain(this, 0/* don't care */, task).sendToTarget();
14099c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
14109c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
14119c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        @Override
14129c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public void handleMessage(Message msg) {
14139c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (msg.obj instanceof Runnable) {
1414257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                executeInternal((Runnable) msg.obj);
14159c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            } else {
14169c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                Log.w(TAG, "can't handle msg: " + msg);
14179c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
14189c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
1419257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan
1420257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan        private void executeInternal(Runnable task) {
1421257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan            try {
1422257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                task.run();
1423257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan            } catch (Throwable t) {
1424257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                Log.e(TAG, "run task: " + task, t);
1425257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan            } finally {
1426257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                mMyWakeLock.release(task);
1427257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan            }
1428257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan        }
1429257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan    }
14309c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan}
1431