12d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang/*
22d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang * Copyright (C) 2010, The Android Open Source Project
32d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang *
42d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang * Licensed under the Apache License, Version 2.0 (the "License");
52d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang * you may not use this file except in compliance with the License.
62d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang * You may obtain a copy of the License at
72d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang *
82d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang *     http://www.apache.org/licenses/LICENSE-2.0
92d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang *
102d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang * Unless required by applicable law or agreed to in writing, software
112d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang * distributed under the License is distributed on an "AS IS" BASIS,
122d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
132d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang * See the License for the specific language governing permissions and
142d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang * limitations under the License.
152d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang */
162d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
172d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangpackage com.android.server.sip;
182d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
192d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport android.app.AlarmManager;
202d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport android.app.PendingIntent;
212d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport android.content.BroadcastReceiver;
222d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport android.content.Context;
232d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport android.content.Intent;
242d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport android.content.IntentFilter;
252d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport android.net.ConnectivityManager;
262d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport android.net.NetworkInfo;
272d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport android.net.sip.ISipService;
282d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport android.net.sip.ISipSession;
292d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport android.net.sip.ISipSessionListener;
303d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyanimport android.net.sip.SipErrorCode;
312d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport android.net.sip.SipManager;
322d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport android.net.sip.SipProfile;
3384a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyanimport android.net.sip.SipSession;
342d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport android.net.sip.SipSessionAdapter;
352d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport android.net.wifi.WifiManager;
365424c8dcacf1c227fe7deb0185510614122ab447Chung-yih Wangimport android.os.Binder;
372d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport android.os.Bundle;
38b17eae9e227475a323f61519abc8a7d35ddf8828Hung-ying Tyanimport android.os.Handler;
39b17eae9e227475a323f61519abc8a7d35ddf8828Hung-ying Tyanimport android.os.HandlerThread;
40b17eae9e227475a323f61519abc8a7d35ddf8828Hung-ying Tyanimport android.os.Looper;
41b17eae9e227475a323f61519abc8a7d35ddf8828Hung-ying Tyanimport android.os.Message;
42bd57eeafe034cf850225db403700b5dc5db5ebccHung-ying Tyanimport android.os.PowerManager;
436a53489ae594d7cc373a00687d6ea2f23d0634dfHung-ying Tyanimport android.os.Process;
442d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport android.os.RemoteException;
457e54ef71db3320a751571bba5259fba816399421Hung-ying Tyanimport android.os.ServiceManager;
462d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport android.os.SystemClock;
472d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport android.text.TextUtils;
482d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport android.util.Log;
492d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
502d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport java.io.IOException;
512d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport java.net.DatagramSocket;
522d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport java.net.InetAddress;
532d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport java.net.UnknownHostException;
546a53489ae594d7cc373a00687d6ea2f23d0634dfHung-ying Tyanimport java.util.ArrayList;
552d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport java.util.Collection;
562d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport java.util.Comparator;
572d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport java.util.HashMap;
582d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport java.util.Iterator;
592d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport java.util.Map;
602d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport java.util.Timer;
612d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport java.util.TimerTask;
622d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport java.util.TreeSet;
635621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyanimport java.util.concurrent.Executor;
642d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.SipException;
652d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
6695b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh/**
6795b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh * @hide
6895b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh */
692d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangpublic final class SipService extends ISipService.Stub {
7028f63c06894b9ca9252f43bc54a098c0a785d4b4Hung-ying Tyan    static final String TAG = "SipService";
71cb6ee06f62c20ae036a206667097f20b837b11abChia-chi Yeh    static final boolean DEBUG = false;
722d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private static final int EXPIRY_TIME = 3600;
732d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private static final int SHORT_EXPIRY_TIME = 10;
742d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private static final int MIN_EXPIRY_TIME = 60;
754a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan    private static final int DEFAULT_KEEPALIVE_INTERVAL = 10; // in seconds
769edfa107575b5905c9ae0a2fa0d6f0cc19595300Hung-ying Tyan    private static final int DEFAULT_MAX_KEEPALIVE_INTERVAL = 120; // in seconds
772d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
782d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private Context mContext;
792d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private String mLocalIp;
80c2bd6162eddad0cdfdafc037142e043680ffa705Chia-chi Yeh    private int mNetworkType = -1;
815621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan    private SipWakeupTimer mTimer;
822d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private WifiManager.WifiLock mWifiLock;
83ee59e6a9fc69241b286acb7b55a22b8393c81222Chia-chi Yeh    private boolean mSipOnWifiOnly;
84f89654dd2847cc574dfa6a44806289f7e69e17b7Hung-ying Tyan
85bb0a989c17cd6135c8d9c8566507521d4d927fe0Chung-yih Wang    private IntervalMeasurementProcess mIntervalMeasurementProcess;
862d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
875621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan    private MyExecutor mExecutor = new MyExecutor();
88b17eae9e227475a323f61519abc8a7d35ddf8828Hung-ying Tyan
892d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    // SipProfile URI --> group
902d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private Map<String, SipSessionGroupExt> mSipGroups =
912d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            new HashMap<String, SipSessionGroupExt>();
922d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
932d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    // session ID --> session
942d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private Map<String, ISipSession> mPendingSessions =
952d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            new HashMap<String, ISipSession>();
962d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
972d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private ConnectivityReceiver mConnectivityReceiver;
9828f63c06894b9ca9252f43bc54a098c0a785d4b4Hung-ying Tyan    private SipWakeLock mMyWakeLock;
99bb0a989c17cd6135c8d9c8566507521d4d927fe0Chung-yih Wang    private int mKeepAliveInterval;
1009edfa107575b5905c9ae0a2fa0d6f0cc19595300Hung-ying Tyan    private int mLastGoodKeepAliveInterval = DEFAULT_KEEPALIVE_INTERVAL;
1012d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
1023424c02e6b931a8bbd651ae75217bebd008b2605Hung-ying Tyan    /**
1037e54ef71db3320a751571bba5259fba816399421Hung-ying Tyan     * Starts the SIP service. Do nothing if the SIP API is not supported on the
1047e54ef71db3320a751571bba5259fba816399421Hung-ying Tyan     * device.
1053424c02e6b931a8bbd651ae75217bebd008b2605Hung-ying Tyan     */
1067e54ef71db3320a751571bba5259fba816399421Hung-ying Tyan    public static void start(Context context) {
1077e54ef71db3320a751571bba5259fba816399421Hung-ying Tyan        if (SipManager.isApiSupported(context)) {
1087e54ef71db3320a751571bba5259fba816399421Hung-ying Tyan            ServiceManager.addService("sip", new SipService(context));
1099db99a4dc10ac0d5d3751f03ea51c0fed217d2f8Hung-ying Tyan            context.sendBroadcast(new Intent(SipManager.ACTION_SIP_SERVICE_UP));
110e9b54077274d0c4066093cd90dabca59b3d9a157Hung-ying Tyan            if (DEBUG) Log.d(TAG, "SIP service started");
1117e54ef71db3320a751571bba5259fba816399421Hung-ying Tyan        }
1123424c02e6b931a8bbd651ae75217bebd008b2605Hung-ying Tyan    }
1133424c02e6b931a8bbd651ae75217bebd008b2605Hung-ying Tyan
1143424c02e6b931a8bbd651ae75217bebd008b2605Hung-ying Tyan    private SipService(Context context) {
115c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan        if (DEBUG) Log.d(TAG, " service started!");
1162d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        mContext = context;
1172d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        mConnectivityReceiver = new ConnectivityReceiver();
118ee59e6a9fc69241b286acb7b55a22b8393c81222Chia-chi Yeh
119ee59e6a9fc69241b286acb7b55a22b8393c81222Chia-chi Yeh        mWifiLock = ((WifiManager)
120ee59e6a9fc69241b286acb7b55a22b8393c81222Chia-chi Yeh                context.getSystemService(Context.WIFI_SERVICE))
121ee59e6a9fc69241b286acb7b55a22b8393c81222Chia-chi Yeh                .createWifiLock(WifiManager.WIFI_MODE_FULL, TAG);
122ee59e6a9fc69241b286acb7b55a22b8393c81222Chia-chi Yeh        mWifiLock.setReferenceCounted(false);
123ee59e6a9fc69241b286acb7b55a22b8393c81222Chia-chi Yeh        mSipOnWifiOnly = SipManager.isSipWifiOnly(context);
124ee59e6a9fc69241b286acb7b55a22b8393c81222Chia-chi Yeh
12528f63c06894b9ca9252f43bc54a098c0a785d4b4Hung-ying Tyan        mMyWakeLock = new SipWakeLock((PowerManager)
126bd57eeafe034cf850225db403700b5dc5db5ebccHung-ying Tyan                context.getSystemService(Context.POWER_SERVICE));
1272d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
1285621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        mTimer = new SipWakeupTimer(context, mExecutor);
129e9b54077274d0c4066093cd90dabca59b3d9a157Hung-ying Tyan    }
130e9b54077274d0c4066093cd90dabca59b3d9a157Hung-ying Tyan
1312d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    public synchronized SipProfile[] getListOfProfiles() {
132aa562ffdb8f728569e6957b742f271eb7303f878Hung-ying Tyan        mContext.enforceCallingOrSelfPermission(
133aa562ffdb8f728569e6957b742f271eb7303f878Hung-ying Tyan                android.Manifest.permission.USE_SIP, null);
1346a53489ae594d7cc373a00687d6ea2f23d0634dfHung-ying Tyan        boolean isCallerRadio = isCallerRadio();
1356a53489ae594d7cc373a00687d6ea2f23d0634dfHung-ying Tyan        ArrayList<SipProfile> profiles = new ArrayList<SipProfile>();
1362d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        for (SipSessionGroupExt group : mSipGroups.values()) {
1376a53489ae594d7cc373a00687d6ea2f23d0634dfHung-ying Tyan            if (isCallerRadio || isCallerCreator(group)) {
1386a53489ae594d7cc373a00687d6ea2f23d0634dfHung-ying Tyan                profiles.add(group.getLocalProfile());
1396a53489ae594d7cc373a00687d6ea2f23d0634dfHung-ying Tyan            }
1402d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
1416a53489ae594d7cc373a00687d6ea2f23d0634dfHung-ying Tyan        return profiles.toArray(new SipProfile[profiles.size()]);
1422d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
1432d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
144e9b54077274d0c4066093cd90dabca59b3d9a157Hung-ying Tyan    public synchronized void open(SipProfile localProfile) {
145aa562ffdb8f728569e6957b742f271eb7303f878Hung-ying Tyan        mContext.enforceCallingOrSelfPermission(
146aa562ffdb8f728569e6957b742f271eb7303f878Hung-ying Tyan                android.Manifest.permission.USE_SIP, null);
1475424c8dcacf1c227fe7deb0185510614122ab447Chung-yih Wang        localProfile.setCallingUid(Binder.getCallingUid());
1482d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        try {
1492d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            createGroup(localProfile);
1502d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        } catch (SipException e) {
1512d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            Log.e(TAG, "openToMakeCalls()", e);
1522d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            // TODO: how to send the exception back
1532d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
1542d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
1552d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
1562d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    public synchronized void open3(SipProfile localProfile,
157323d3671ac813df8dd173f3f4d6cb681ee29f740Hung-ying Tyan            PendingIntent incomingCallPendingIntent,
158323d3671ac813df8dd173f3f4d6cb681ee29f740Hung-ying Tyan            ISipSessionListener listener) {
159aa562ffdb8f728569e6957b742f271eb7303f878Hung-ying Tyan        mContext.enforceCallingOrSelfPermission(
160aa562ffdb8f728569e6957b742f271eb7303f878Hung-ying Tyan                android.Manifest.permission.USE_SIP, null);
1615424c8dcacf1c227fe7deb0185510614122ab447Chung-yih Wang        localProfile.setCallingUid(Binder.getCallingUid());
162323d3671ac813df8dd173f3f4d6cb681ee29f740Hung-ying Tyan        if (incomingCallPendingIntent == null) {
163323d3671ac813df8dd173f3f4d6cb681ee29f740Hung-ying Tyan            Log.w(TAG, "incomingCallPendingIntent cannot be null; "
164323d3671ac813df8dd173f3f4d6cb681ee29f740Hung-ying Tyan                    + "the profile is not opened");
1656a53489ae594d7cc373a00687d6ea2f23d0634dfHung-ying Tyan            return;
1662d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
167c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan        if (DEBUG) Log.d(TAG, "open3: " + localProfile.getUriString() + ": "
168323d3671ac813df8dd173f3f4d6cb681ee29f740Hung-ying Tyan                + incomingCallPendingIntent + ": " + listener);
1692d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        try {
1702d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            SipSessionGroupExt group = createGroup(localProfile,
171323d3671ac813df8dd173f3f4d6cb681ee29f740Hung-ying Tyan                    incomingCallPendingIntent, listener);
1722d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            if (localProfile.getAutoRegistration()) {
1732d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                group.openToReceiveCalls();
174c2bd6162eddad0cdfdafc037142e043680ffa705Chia-chi Yeh                updateWakeLocks();
1752d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
1762d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        } catch (SipException e) {
1772d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            Log.e(TAG, "openToReceiveCalls()", e);
1782d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            // TODO: how to send the exception back
1792d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
1802d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
1812d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
1826a53489ae594d7cc373a00687d6ea2f23d0634dfHung-ying Tyan    private boolean isCallerCreator(SipSessionGroupExt group) {
1836a53489ae594d7cc373a00687d6ea2f23d0634dfHung-ying Tyan        SipProfile profile = group.getLocalProfile();
1846a53489ae594d7cc373a00687d6ea2f23d0634dfHung-ying Tyan        return (profile.getCallingUid() == Binder.getCallingUid());
1856a53489ae594d7cc373a00687d6ea2f23d0634dfHung-ying Tyan    }
1866a53489ae594d7cc373a00687d6ea2f23d0634dfHung-ying Tyan
1876a53489ae594d7cc373a00687d6ea2f23d0634dfHung-ying Tyan    private boolean isCallerCreatorOrRadio(SipSessionGroupExt group) {
1886a53489ae594d7cc373a00687d6ea2f23d0634dfHung-ying Tyan        return (isCallerRadio() || isCallerCreator(group));
1896a53489ae594d7cc373a00687d6ea2f23d0634dfHung-ying Tyan    }
1906a53489ae594d7cc373a00687d6ea2f23d0634dfHung-ying Tyan
1916a53489ae594d7cc373a00687d6ea2f23d0634dfHung-ying Tyan    private boolean isCallerRadio() {
1926a53489ae594d7cc373a00687d6ea2f23d0634dfHung-ying Tyan        return (Binder.getCallingUid() == Process.PHONE_UID);
1936a53489ae594d7cc373a00687d6ea2f23d0634dfHung-ying Tyan    }
1946a53489ae594d7cc373a00687d6ea2f23d0634dfHung-ying Tyan
1952d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    public synchronized void close(String localProfileUri) {
196aa562ffdb8f728569e6957b742f271eb7303f878Hung-ying Tyan        mContext.enforceCallingOrSelfPermission(
197aa562ffdb8f728569e6957b742f271eb7303f878Hung-ying Tyan                android.Manifest.permission.USE_SIP, null);
1986a53489ae594d7cc373a00687d6ea2f23d0634dfHung-ying Tyan        SipSessionGroupExt group = mSipGroups.get(localProfileUri);
1996a53489ae594d7cc373a00687d6ea2f23d0634dfHung-ying Tyan        if (group == null) return;
2006a53489ae594d7cc373a00687d6ea2f23d0634dfHung-ying Tyan        if (!isCallerCreatorOrRadio(group)) {
201431bb2269532f2514861b908d5fafda8fa64da79Joe Onorato            Log.w(TAG, "only creator or radio can close this profile");
2026a53489ae594d7cc373a00687d6ea2f23d0634dfHung-ying Tyan            return;
2032d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
2046a53489ae594d7cc373a00687d6ea2f23d0634dfHung-ying Tyan
2056a53489ae594d7cc373a00687d6ea2f23d0634dfHung-ying Tyan        group = mSipGroups.remove(localProfileUri);
2066a53489ae594d7cc373a00687d6ea2f23d0634dfHung-ying Tyan        notifyProfileRemoved(group.getLocalProfile());
2076a53489ae594d7cc373a00687d6ea2f23d0634dfHung-ying Tyan        group.close();
208bd57eeafe034cf850225db403700b5dc5db5ebccHung-ying Tyan
209c2bd6162eddad0cdfdafc037142e043680ffa705Chia-chi Yeh        updateWakeLocks();
2102d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
2112d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
2122d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    public synchronized boolean isOpened(String localProfileUri) {
213aa562ffdb8f728569e6957b742f271eb7303f878Hung-ying Tyan        mContext.enforceCallingOrSelfPermission(
214aa562ffdb8f728569e6957b742f271eb7303f878Hung-ying Tyan                android.Manifest.permission.USE_SIP, null);
2152d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        SipSessionGroupExt group = mSipGroups.get(localProfileUri);
2166a53489ae594d7cc373a00687d6ea2f23d0634dfHung-ying Tyan        if (group == null) return false;
2176a53489ae594d7cc373a00687d6ea2f23d0634dfHung-ying Tyan        if (isCallerCreatorOrRadio(group)) {
218262cdfca7a0940735d3a08779e2d01bfdf639294Hung-ying Tyan            return true;
2196a53489ae594d7cc373a00687d6ea2f23d0634dfHung-ying Tyan        } else {
220431bb2269532f2514861b908d5fafda8fa64da79Joe Onorato            Log.w(TAG, "only creator or radio can query on the profile");
2216a53489ae594d7cc373a00687d6ea2f23d0634dfHung-ying Tyan            return false;
2226a53489ae594d7cc373a00687d6ea2f23d0634dfHung-ying Tyan        }
2232d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
2242d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
2252d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    public synchronized boolean isRegistered(String localProfileUri) {
226aa562ffdb8f728569e6957b742f271eb7303f878Hung-ying Tyan        mContext.enforceCallingOrSelfPermission(
227aa562ffdb8f728569e6957b742f271eb7303f878Hung-ying Tyan                android.Manifest.permission.USE_SIP, null);
2282d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        SipSessionGroupExt group = mSipGroups.get(localProfileUri);
2296a53489ae594d7cc373a00687d6ea2f23d0634dfHung-ying Tyan        if (group == null) return false;
2306a53489ae594d7cc373a00687d6ea2f23d0634dfHung-ying Tyan        if (isCallerCreatorOrRadio(group)) {
2316a53489ae594d7cc373a00687d6ea2f23d0634dfHung-ying Tyan            return group.isRegistered();
2326a53489ae594d7cc373a00687d6ea2f23d0634dfHung-ying Tyan        } else {
233431bb2269532f2514861b908d5fafda8fa64da79Joe Onorato            Log.w(TAG, "only creator or radio can query on the profile");
2346a53489ae594d7cc373a00687d6ea2f23d0634dfHung-ying Tyan            return false;
2356a53489ae594d7cc373a00687d6ea2f23d0634dfHung-ying Tyan        }
2362d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
2372d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
2382d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    public synchronized void setRegistrationListener(String localProfileUri,
2392d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            ISipSessionListener listener) {
240aa562ffdb8f728569e6957b742f271eb7303f878Hung-ying Tyan        mContext.enforceCallingOrSelfPermission(
241aa562ffdb8f728569e6957b742f271eb7303f878Hung-ying Tyan                android.Manifest.permission.USE_SIP, null);
2422d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        SipSessionGroupExt group = mSipGroups.get(localProfileUri);
2436a53489ae594d7cc373a00687d6ea2f23d0634dfHung-ying Tyan        if (group == null) return;
2446a53489ae594d7cc373a00687d6ea2f23d0634dfHung-ying Tyan        if (isCallerCreator(group)) {
2456a53489ae594d7cc373a00687d6ea2f23d0634dfHung-ying Tyan            group.setListener(listener);
2466a53489ae594d7cc373a00687d6ea2f23d0634dfHung-ying Tyan        } else {
247431bb2269532f2514861b908d5fafda8fa64da79Joe Onorato            Log.w(TAG, "only creator can set listener on the profile");
2486a53489ae594d7cc373a00687d6ea2f23d0634dfHung-ying Tyan        }
2492d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
2502d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
2512d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    public synchronized ISipSession createSession(SipProfile localProfile,
2522d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            ISipSessionListener listener) {
253aa562ffdb8f728569e6957b742f271eb7303f878Hung-ying Tyan        mContext.enforceCallingOrSelfPermission(
254aa562ffdb8f728569e6957b742f271eb7303f878Hung-ying Tyan                android.Manifest.permission.USE_SIP, null);
2555424c8dcacf1c227fe7deb0185510614122ab447Chung-yih Wang        localProfile.setCallingUid(Binder.getCallingUid());
256c2bd6162eddad0cdfdafc037142e043680ffa705Chia-chi Yeh        if (mNetworkType == -1) return null;
2572d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        try {
2582d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            SipSessionGroupExt group = createGroup(localProfile);
2592d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return group.createSession(listener);
2602d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        } catch (SipException e) {
261431bb2269532f2514861b908d5fafda8fa64da79Joe Onorato            if (DEBUG) Log.d(TAG, "createSession()", e);
2622d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return null;
2632d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
2642d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
2652d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
2662d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    public synchronized ISipSession getPendingSession(String callId) {
267aa562ffdb8f728569e6957b742f271eb7303f878Hung-ying Tyan        mContext.enforceCallingOrSelfPermission(
268aa562ffdb8f728569e6957b742f271eb7303f878Hung-ying Tyan                android.Manifest.permission.USE_SIP, null);
2692d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        if (callId == null) return null;
2702d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        return mPendingSessions.get(callId);
2712d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
2722d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
2732d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private String determineLocalIp() {
2742d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        try {
2752d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            DatagramSocket s = new DatagramSocket();
2762d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            s.connect(InetAddress.getByName("192.168.1.1"), 80);
2772d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return s.getLocalAddress().getHostAddress();
2782d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        } catch (IOException e) {
279431bb2269532f2514861b908d5fafda8fa64da79Joe Onorato            if (DEBUG) Log.d(TAG, "determineLocalIp()", e);
2802d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            // dont do anything; there should be a connectivity change going
2812d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return null;
2822d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
2832d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
2842d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
2852d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private SipSessionGroupExt createGroup(SipProfile localProfile)
2862d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            throws SipException {
2872d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        String key = localProfile.getUriString();
2882d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        SipSessionGroupExt group = mSipGroups.get(key);
2892d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        if (group == null) {
2902d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            group = new SipSessionGroupExt(localProfile, null, null);
2912d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            mSipGroups.put(key, group);
2922d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            notifyProfileAdded(localProfile);
2936a53489ae594d7cc373a00687d6ea2f23d0634dfHung-ying Tyan        } else if (!isCallerCreator(group)) {
2946a53489ae594d7cc373a00687d6ea2f23d0634dfHung-ying Tyan            throw new SipException("only creator can access the profile");
2952d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
2962d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        return group;
2972d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
2982d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
2992d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private SipSessionGroupExt createGroup(SipProfile localProfile,
300323d3671ac813df8dd173f3f4d6cb681ee29f740Hung-ying Tyan            PendingIntent incomingCallPendingIntent,
301323d3671ac813df8dd173f3f4d6cb681ee29f740Hung-ying Tyan            ISipSessionListener listener) throws SipException {
3022d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        String key = localProfile.getUriString();
3032d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        SipSessionGroupExt group = mSipGroups.get(key);
3042d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        if (group != null) {
3056a53489ae594d7cc373a00687d6ea2f23d0634dfHung-ying Tyan            if (!isCallerCreator(group)) {
3066a53489ae594d7cc373a00687d6ea2f23d0634dfHung-ying Tyan                throw new SipException("only creator can access the profile");
3076a53489ae594d7cc373a00687d6ea2f23d0634dfHung-ying Tyan            }
308323d3671ac813df8dd173f3f4d6cb681ee29f740Hung-ying Tyan            group.setIncomingCallPendingIntent(incomingCallPendingIntent);
3092d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            group.setListener(listener);
3102d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        } else {
3112d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            group = new SipSessionGroupExt(localProfile,
312323d3671ac813df8dd173f3f4d6cb681ee29f740Hung-ying Tyan                    incomingCallPendingIntent, listener);
3132d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            mSipGroups.put(key, group);
3142d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            notifyProfileAdded(localProfile);
3152d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
3162d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        return group;
3172d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
3182d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
3192d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private void notifyProfileAdded(SipProfile localProfile) {
320c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan        if (DEBUG) Log.d(TAG, "notify: profile added: " + localProfile);
32184a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan        Intent intent = new Intent(SipManager.ACTION_SIP_ADD_PHONE);
32284a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan        intent.putExtra(SipManager.EXTRA_LOCAL_URI, localProfile.getUriString());
3232d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        mContext.sendBroadcast(intent);
324c2bd6162eddad0cdfdafc037142e043680ffa705Chia-chi Yeh        if (mSipGroups.size() == 1) {
325c2bd6162eddad0cdfdafc037142e043680ffa705Chia-chi Yeh            registerReceivers();
326c2bd6162eddad0cdfdafc037142e043680ffa705Chia-chi Yeh        }
3272d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
3282d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
3292d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private void notifyProfileRemoved(SipProfile localProfile) {
330c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan        if (DEBUG) Log.d(TAG, "notify: profile removed: " + localProfile);
33184a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan        Intent intent = new Intent(SipManager.ACTION_SIP_REMOVE_PHONE);
33284a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan        intent.putExtra(SipManager.EXTRA_LOCAL_URI, localProfile.getUriString());
3332d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        mContext.sendBroadcast(intent);
334c2bd6162eddad0cdfdafc037142e043680ffa705Chia-chi Yeh        if (mSipGroups.size() == 0) {
335c2bd6162eddad0cdfdafc037142e043680ffa705Chia-chi Yeh            unregisterReceivers();
3362d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
3372d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
3382d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
339bb0a989c17cd6135c8d9c8566507521d4d927fe0Chung-yih Wang    private void stopPortMappingMeasurement() {
340bb0a989c17cd6135c8d9c8566507521d4d927fe0Chung-yih Wang        if (mIntervalMeasurementProcess != null) {
341bb0a989c17cd6135c8d9c8566507521d4d927fe0Chung-yih Wang            mIntervalMeasurementProcess.stop();
342bb0a989c17cd6135c8d9c8566507521d4d927fe0Chung-yih Wang            mIntervalMeasurementProcess = null;
343bb0a989c17cd6135c8d9c8566507521d4d927fe0Chung-yih Wang        }
344bb0a989c17cd6135c8d9c8566507521d4d927fe0Chung-yih Wang    }
345bb0a989c17cd6135c8d9c8566507521d4d927fe0Chung-yih Wang
3464a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan    private void startPortMappingLifetimeMeasurement(
3474a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan            SipProfile localProfile) {
3489edfa107575b5905c9ae0a2fa0d6f0cc19595300Hung-ying Tyan        startPortMappingLifetimeMeasurement(localProfile,
3499edfa107575b5905c9ae0a2fa0d6f0cc19595300Hung-ying Tyan                DEFAULT_MAX_KEEPALIVE_INTERVAL);
350e65f3a896f03bba5327ce4f3989c0422855450caHung-ying Tyan    }
351e65f3a896f03bba5327ce4f3989c0422855450caHung-ying Tyan
352e65f3a896f03bba5327ce4f3989c0422855450caHung-ying Tyan    private void startPortMappingLifetimeMeasurement(
353e65f3a896f03bba5327ce4f3989c0422855450caHung-ying Tyan            SipProfile localProfile, int maxInterval) {
3544a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan        if ((mIntervalMeasurementProcess == null)
3554a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                && (mKeepAliveInterval == -1)
3564a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                && isBehindNAT(mLocalIp)) {
3574a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan            Log.d(TAG, "start NAT port mapping timeout measurement on "
3584a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                    + localProfile.getUriString());
3594a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan
3609edfa107575b5905c9ae0a2fa0d6f0cc19595300Hung-ying Tyan            int minInterval = mLastGoodKeepAliveInterval;
3619edfa107575b5905c9ae0a2fa0d6f0cc19595300Hung-ying Tyan            if (minInterval >= maxInterval) {
3629edfa107575b5905c9ae0a2fa0d6f0cc19595300Hung-ying Tyan                // If mLastGoodKeepAliveInterval also does not work, reset it
3639edfa107575b5905c9ae0a2fa0d6f0cc19595300Hung-ying Tyan                // to the default min
3649edfa107575b5905c9ae0a2fa0d6f0cc19595300Hung-ying Tyan                minInterval = mLastGoodKeepAliveInterval
3659edfa107575b5905c9ae0a2fa0d6f0cc19595300Hung-ying Tyan                        = DEFAULT_KEEPALIVE_INTERVAL;
3669edfa107575b5905c9ae0a2fa0d6f0cc19595300Hung-ying Tyan                Log.d(TAG, "  reset min interval to " + minInterval);
3679edfa107575b5905c9ae0a2fa0d6f0cc19595300Hung-ying Tyan            }
3689edfa107575b5905c9ae0a2fa0d6f0cc19595300Hung-ying Tyan            mIntervalMeasurementProcess = new IntervalMeasurementProcess(
3699edfa107575b5905c9ae0a2fa0d6f0cc19595300Hung-ying Tyan                    localProfile, minInterval, maxInterval);
3704a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan            mIntervalMeasurementProcess.start();
3714a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan        }
372bb0a989c17cd6135c8d9c8566507521d4d927fe0Chung-yih Wang    }
373bb0a989c17cd6135c8d9c8566507521d4d927fe0Chung-yih Wang
374e65f3a896f03bba5327ce4f3989c0422855450caHung-ying Tyan    private void restartPortMappingLifetimeMeasurement(
375e65f3a896f03bba5327ce4f3989c0422855450caHung-ying Tyan            SipProfile localProfile, int maxInterval) {
376e65f3a896f03bba5327ce4f3989c0422855450caHung-ying Tyan        stopPortMappingMeasurement();
377e65f3a896f03bba5327ce4f3989c0422855450caHung-ying Tyan        mKeepAliveInterval = -1;
378e65f3a896f03bba5327ce4f3989c0422855450caHung-ying Tyan        startPortMappingLifetimeMeasurement(localProfile, maxInterval);
379e65f3a896f03bba5327ce4f3989c0422855450caHung-ying Tyan    }
380e65f3a896f03bba5327ce4f3989c0422855450caHung-ying Tyan
3812d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private synchronized void addPendingSession(ISipSession session) {
3822d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        try {
38360c45d026907edbe340c8cf9e1723b3dd34f8b6aHung-ying Tyan            cleanUpPendingSessions();
3842d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            mPendingSessions.put(session.getCallId(), session);
38560c45d026907edbe340c8cf9e1723b3dd34f8b6aHung-ying Tyan            if (DEBUG) Log.d(TAG, "#pending sess=" + mPendingSessions.size());
3862d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        } catch (RemoteException e) {
3872d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            // should not happen with a local call
3882d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            Log.e(TAG, "addPendingSession()", e);
3892d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
3902d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
3912d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
39260c45d026907edbe340c8cf9e1723b3dd34f8b6aHung-ying Tyan    private void cleanUpPendingSessions() throws RemoteException {
39360c45d026907edbe340c8cf9e1723b3dd34f8b6aHung-ying Tyan        Map.Entry<String, ISipSession>[] entries =
39460c45d026907edbe340c8cf9e1723b3dd34f8b6aHung-ying Tyan                mPendingSessions.entrySet().toArray(
39560c45d026907edbe340c8cf9e1723b3dd34f8b6aHung-ying Tyan                new Map.Entry[mPendingSessions.size()]);
39660c45d026907edbe340c8cf9e1723b3dd34f8b6aHung-ying Tyan        for (Map.Entry<String, ISipSession> entry : entries) {
39760c45d026907edbe340c8cf9e1723b3dd34f8b6aHung-ying Tyan            if (entry.getValue().getState() != SipSession.State.INCOMING_CALL) {
39860c45d026907edbe340c8cf9e1723b3dd34f8b6aHung-ying Tyan                mPendingSessions.remove(entry.getKey());
39960c45d026907edbe340c8cf9e1723b3dd34f8b6aHung-ying Tyan            }
40060c45d026907edbe340c8cf9e1723b3dd34f8b6aHung-ying Tyan        }
40160c45d026907edbe340c8cf9e1723b3dd34f8b6aHung-ying Tyan    }
40260c45d026907edbe340c8cf9e1723b3dd34f8b6aHung-ying Tyan
4030a6e717fb6846f66b8dc853e079f2166307bfc60Hung-ying Tyan    private synchronized boolean callingSelf(SipSessionGroupExt ringingGroup,
4040a6e717fb6846f66b8dc853e079f2166307bfc60Hung-ying Tyan            SipSessionGroup.SipSessionImpl ringingSession) {
4050a6e717fb6846f66b8dc853e079f2166307bfc60Hung-ying Tyan        String callId = ringingSession.getCallId();
4060a6e717fb6846f66b8dc853e079f2166307bfc60Hung-ying Tyan        for (SipSessionGroupExt group : mSipGroups.values()) {
4070a6e717fb6846f66b8dc853e079f2166307bfc60Hung-ying Tyan            if ((group != ringingGroup) && group.containsSession(callId)) {
4080a6e717fb6846f66b8dc853e079f2166307bfc60Hung-ying Tyan                if (DEBUG) Log.d(TAG, "call self: "
4090a6e717fb6846f66b8dc853e079f2166307bfc60Hung-ying Tyan                        + ringingSession.getLocalProfile().getUriString()
4100a6e717fb6846f66b8dc853e079f2166307bfc60Hung-ying Tyan                        + " -> " + group.getLocalProfile().getUriString());
4110a6e717fb6846f66b8dc853e079f2166307bfc60Hung-ying Tyan                return true;
4120a6e717fb6846f66b8dc853e079f2166307bfc60Hung-ying Tyan            }
4130a6e717fb6846f66b8dc853e079f2166307bfc60Hung-ying Tyan        }
4140a6e717fb6846f66b8dc853e079f2166307bfc60Hung-ying Tyan        return false;
4150a6e717fb6846f66b8dc853e079f2166307bfc60Hung-ying Tyan    }
4160a6e717fb6846f66b8dc853e079f2166307bfc60Hung-ying Tyan
4174a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan    private synchronized void onKeepAliveIntervalChanged() {
4184a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan        for (SipSessionGroupExt group : mSipGroups.values()) {
4194a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan            group.onKeepAliveIntervalChanged();
4204a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan        }
4214a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan    }
4224a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan
4234a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan    private int getKeepAliveInterval() {
4244a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan        return (mKeepAliveInterval < 0)
4259edfa107575b5905c9ae0a2fa0d6f0cc19595300Hung-ying Tyan                ? mLastGoodKeepAliveInterval
4264a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                : mKeepAliveInterval;
4274a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan    }
4284a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan
4294a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan    private boolean isBehindNAT(String address) {
4304a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan        try {
4314a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan            byte[] d = InetAddress.getByName(address).getAddress();
4324a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan            if ((d[0] == 10) ||
4334a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                    (((0x000000FF & ((int)d[0])) == 172) &&
4344a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                    ((0x000000F0 & ((int)d[1])) == 16)) ||
4354a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                    (((0x000000FF & ((int)d[0])) == 192) &&
4364a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                    ((0x000000FF & ((int)d[1])) == 168))) {
4374a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                return true;
4384a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan            }
4394a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan        } catch (UnknownHostException e) {
4404a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan            Log.e(TAG, "isBehindAT()" + address, e);
4414a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan        }
4424a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan        return false;
4434a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan    }
4440a6e717fb6846f66b8dc853e079f2166307bfc60Hung-ying Tyan
4452d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private class SipSessionGroupExt extends SipSessionAdapter {
4462d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private SipSessionGroup mSipGroup;
447323d3671ac813df8dd173f3f4d6cb681ee29f740Hung-ying Tyan        private PendingIntent mIncomingCallPendingIntent;
448262cdfca7a0940735d3a08779e2d01bfdf639294Hung-ying Tyan        private boolean mOpenedToReceiveCalls;
4492d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
4502d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private AutoRegistrationProcess mAutoRegistration =
4512d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                new AutoRegistrationProcess();
4522d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
4532d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public SipSessionGroupExt(SipProfile localProfile,
454323d3671ac813df8dd173f3f4d6cb681ee29f740Hung-ying Tyan                PendingIntent incomingCallPendingIntent,
4552d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                ISipSessionListener listener) throws SipException {
4562dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh            mSipGroup = new SipSessionGroup(duplicate(localProfile),
4572dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh                    localProfile.getPassword(), mTimer, mMyWakeLock);
458323d3671ac813df8dd173f3f4d6cb681ee29f740Hung-ying Tyan            mIncomingCallPendingIntent = incomingCallPendingIntent;
4592d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            mAutoRegistration.setListener(listener);
4602d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
4612d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
4622d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public SipProfile getLocalProfile() {
4632d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return mSipGroup.getLocalProfile();
4642d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
4652d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
4660a6e717fb6846f66b8dc853e079f2166307bfc60Hung-ying Tyan        public boolean containsSession(String callId) {
4670a6e717fb6846f66b8dc853e079f2166307bfc60Hung-ying Tyan            return mSipGroup.containsSession(callId);
4680a6e717fb6846f66b8dc853e079f2166307bfc60Hung-ying Tyan        }
4690a6e717fb6846f66b8dc853e079f2166307bfc60Hung-ying Tyan
4704a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan        public void onKeepAliveIntervalChanged() {
4714a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan            mAutoRegistration.onKeepAliveIntervalChanged();
4724a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan        }
4734a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan
4744a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan        // TODO: remove this method once SipWakeupTimer can better handle variety
4754a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan        // of timeout values
4764a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan        void setWakeupTimer(SipWakeupTimer timer) {
4774a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan            mSipGroup.setWakeupTimer(timer);
4784a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan        }
4794a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan
4802d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private SipProfile duplicate(SipProfile p) {
4812d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            try {
4825424c8dcacf1c227fe7deb0185510614122ab447Chung-yih Wang                return new SipProfile.Builder(p).setPassword("*").build();
4832d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            } catch (Exception e) {
4842d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                Log.wtf(TAG, "duplicate()", e);
4852d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                throw new RuntimeException("duplicate profile", e);
4862d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
4872d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
4882d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
4892d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public void setListener(ISipSessionListener listener) {
4902d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            mAutoRegistration.setListener(listener);
4912d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
4922d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
493323d3671ac813df8dd173f3f4d6cb681ee29f740Hung-ying Tyan        public void setIncomingCallPendingIntent(PendingIntent pIntent) {
494323d3671ac813df8dd173f3f4d6cb681ee29f740Hung-ying Tyan            mIncomingCallPendingIntent = pIntent;
4952d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
4962d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
4972d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public void openToReceiveCalls() throws SipException {
498262cdfca7a0940735d3a08779e2d01bfdf639294Hung-ying Tyan            mOpenedToReceiveCalls = true;
499c2bd6162eddad0cdfdafc037142e043680ffa705Chia-chi Yeh            if (mNetworkType != -1) {
5002d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mSipGroup.openToReceiveCalls(this);
5012d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mAutoRegistration.start(mSipGroup);
5022d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
503c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan            if (DEBUG) Log.d(TAG, "  openToReceiveCalls: " + getUri() + ": "
504323d3671ac813df8dd173f3f4d6cb681ee29f740Hung-ying Tyan                    + mIncomingCallPendingIntent);
5052d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
5062d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
5072d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public void onConnectivityChanged(boolean connected)
5082d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                throws SipException {
509d231aa880ab006d51ffe03454c1fc082f1c97bb8Hung-ying Tyan            mSipGroup.onConnectivityChanged();
5102d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            if (connected) {
5112dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh                mSipGroup.reset();
512262cdfca7a0940735d3a08779e2d01bfdf639294Hung-ying Tyan                if (mOpenedToReceiveCalls) openToReceiveCalls();
5132d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            } else {
514262cdfca7a0940735d3a08779e2d01bfdf639294Hung-ying Tyan                // close mSipGroup but remember mOpenedToReceiveCalls
515c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan                if (DEBUG) Log.d(TAG, "  close auto reg temporarily: "
516323d3671ac813df8dd173f3f4d6cb681ee29f740Hung-ying Tyan                        + getUri() + ": " + mIncomingCallPendingIntent);
5172d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mSipGroup.close();
5182d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mAutoRegistration.stop();
5192d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
5202d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
5212d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
522fc51f2c972c7a3a829f556c4c19f16c60c87d7e7Hung-ying Tyan        public void close() {
523262cdfca7a0940735d3a08779e2d01bfdf639294Hung-ying Tyan            mOpenedToReceiveCalls = false;
524fc51f2c972c7a3a829f556c4c19f16c60c87d7e7Hung-ying Tyan            mSipGroup.close();
5252d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            mAutoRegistration.stop();
526c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan            if (DEBUG) Log.d(TAG, "   close: " + getUri() + ": "
527323d3671ac813df8dd173f3f4d6cb681ee29f740Hung-ying Tyan                    + mIncomingCallPendingIntent);
5282d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
5292d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
5302d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public ISipSession createSession(ISipSessionListener listener) {
5312d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return mSipGroup.createSession(listener);
5322d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
5332d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
5342d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        @Override
535323d3671ac813df8dd173f3f4d6cb681ee29f740Hung-ying Tyan        public void onRinging(ISipSession s, SipProfile caller,
53695b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh                String sessionDescription) {
537cb6ee06f62c20ae036a206667097f20b837b11abChia-chi Yeh            if (DEBUG) Log.d(TAG, "<<<<< onRinging()");
538323d3671ac813df8dd173f3f4d6cb681ee29f740Hung-ying Tyan            SipSessionGroup.SipSessionImpl session =
539323d3671ac813df8dd173f3f4d6cb681ee29f740Hung-ying Tyan                    (SipSessionGroup.SipSessionImpl) s;
5402d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            synchronized (SipService.this) {
5412d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                try {
5420a6e717fb6846f66b8dc853e079f2166307bfc60Hung-ying Tyan                    if (!isRegistered() || callingSelf(this, session)) {
5432d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        session.endCall();
5442d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        return;
5452d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    }
5462d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
5472d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    // send out incoming call broadcast
5482d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    addPendingSession(session);
5492d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    Intent intent = SipManager.createIncomingCallBroadcast(
550323d3671ac813df8dd173f3f4d6cb681ee29f740Hung-ying Tyan                            session.getCallId(), sessionDescription);
551c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan                    if (DEBUG) Log.d(TAG, " ringing~~ " + getUri() + ": "
552c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan                            + caller.getUri() + ": " + session.getCallId()
553323d3671ac813df8dd173f3f4d6cb681ee29f740Hung-ying Tyan                            + " " + mIncomingCallPendingIntent);
554323d3671ac813df8dd173f3f4d6cb681ee29f740Hung-ying Tyan                    mIncomingCallPendingIntent.send(mContext,
555323d3671ac813df8dd173f3f4d6cb681ee29f740Hung-ying Tyan                            SipManager.INCOMING_CALL_RESULT_CODE, intent);
556323d3671ac813df8dd173f3f4d6cb681ee29f740Hung-ying Tyan                } catch (PendingIntent.CanceledException e) {
557323d3671ac813df8dd173f3f4d6cb681ee29f740Hung-ying Tyan                    Log.w(TAG, "pendingIntent is canceled, drop incoming call");
558323d3671ac813df8dd173f3f4d6cb681ee29f740Hung-ying Tyan                    session.endCall();
5592d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                }
5602d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
5612d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
5622d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
5632d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        @Override
56497963794af1e18674dd111e3ad344d90b16c922cHung-ying Tyan        public void onError(ISipSession session, int errorCode,
5652d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                String message) {
56697963794af1e18674dd111e3ad344d90b16c922cHung-ying Tyan            if (DEBUG) Log.d(TAG, "sip session error: "
56797963794af1e18674dd111e3ad344d90b16c922cHung-ying Tyan                    + SipErrorCode.toString(errorCode) + ": " + message);
5682d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
5692d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
570262cdfca7a0940735d3a08779e2d01bfdf639294Hung-ying Tyan        public boolean isOpenedToReceiveCalls() {
571262cdfca7a0940735d3a08779e2d01bfdf639294Hung-ying Tyan            return mOpenedToReceiveCalls;
5722d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
5732d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
5742d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public boolean isRegistered() {
5752d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return mAutoRegistration.isRegistered();
5762d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
5772d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
5782d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private String getUri() {
5792d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return mSipGroup.getLocalProfileUri();
5802d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
5812d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
5822d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
583129d0b08fdf9588f7c8feeb9db3def30973c092eHung-ying Tyan    private class IntervalMeasurementProcess implements Runnable,
5844a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan            SipSessionGroup.KeepAliveProcessCallback {
5854a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan        private static final String TAG = "SipKeepAliveInterval";
586129d0b08fdf9588f7c8feeb9db3def30973c092eHung-ying Tyan        private static final int MIN_INTERVAL = 5; // in seconds
5874a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan        private static final int PASS_THRESHOLD = 10;
588e65f3a896f03bba5327ce4f3989c0422855450caHung-ying Tyan        private static final int MAX_RETRY_COUNT = 5;
589129d0b08fdf9588f7c8feeb9db3def30973c092eHung-ying Tyan        private static final int NAT_MEASUREMENT_RETRY_INTERVAL = 120; // in seconds
590d17b6d526648c372be761097e55c19767d5dba7dChia-chi Yeh        private SipProfile mLocalProfile;
591bb0a989c17cd6135c8d9c8566507521d4d927fe0Chung-yih Wang        private SipSessionGroupExt mGroup;
592bb0a989c17cd6135c8d9c8566507521d4d927fe0Chung-yih Wang        private SipSessionGroup.SipSessionImpl mSession;
5939edfa107575b5905c9ae0a2fa0d6f0cc19595300Hung-ying Tyan        private int mMinInterval;
594e65f3a896f03bba5327ce4f3989c0422855450caHung-ying Tyan        private int mMaxInterval;
595e65f3a896f03bba5327ce4f3989c0422855450caHung-ying Tyan        private int mInterval;
596d17b6d526648c372be761097e55c19767d5dba7dChia-chi Yeh        private int mPassCount;
597e65f3a896f03bba5327ce4f3989c0422855450caHung-ying Tyan
5989edfa107575b5905c9ae0a2fa0d6f0cc19595300Hung-ying Tyan        public IntervalMeasurementProcess(SipProfile localProfile,
5999edfa107575b5905c9ae0a2fa0d6f0cc19595300Hung-ying Tyan                int minInterval, int maxInterval) {
6009edfa107575b5905c9ae0a2fa0d6f0cc19595300Hung-ying Tyan            mMaxInterval = maxInterval;
6019edfa107575b5905c9ae0a2fa0d6f0cc19595300Hung-ying Tyan            mMinInterval = minInterval;
602d17b6d526648c372be761097e55c19767d5dba7dChia-chi Yeh            mLocalProfile = localProfile;
603bb0a989c17cd6135c8d9c8566507521d4d927fe0Chung-yih Wang        }
604bb0a989c17cd6135c8d9c8566507521d4d927fe0Chung-yih Wang
605bb0a989c17cd6135c8d9c8566507521d4d927fe0Chung-yih Wang        public void start() {
6064a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan            synchronized (SipService.this) {
607d17b6d526648c372be761097e55c19767d5dba7dChia-chi Yeh                if (mSession != null) {
608d17b6d526648c372be761097e55c19767d5dba7dChia-chi Yeh                    return;
609129d0b08fdf9588f7c8feeb9db3def30973c092eHung-ying Tyan                }
610d17b6d526648c372be761097e55c19767d5dba7dChia-chi Yeh
611d17b6d526648c372be761097e55c19767d5dba7dChia-chi Yeh                mInterval = (mMaxInterval + mMinInterval) / 2;
612d17b6d526648c372be761097e55c19767d5dba7dChia-chi Yeh                mPassCount = 0;
613d17b6d526648c372be761097e55c19767d5dba7dChia-chi Yeh
614d17b6d526648c372be761097e55c19767d5dba7dChia-chi Yeh                // Don't start measurement if the interval is too small
615d17b6d526648c372be761097e55c19767d5dba7dChia-chi Yeh                if (mInterval < DEFAULT_KEEPALIVE_INTERVAL || checkTermination()) {
616d17b6d526648c372be761097e55c19767d5dba7dChia-chi Yeh                    Log.w(TAG, "measurement aborted; interval=[" +
617d17b6d526648c372be761097e55c19767d5dba7dChia-chi Yeh                            mMinInterval + "," + mMaxInterval + "]");
618d17b6d526648c372be761097e55c19767d5dba7dChia-chi Yeh                    return;
619d17b6d526648c372be761097e55c19767d5dba7dChia-chi Yeh                }
620d17b6d526648c372be761097e55c19767d5dba7dChia-chi Yeh
6214a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                try {
622d17b6d526648c372be761097e55c19767d5dba7dChia-chi Yeh                    Log.d(TAG, "start measurement w interval=" + mInterval);
623d17b6d526648c372be761097e55c19767d5dba7dChia-chi Yeh
624d17b6d526648c372be761097e55c19767d5dba7dChia-chi Yeh                    mGroup = new SipSessionGroupExt(mLocalProfile, null, null);
625d17b6d526648c372be761097e55c19767d5dba7dChia-chi Yeh                    // TODO: remove this line once SipWakeupTimer can better handle
626d17b6d526648c372be761097e55c19767d5dba7dChia-chi Yeh                    // variety of timeout values
627d17b6d526648c372be761097e55c19767d5dba7dChia-chi Yeh                    mGroup.setWakeupTimer(new SipWakeupTimer(mContext, mExecutor));
628d17b6d526648c372be761097e55c19767d5dba7dChia-chi Yeh
629d17b6d526648c372be761097e55c19767d5dba7dChia-chi Yeh                    mSession = (SipSessionGroup.SipSessionImpl)
630d17b6d526648c372be761097e55c19767d5dba7dChia-chi Yeh                            mGroup.createSession(null);
6314a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                    mSession.startKeepAliveProcess(mInterval, this);
632d17b6d526648c372be761097e55c19767d5dba7dChia-chi Yeh                } catch (Throwable t) {
633d17b6d526648c372be761097e55c19767d5dba7dChia-chi Yeh                    onError(SipErrorCode.CLIENT_ERROR, t.toString());
6344a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                }
6354a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan            }
636bb0a989c17cd6135c8d9c8566507521d4d927fe0Chung-yih Wang        }
637bb0a989c17cd6135c8d9c8566507521d4d927fe0Chung-yih Wang
638bb0a989c17cd6135c8d9c8566507521d4d927fe0Chung-yih Wang        public void stop() {
6394a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan            synchronized (SipService.this) {
640129d0b08fdf9588f7c8feeb9db3def30973c092eHung-ying Tyan                if (mSession != null) {
641129d0b08fdf9588f7c8feeb9db3def30973c092eHung-ying Tyan                    mSession.stopKeepAliveProcess();
642129d0b08fdf9588f7c8feeb9db3def30973c092eHung-ying Tyan                    mSession = null;
643129d0b08fdf9588f7c8feeb9db3def30973c092eHung-ying Tyan                }
644d17b6d526648c372be761097e55c19767d5dba7dChia-chi Yeh                if (mGroup != null) {
645d17b6d526648c372be761097e55c19767d5dba7dChia-chi Yeh                    mGroup.close();
646d17b6d526648c372be761097e55c19767d5dba7dChia-chi Yeh                    mGroup = null;
647d17b6d526648c372be761097e55c19767d5dba7dChia-chi Yeh                }
648129d0b08fdf9588f7c8feeb9db3def30973c092eHung-ying Tyan                mTimer.cancel(this);
649bb0a989c17cd6135c8d9c8566507521d4d927fe0Chung-yih Wang            }
650bb0a989c17cd6135c8d9c8566507521d4d927fe0Chung-yih Wang        }
651bb0a989c17cd6135c8d9c8566507521d4d927fe0Chung-yih Wang
6524a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan        private void restart() {
653bb0a989c17cd6135c8d9c8566507521d4d927fe0Chung-yih Wang            synchronized (SipService.this) {
654129d0b08fdf9588f7c8feeb9db3def30973c092eHung-ying Tyan                // Return immediately if the measurement process is stopped
655129d0b08fdf9588f7c8feeb9db3def30973c092eHung-ying Tyan                if (mSession == null) return;
656129d0b08fdf9588f7c8feeb9db3def30973c092eHung-ying Tyan
657129d0b08fdf9588f7c8feeb9db3def30973c092eHung-ying Tyan                Log.d(TAG, "restart measurement w interval=" + mInterval);
658bb0a989c17cd6135c8d9c8566507521d4d927fe0Chung-yih Wang                try {
6594a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                    mSession.stopKeepAliveProcess();
660129d0b08fdf9588f7c8feeb9db3def30973c092eHung-ying Tyan                    mPassCount = 0;
6614a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                    mSession.startKeepAliveProcess(mInterval, this);
6624a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                } catch (SipException e) {
6634a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                    Log.e(TAG, "restart()", e);
664bb0a989c17cd6135c8d9c8566507521d4d927fe0Chung-yih Wang                }
665bb0a989c17cd6135c8d9c8566507521d4d927fe0Chung-yih Wang            }
666bb0a989c17cd6135c8d9c8566507521d4d927fe0Chung-yih Wang        }
667bb0a989c17cd6135c8d9c8566507521d4d927fe0Chung-yih Wang
6689edfa107575b5905c9ae0a2fa0d6f0cc19595300Hung-ying Tyan        private boolean checkTermination() {
6699edfa107575b5905c9ae0a2fa0d6f0cc19595300Hung-ying Tyan            return ((mMaxInterval - mMinInterval) < MIN_INTERVAL);
6709edfa107575b5905c9ae0a2fa0d6f0cc19595300Hung-ying Tyan        }
6719edfa107575b5905c9ae0a2fa0d6f0cc19595300Hung-ying Tyan
6724a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan        // SipSessionGroup.KeepAliveProcessCallback
6734a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan        @Override
6744a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan        public void onResponse(boolean portChanged) {
6752d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            synchronized (SipService.this) {
6764a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                if (!portChanged) {
677e65f3a896f03bba5327ce4f3989c0422855450caHung-ying Tyan                    if (++mPassCount != PASS_THRESHOLD) return;
6784a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                    // update the interval, since the current interval is good to
6794a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                    // keep the port mapping.
6809edfa107575b5905c9ae0a2fa0d6f0cc19595300Hung-ying Tyan                    if (mKeepAliveInterval > 0) {
6819edfa107575b5905c9ae0a2fa0d6f0cc19595300Hung-ying Tyan                        mLastGoodKeepAliveInterval = mKeepAliveInterval;
6829edfa107575b5905c9ae0a2fa0d6f0cc19595300Hung-ying Tyan                    }
6834a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                    mKeepAliveInterval = mMinInterval = mInterval;
6844a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                    if (DEBUG) {
6854a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                        Log.d(TAG, "measured good keepalive interval: "
6864a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                                + mKeepAliveInterval);
687bd57eeafe034cf850225db403700b5dc5db5ebccHung-ying Tyan                    }
6884a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                    onKeepAliveIntervalChanged();
6894a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                } else {
6904a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                    // Since the rport is changed, shorten the interval.
6914a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                    mMaxInterval = mInterval;
6924a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                }
6939edfa107575b5905c9ae0a2fa0d6f0cc19595300Hung-ying Tyan                if (checkTermination()) {
6944a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                    // update mKeepAliveInterval and stop measurement.
6954a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                    stop();
6969edfa107575b5905c9ae0a2fa0d6f0cc19595300Hung-ying Tyan                    // If all the measurements failed, we still set it to
6979edfa107575b5905c9ae0a2fa0d6f0cc19595300Hung-ying Tyan                    // mMinInterval; If mMinInterval still doesn't work, a new
6989edfa107575b5905c9ae0a2fa0d6f0cc19595300Hung-ying Tyan                    // measurement with min interval=DEFAULT_KEEPALIVE_INTERVAL
6999edfa107575b5905c9ae0a2fa0d6f0cc19595300Hung-ying Tyan                    // will be conducted.
7004a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                    mKeepAliveInterval = mMinInterval;
7014a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                    if (DEBUG) {
7024a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                        Log.d(TAG, "measured keepalive interval: "
7034a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                                + mKeepAliveInterval);
704bb0a989c17cd6135c8d9c8566507521d4d927fe0Chung-yih Wang                    }
7054a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                } else {
7064a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                    // calculate the new interval and continue.
7074a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                    mInterval = (mMaxInterval + mMinInterval) / 2;
7084a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                    if (DEBUG) {
7094a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                        Log.d(TAG, "current interval: " + mKeepAliveInterval
7104a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                                + ", test new interval: " + mInterval);
7114a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                    }
7124a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                    restart();
7132d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                }
7142d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
7152d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
7162d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
7174a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan        // SipSessionGroup.KeepAliveProcessCallback
7184a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan        @Override
7194a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan        public void onError(int errorCode, String description) {
720129d0b08fdf9588f7c8feeb9db3def30973c092eHung-ying Tyan            Log.w(TAG, "interval measurement error: " + description);
721129d0b08fdf9588f7c8feeb9db3def30973c092eHung-ying Tyan            restartLater();
722129d0b08fdf9588f7c8feeb9db3def30973c092eHung-ying Tyan        }
723129d0b08fdf9588f7c8feeb9db3def30973c092eHung-ying Tyan
724129d0b08fdf9588f7c8feeb9db3def30973c092eHung-ying Tyan        // timeout handler
725129d0b08fdf9588f7c8feeb9db3def30973c092eHung-ying Tyan        @Override
726129d0b08fdf9588f7c8feeb9db3def30973c092eHung-ying Tyan        public void run() {
727129d0b08fdf9588f7c8feeb9db3def30973c092eHung-ying Tyan            mTimer.cancel(this);
728129d0b08fdf9588f7c8feeb9db3def30973c092eHung-ying Tyan            restart();
729129d0b08fdf9588f7c8feeb9db3def30973c092eHung-ying Tyan        }
730129d0b08fdf9588f7c8feeb9db3def30973c092eHung-ying Tyan
731129d0b08fdf9588f7c8feeb9db3def30973c092eHung-ying Tyan        private void restartLater() {
7324a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan            synchronized (SipService.this) {
733129d0b08fdf9588f7c8feeb9db3def30973c092eHung-ying Tyan                int interval = NAT_MEASUREMENT_RETRY_INTERVAL;
734129d0b08fdf9588f7c8feeb9db3def30973c092eHung-ying Tyan                mTimer.cancel(this);
735129d0b08fdf9588f7c8feeb9db3def30973c092eHung-ying Tyan                mTimer.set(interval * 1000, this);
7364a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan            }
7372d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
7382d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
7392d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
7402d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private class AutoRegistrationProcess extends SipSessionAdapter
7414a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan            implements Runnable, SipSessionGroup.KeepAliveProcessCallback {
742129d0b08fdf9588f7c8feeb9db3def30973c092eHung-ying Tyan        private static final int MIN_KEEPALIVE_SUCCESS_COUNT = 10;
743cb6ee06f62c20ae036a206667097f20b837b11abChia-chi Yeh        private String TAG = "SipAutoReg";
744129d0b08fdf9588f7c8feeb9db3def30973c092eHung-ying Tyan
7452d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private SipSessionGroup.SipSessionImpl mSession;
7464a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan        private SipSessionGroup.SipSessionImpl mKeepAliveSession;
7472d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private SipSessionListenerProxy mProxy = new SipSessionListenerProxy();
7482d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private int mBackoff = 1;
7492d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private boolean mRegistered;
7502d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private long mExpiryTime;
75197963794af1e18674dd111e3ad344d90b16c922cHung-ying Tyan        private int mErrorCode;
7523d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan        private String mErrorMessage;
753fb3a98b1d8d0ad040980d509c4c5341928b9460bHung-ying Tyan        private boolean mRunning = false;
7542d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
755129d0b08fdf9588f7c8feeb9db3def30973c092eHung-ying Tyan        private int mKeepAliveSuccessCount = 0;
756129d0b08fdf9588f7c8feeb9db3def30973c092eHung-ying Tyan
7572d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private String getAction() {
7582d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return toString();
7592d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
7602d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
7612d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public void start(SipSessionGroup group) {
762fb3a98b1d8d0ad040980d509c4c5341928b9460bHung-ying Tyan            if (!mRunning) {
763fb3a98b1d8d0ad040980d509c4c5341928b9460bHung-ying Tyan                mRunning = true;
7642d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mBackoff = 1;
7652d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mSession = (SipSessionGroup.SipSessionImpl)
7662d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        group.createSession(this);
7672d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                // return right away if no active network connection.
7682d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                if (mSession == null) return;
7692d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
7702d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                // start unregistration to clear up old registration at server
7712d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                // TODO: when rfc5626 is deployed, use reg-id and sip.instance
7722d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                // in registration to avoid adding duplicate entries to server
773bd57eeafe034cf850225db403700b5dc5db5ebccHung-ying Tyan                mMyWakeLock.acquire(mSession);
7742d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mSession.unregister();
775cb6ee06f62c20ae036a206667097f20b837b11abChia-chi Yeh                TAG = "SipAutoReg:" + mSession.getLocalProfile().getUriString();
7762d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
7772d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
7782d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
779129d0b08fdf9588f7c8feeb9db3def30973c092eHung-ying Tyan        private void startKeepAliveProcess(int interval) {
780cb6ee06f62c20ae036a206667097f20b837b11abChia-chi Yeh            if (DEBUG) Log.d(TAG, "start keepalive w interval=" + interval);
781129d0b08fdf9588f7c8feeb9db3def30973c092eHung-ying Tyan            if (mKeepAliveSession == null) {
782129d0b08fdf9588f7c8feeb9db3def30973c092eHung-ying Tyan                mKeepAliveSession = mSession.duplicate();
783129d0b08fdf9588f7c8feeb9db3def30973c092eHung-ying Tyan            } else {
784129d0b08fdf9588f7c8feeb9db3def30973c092eHung-ying Tyan                mKeepAliveSession.stopKeepAliveProcess();
785129d0b08fdf9588f7c8feeb9db3def30973c092eHung-ying Tyan            }
786129d0b08fdf9588f7c8feeb9db3def30973c092eHung-ying Tyan            try {
787129d0b08fdf9588f7c8feeb9db3def30973c092eHung-ying Tyan                mKeepAliveSession.startKeepAliveProcess(interval, this);
788129d0b08fdf9588f7c8feeb9db3def30973c092eHung-ying Tyan            } catch (SipException e) {
789129d0b08fdf9588f7c8feeb9db3def30973c092eHung-ying Tyan                Log.e(TAG, "failed to start keepalive w interval=" + interval,
790129d0b08fdf9588f7c8feeb9db3def30973c092eHung-ying Tyan                        e);
791129d0b08fdf9588f7c8feeb9db3def30973c092eHung-ying Tyan            }
792129d0b08fdf9588f7c8feeb9db3def30973c092eHung-ying Tyan        }
793129d0b08fdf9588f7c8feeb9db3def30973c092eHung-ying Tyan
794129d0b08fdf9588f7c8feeb9db3def30973c092eHung-ying Tyan        private void stopKeepAliveProcess() {
795129d0b08fdf9588f7c8feeb9db3def30973c092eHung-ying Tyan            if (mKeepAliveSession != null) {
796129d0b08fdf9588f7c8feeb9db3def30973c092eHung-ying Tyan                mKeepAliveSession.stopKeepAliveProcess();
797129d0b08fdf9588f7c8feeb9db3def30973c092eHung-ying Tyan                mKeepAliveSession = null;
798129d0b08fdf9588f7c8feeb9db3def30973c092eHung-ying Tyan            }
799129d0b08fdf9588f7c8feeb9db3def30973c092eHung-ying Tyan            mKeepAliveSuccessCount = 0;
800129d0b08fdf9588f7c8feeb9db3def30973c092eHung-ying Tyan        }
801129d0b08fdf9588f7c8feeb9db3def30973c092eHung-ying Tyan
8024a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan        // SipSessionGroup.KeepAliveProcessCallback
8034a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan        @Override
8044a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan        public void onResponse(boolean portChanged) {
8054a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan            synchronized (SipService.this) {
806e65f3a896f03bba5327ce4f3989c0422855450caHung-ying Tyan                if (portChanged) {
807129d0b08fdf9588f7c8feeb9db3def30973c092eHung-ying Tyan                    int interval = getKeepAliveInterval();
808129d0b08fdf9588f7c8feeb9db3def30973c092eHung-ying Tyan                    if (mKeepAliveSuccessCount < MIN_KEEPALIVE_SUCCESS_COUNT) {
809129d0b08fdf9588f7c8feeb9db3def30973c092eHung-ying Tyan                        Log.i(TAG, "keepalive doesn't work with interval "
810129d0b08fdf9588f7c8feeb9db3def30973c092eHung-ying Tyan                                + interval + ", past success count="
811129d0b08fdf9588f7c8feeb9db3def30973c092eHung-ying Tyan                                + mKeepAliveSuccessCount);
812129d0b08fdf9588f7c8feeb9db3def30973c092eHung-ying Tyan                        if (interval > DEFAULT_KEEPALIVE_INTERVAL) {
813129d0b08fdf9588f7c8feeb9db3def30973c092eHung-ying Tyan                            restartPortMappingLifetimeMeasurement(
814129d0b08fdf9588f7c8feeb9db3def30973c092eHung-ying Tyan                                    mSession.getLocalProfile(), interval);
815129d0b08fdf9588f7c8feeb9db3def30973c092eHung-ying Tyan                            mKeepAliveSuccessCount = 0;
816129d0b08fdf9588f7c8feeb9db3def30973c092eHung-ying Tyan                        }
817129d0b08fdf9588f7c8feeb9db3def30973c092eHung-ying Tyan                    } else {
818cb6ee06f62c20ae036a206667097f20b837b11abChia-chi Yeh                        if (DEBUG) {
819cb6ee06f62c20ae036a206667097f20b837b11abChia-chi Yeh                            Log.i(TAG, "keep keepalive going with interval "
820cb6ee06f62c20ae036a206667097f20b837b11abChia-chi Yeh                                    + interval + ", past success count="
821cb6ee06f62c20ae036a206667097f20b837b11abChia-chi Yeh                                    + mKeepAliveSuccessCount);
822cb6ee06f62c20ae036a206667097f20b837b11abChia-chi Yeh                        }
823129d0b08fdf9588f7c8feeb9db3def30973c092eHung-ying Tyan                        mKeepAliveSuccessCount /= 2;
824129d0b08fdf9588f7c8feeb9db3def30973c092eHung-ying Tyan                    }
825e65f3a896f03bba5327ce4f3989c0422855450caHung-ying Tyan                } else {
826e65f3a896f03bba5327ce4f3989c0422855450caHung-ying Tyan                    // Start keep-alive interval measurement on the first
827e65f3a896f03bba5327ce4f3989c0422855450caHung-ying Tyan                    // successfully kept-alive SipSessionGroup
828e65f3a896f03bba5327ce4f3989c0422855450caHung-ying Tyan                    startPortMappingLifetimeMeasurement(
829e65f3a896f03bba5327ce4f3989c0422855450caHung-ying Tyan                            mSession.getLocalProfile());
830129d0b08fdf9588f7c8feeb9db3def30973c092eHung-ying Tyan                    mKeepAliveSuccessCount++;
831e65f3a896f03bba5327ce4f3989c0422855450caHung-ying Tyan                }
8324a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan
8334a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                if (!mRunning || !portChanged) return;
83412750701d0f90ed0166f5ddcf588c1235efe830aHung-ying Tyan
83512750701d0f90ed0166f5ddcf588c1235efe830aHung-ying Tyan                // The keep alive process is stopped when port is changed;
83612750701d0f90ed0166f5ddcf588c1235efe830aHung-ying Tyan                // Nullify the session so that the process can be restarted
83712750701d0f90ed0166f5ddcf588c1235efe830aHung-ying Tyan                // again when the re-registration is done
83812750701d0f90ed0166f5ddcf588c1235efe830aHung-ying Tyan                mKeepAliveSession = null;
83912750701d0f90ed0166f5ddcf588c1235efe830aHung-ying Tyan
8404a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                // Acquire wake lock for the registration process. The
8414a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                // lock will be released when registration is complete.
8424a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                mMyWakeLock.acquire(mSession);
8434a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                mSession.register(EXPIRY_TIME);
8444a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan            }
8454a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan        }
8464a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan
8474a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan        // SipSessionGroup.KeepAliveProcessCallback
8484a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan        @Override
8494a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan        public void onError(int errorCode, String description) {
850cb6ee06f62c20ae036a206667097f20b837b11abChia-chi Yeh            if (DEBUG) {
851cb6ee06f62c20ae036a206667097f20b837b11abChia-chi Yeh                Log.e(TAG, "keepalive error: " + description);
852cb6ee06f62c20ae036a206667097f20b837b11abChia-chi Yeh            }
853e65f3a896f03bba5327ce4f3989c0422855450caHung-ying Tyan            onResponse(true); // re-register immediately
8544a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan        }
8554a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan
8562d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public void stop() {
857fb3a98b1d8d0ad040980d509c4c5341928b9460bHung-ying Tyan            if (!mRunning) return;
858fb3a98b1d8d0ad040980d509c4c5341928b9460bHung-ying Tyan            mRunning = false;
859bd57eeafe034cf850225db403700b5dc5db5ebccHung-ying Tyan            mMyWakeLock.release(mSession);
860bd57eeafe034cf850225db403700b5dc5db5ebccHung-ying Tyan            if (mSession != null) {
861bd57eeafe034cf850225db403700b5dc5db5ebccHung-ying Tyan                mSession.setListener(null);
862c2bd6162eddad0cdfdafc037142e043680ffa705Chia-chi Yeh                if (mNetworkType != -1 && mRegistered) mSession.unregister();
863bd57eeafe034cf850225db403700b5dc5db5ebccHung-ying Tyan            }
864fb3a98b1d8d0ad040980d509c4c5341928b9460bHung-ying Tyan
8652d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            mTimer.cancel(this);
866129d0b08fdf9588f7c8feeb9db3def30973c092eHung-ying Tyan            stopKeepAliveProcess();
8672d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
868fb3a98b1d8d0ad040980d509c4c5341928b9460bHung-ying Tyan            mRegistered = false;
869fb3a98b1d8d0ad040980d509c4c5341928b9460bHung-ying Tyan            setListener(mProxy.getListener());
8702d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
8712d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
8724a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan        public void onKeepAliveIntervalChanged() {
8734a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan            if (mKeepAliveSession != null) {
8744a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                int newInterval = getKeepAliveInterval();
875cb6ee06f62c20ae036a206667097f20b837b11abChia-chi Yeh                if (DEBUG) {
8764a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                    Log.v(TAG, "restart keepalive w interval=" + newInterval);
8774a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                }
878129d0b08fdf9588f7c8feeb9db3def30973c092eHung-ying Tyan                mKeepAliveSuccessCount = 0;
879129d0b08fdf9588f7c8feeb9db3def30973c092eHung-ying Tyan                startKeepAliveProcess(newInterval);
8804a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan            }
8814a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan        }
8824a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan
8832d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public void setListener(ISipSessionListener listener) {
8843d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan            synchronized (SipService.this) {
8853d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                mProxy.setListener(listener);
8862d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
8873d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                try {
88897963794af1e18674dd111e3ad344d90b16c922cHung-ying Tyan                    int state = (mSession == null)
88984a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                            ? SipSession.State.READY_TO_CALL
89097963794af1e18674dd111e3ad344d90b16c922cHung-ying Tyan                            : mSession.getState();
89184a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                    if ((state == SipSession.State.REGISTERING)
89284a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                            || (state == SipSession.State.DEREGISTERING)) {
8933d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                        mProxy.onRegistering(mSession);
8943d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                    } else if (mRegistered) {
8953d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                        int duration = (int)
8963d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                                (mExpiryTime - SystemClock.elapsedRealtime());
8973d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                        mProxy.onRegistrationDone(mSession, duration);
89897963794af1e18674dd111e3ad344d90b16c922cHung-ying Tyan                    } else if (mErrorCode != SipErrorCode.NO_ERROR) {
8993d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                        if (mErrorCode == SipErrorCode.TIME_OUT) {
9003d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                            mProxy.onRegistrationTimeout(mSession);
9013d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                        } else {
90297963794af1e18674dd111e3ad344d90b16c922cHung-ying Tyan                            mProxy.onRegistrationFailed(mSession, mErrorCode,
90397963794af1e18674dd111e3ad344d90b16c922cHung-ying Tyan                                    mErrorMessage);
9043d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                        }
905c2bd6162eddad0cdfdafc037142e043680ffa705Chia-chi Yeh                    } else if (mNetworkType == -1) {
906fb3a98b1d8d0ad040980d509c4c5341928b9460bHung-ying Tyan                        mProxy.onRegistrationFailed(mSession,
907fb3a98b1d8d0ad040980d509c4c5341928b9460bHung-ying Tyan                                SipErrorCode.DATA_CONNECTION_LOST,
908fb3a98b1d8d0ad040980d509c4c5341928b9460bHung-ying Tyan                                "no data connection");
909fb3a98b1d8d0ad040980d509c4c5341928b9460bHung-ying Tyan                    } else if (!mRunning) {
910fb3a98b1d8d0ad040980d509c4c5341928b9460bHung-ying Tyan                        mProxy.onRegistrationFailed(mSession,
911fb3a98b1d8d0ad040980d509c4c5341928b9460bHung-ying Tyan                                SipErrorCode.CLIENT_ERROR,
912fb3a98b1d8d0ad040980d509c4c5341928b9460bHung-ying Tyan                                "registration not running");
913fb3a98b1d8d0ad040980d509c4c5341928b9460bHung-ying Tyan                    } else {
914fb3a98b1d8d0ad040980d509c4c5341928b9460bHung-ying Tyan                        mProxy.onRegistrationFailed(mSession,
915fb3a98b1d8d0ad040980d509c4c5341928b9460bHung-ying Tyan                                SipErrorCode.IN_PROGRESS,
916fb3a98b1d8d0ad040980d509c4c5341928b9460bHung-ying Tyan                                String.valueOf(state));
9173d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                    }
9183d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                } catch (Throwable t) {
9193d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                    Log.w(TAG, "setListener(): " + t);
9202d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                }
9212d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
9222d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
9232d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
9242d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public boolean isRegistered() {
9252d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return mRegistered;
9262d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
9272d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
928bd57eeafe034cf850225db403700b5dc5db5ebccHung-ying Tyan        // timeout handler: re-register
9294a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan        @Override
9302d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public void run() {
931fb3a98b1d8d0ad040980d509c4c5341928b9460bHung-ying Tyan            synchronized (SipService.this) {
932fb3a98b1d8d0ad040980d509c4c5341928b9460bHung-ying Tyan                if (!mRunning) return;
933fb3a98b1d8d0ad040980d509c4c5341928b9460bHung-ying Tyan
934fb3a98b1d8d0ad040980d509c4c5341928b9460bHung-ying Tyan                mErrorCode = SipErrorCode.NO_ERROR;
935fb3a98b1d8d0ad040980d509c4c5341928b9460bHung-ying Tyan                mErrorMessage = null;
9364a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                if (DEBUG) Log.d(TAG, "registering");
937c2bd6162eddad0cdfdafc037142e043680ffa705Chia-chi Yeh                if (mNetworkType != -1) {
938bd57eeafe034cf850225db403700b5dc5db5ebccHung-ying Tyan                    mMyWakeLock.acquire(mSession);
939bd57eeafe034cf850225db403700b5dc5db5ebccHung-ying Tyan                    mSession.register(EXPIRY_TIME);
940bd57eeafe034cf850225db403700b5dc5db5ebccHung-ying Tyan                }
9412d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
9422d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
9432d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
9442d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private void restart(int duration) {
945cb6ee06f62c20ae036a206667097f20b837b11abChia-chi Yeh            Log.d(TAG, "Refresh registration " + duration + "s later.");
9462d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            mTimer.cancel(this);
9472d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            mTimer.set(duration * 1000, this);
9482d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
9492d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
9502d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private int backoffDuration() {
9512d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            int duration = SHORT_EXPIRY_TIME * mBackoff;
9522d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            if (duration > 3600) {
9532d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                duration = 3600;
9542d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            } else {
9552d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mBackoff *= 2;
9562d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
9572d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return duration;
9582d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
9592d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
9602d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        @Override
9612d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public void onRegistering(ISipSession session) {
962c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan            if (DEBUG) Log.d(TAG, "onRegistering(): " + session);
9632d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            synchronized (SipService.this) {
964fb3a98b1d8d0ad040980d509c4c5341928b9460bHung-ying Tyan                if (notCurrentSession(session)) return;
965fb3a98b1d8d0ad040980d509c4c5341928b9460bHung-ying Tyan
9662d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mRegistered = false;
9673d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                mProxy.onRegistering(session);
9682d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
9692d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
9702d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
971fb3a98b1d8d0ad040980d509c4c5341928b9460bHung-ying Tyan        private boolean notCurrentSession(ISipSession session) {
972fb3a98b1d8d0ad040980d509c4c5341928b9460bHung-ying Tyan            if (session != mSession) {
973fb3a98b1d8d0ad040980d509c4c5341928b9460bHung-ying Tyan                ((SipSessionGroup.SipSessionImpl) session).setListener(null);
974bd57eeafe034cf850225db403700b5dc5db5ebccHung-ying Tyan                mMyWakeLock.release(session);
975fb3a98b1d8d0ad040980d509c4c5341928b9460bHung-ying Tyan                return true;
976fb3a98b1d8d0ad040980d509c4c5341928b9460bHung-ying Tyan            }
977fb3a98b1d8d0ad040980d509c4c5341928b9460bHung-ying Tyan            return !mRunning;
978fb3a98b1d8d0ad040980d509c4c5341928b9460bHung-ying Tyan        }
979fb3a98b1d8d0ad040980d509c4c5341928b9460bHung-ying Tyan
9802d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        @Override
9812d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public void onRegistrationDone(ISipSession session, int duration) {
982c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan            if (DEBUG) Log.d(TAG, "onRegistrationDone(): " + session);
9832d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            synchronized (SipService.this) {
984fb3a98b1d8d0ad040980d509c4c5341928b9460bHung-ying Tyan                if (notCurrentSession(session)) return;
9853d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan
9863d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                mProxy.onRegistrationDone(session, duration);
9873d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan
9882d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                if (duration > 0) {
9892d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    mExpiryTime = SystemClock.elapsedRealtime()
9902d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                            + (duration * 1000);
9912d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
9922d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    if (!mRegistered) {
9932d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        mRegistered = true;
9942d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        // allow some overlap to avoid call drop during renew
9952d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        duration -= MIN_EXPIRY_TIME;
9962d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        if (duration < MIN_EXPIRY_TIME) {
9972d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                            duration = MIN_EXPIRY_TIME;
9982d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        }
9992d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        restart(duration);
10002d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
10014a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                        SipProfile localProfile = mSession.getLocalProfile();
10024a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                        if ((mKeepAliveSession == null) && (isBehindNAT(mLocalIp)
10034a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                                || localProfile.getSendKeepAlive())) {
1004129d0b08fdf9588f7c8feeb9db3def30973c092eHung-ying Tyan                            startKeepAliveProcess(getKeepAliveInterval());
10052d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        }
10062d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    }
1007bd57eeafe034cf850225db403700b5dc5db5ebccHung-ying Tyan                    mMyWakeLock.release(session);
10082d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                } else {
10092d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    mRegistered = false;
10102d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    mExpiryTime = -1L;
1011c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan                    if (DEBUG) Log.d(TAG, "Refresh registration immediately");
10122d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    run();
10132d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                }
10142d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
10152d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
10162d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
10172d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        @Override
101897963794af1e18674dd111e3ad344d90b16c922cHung-ying Tyan        public void onRegistrationFailed(ISipSession session, int errorCode,
101997963794af1e18674dd111e3ad344d90b16c922cHung-ying Tyan                String message) {
1020c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan            if (DEBUG) Log.d(TAG, "onRegistrationFailed(): " + session + ": "
102197963794af1e18674dd111e3ad344d90b16c922cHung-ying Tyan                    + SipErrorCode.toString(errorCode) + ": " + message);
10222d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            synchronized (SipService.this) {
1023fb3a98b1d8d0ad040980d509c4c5341928b9460bHung-ying Tyan                if (notCurrentSession(session)) return;
10243d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan
1025ee8a884f3504c981be8a1d6888b4590a0a394e05Hung-ying Tyan                switch (errorCode) {
1026ee8a884f3504c981be8a1d6888b4590a0a394e05Hung-ying Tyan                    case SipErrorCode.INVALID_CREDENTIALS:
1027ee8a884f3504c981be8a1d6888b4590a0a394e05Hung-ying Tyan                    case SipErrorCode.SERVER_UNREACHABLE:
1028ee8a884f3504c981be8a1d6888b4590a0a394e05Hung-ying Tyan                        if (DEBUG) Log.d(TAG, "   pause auto-registration");
1029ee8a884f3504c981be8a1d6888b4590a0a394e05Hung-ying Tyan                        stop();
1030685b61b7117dbae94c5ceb5de4546ad23a4d3d0fHung-ying Tyan                        break;
1031ee8a884f3504c981be8a1d6888b4590a0a394e05Hung-ying Tyan                    default:
1032ee8a884f3504c981be8a1d6888b4590a0a394e05Hung-ying Tyan                        restartLater();
10332d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                }
1034fb3a98b1d8d0ad040980d509c4c5341928b9460bHung-ying Tyan
1035fb3a98b1d8d0ad040980d509c4c5341928b9460bHung-ying Tyan                mErrorCode = errorCode;
1036fb3a98b1d8d0ad040980d509c4c5341928b9460bHung-ying Tyan                mErrorMessage = message;
1037fb3a98b1d8d0ad040980d509c4c5341928b9460bHung-ying Tyan                mProxy.onRegistrationFailed(session, errorCode, message);
1038bd57eeafe034cf850225db403700b5dc5db5ebccHung-ying Tyan                mMyWakeLock.release(session);
10392d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
10402d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
10412d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
10422d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        @Override
10432d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public void onRegistrationTimeout(ISipSession session) {
1044c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan            if (DEBUG) Log.d(TAG, "onRegistrationTimeout(): " + session);
10452d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            synchronized (SipService.this) {
1046fb3a98b1d8d0ad040980d509c4c5341928b9460bHung-ying Tyan                if (notCurrentSession(session)) return;
1047fb3a98b1d8d0ad040980d509c4c5341928b9460bHung-ying Tyan
10483d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                mErrorCode = SipErrorCode.TIME_OUT;
10493d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                mProxy.onRegistrationTimeout(session);
1050ee8a884f3504c981be8a1d6888b4590a0a394e05Hung-ying Tyan                restartLater();
1051bd57eeafe034cf850225db403700b5dc5db5ebccHung-ying Tyan                mMyWakeLock.release(session);
10522d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
10532d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
10542d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
1055ee8a884f3504c981be8a1d6888b4590a0a394e05Hung-ying Tyan        private void restartLater() {
10562d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            mRegistered = false;
10572d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            restart(backoffDuration());
10582d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
10592d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
10602d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
10612d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private class ConnectivityReceiver extends BroadcastReceiver {
10622d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        @Override
1063ee59e6a9fc69241b286acb7b55a22b8393c81222Chia-chi Yeh        public void onReceive(Context context, Intent intent) {
1064ee59e6a9fc69241b286acb7b55a22b8393c81222Chia-chi Yeh            Bundle bundle = intent.getExtras();
1065ee59e6a9fc69241b286acb7b55a22b8393c81222Chia-chi Yeh            if (bundle != null) {
1066ee59e6a9fc69241b286acb7b55a22b8393c81222Chia-chi Yeh                final NetworkInfo info = (NetworkInfo)
1067ee59e6a9fc69241b286acb7b55a22b8393c81222Chia-chi Yeh                        bundle.get(ConnectivityManager.EXTRA_NETWORK_INFO);
1068c4b87477c076d61062950becc132b7483e3fb198Hung-ying Tyan
1069ee59e6a9fc69241b286acb7b55a22b8393c81222Chia-chi Yeh                // Run the handler in MyExecutor to be protected by wake lock
1070ee59e6a9fc69241b286acb7b55a22b8393c81222Chia-chi Yeh                mExecutor.execute(new Runnable() {
1071ee59e6a9fc69241b286acb7b55a22b8393c81222Chia-chi Yeh                    public void run() {
1072ee59e6a9fc69241b286acb7b55a22b8393c81222Chia-chi Yeh                        onConnectivityChanged(info);
10732d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    }
1074ee59e6a9fc69241b286acb7b55a22b8393c81222Chia-chi Yeh                });
10752d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
10762d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
1077ee59e6a9fc69241b286acb7b55a22b8393c81222Chia-chi Yeh    }
1078ee59e6a9fc69241b286acb7b55a22b8393c81222Chia-chi Yeh
1079ee59e6a9fc69241b286acb7b55a22b8393c81222Chia-chi Yeh    private void registerReceivers() {
1080ee59e6a9fc69241b286acb7b55a22b8393c81222Chia-chi Yeh        mContext.registerReceiver(mConnectivityReceiver,
1081ee59e6a9fc69241b286acb7b55a22b8393c81222Chia-chi Yeh                new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
1082ee59e6a9fc69241b286acb7b55a22b8393c81222Chia-chi Yeh        if (DEBUG) Log.d(TAG, " +++ register receivers");
1083ee59e6a9fc69241b286acb7b55a22b8393c81222Chia-chi Yeh    }
10842d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
1085ee59e6a9fc69241b286acb7b55a22b8393c81222Chia-chi Yeh    private void unregisterReceivers() {
1086ee59e6a9fc69241b286acb7b55a22b8393c81222Chia-chi Yeh        mContext.unregisterReceiver(mConnectivityReceiver);
1087ee59e6a9fc69241b286acb7b55a22b8393c81222Chia-chi Yeh        if (DEBUG) Log.d(TAG, " --- unregister receivers");
1088ee59e6a9fc69241b286acb7b55a22b8393c81222Chia-chi Yeh
1089ee59e6a9fc69241b286acb7b55a22b8393c81222Chia-chi Yeh        // Reset variables maintained by ConnectivityReceiver.
1090ee59e6a9fc69241b286acb7b55a22b8393c81222Chia-chi Yeh        mWifiLock.release();
1091c2bd6162eddad0cdfdafc037142e043680ffa705Chia-chi Yeh        mNetworkType = -1;
1092c2bd6162eddad0cdfdafc037142e043680ffa705Chia-chi Yeh    }
1093c2bd6162eddad0cdfdafc037142e043680ffa705Chia-chi Yeh
1094c2bd6162eddad0cdfdafc037142e043680ffa705Chia-chi Yeh    private void updateWakeLocks() {
1095c2bd6162eddad0cdfdafc037142e043680ffa705Chia-chi Yeh        for (SipSessionGroupExt group : mSipGroups.values()) {
1096c2bd6162eddad0cdfdafc037142e043680ffa705Chia-chi Yeh            if (group.isOpenedToReceiveCalls()) {
1097c2bd6162eddad0cdfdafc037142e043680ffa705Chia-chi Yeh                // Also grab the WifiLock when we are disconnected, so the
1098c2bd6162eddad0cdfdafc037142e043680ffa705Chia-chi Yeh                // system will keep trying to reconnect. It will be released
1099c2bd6162eddad0cdfdafc037142e043680ffa705Chia-chi Yeh                // when the system eventually connects to something else.
1100c2bd6162eddad0cdfdafc037142e043680ffa705Chia-chi Yeh                if (mNetworkType == ConnectivityManager.TYPE_WIFI || mNetworkType == -1) {
1101c2bd6162eddad0cdfdafc037142e043680ffa705Chia-chi Yeh                    mWifiLock.acquire();
1102c2bd6162eddad0cdfdafc037142e043680ffa705Chia-chi Yeh                } else {
1103c2bd6162eddad0cdfdafc037142e043680ffa705Chia-chi Yeh                    mWifiLock.release();
1104c2bd6162eddad0cdfdafc037142e043680ffa705Chia-chi Yeh                }
1105c2bd6162eddad0cdfdafc037142e043680ffa705Chia-chi Yeh                return;
1106c2bd6162eddad0cdfdafc037142e043680ffa705Chia-chi Yeh            }
1107c2bd6162eddad0cdfdafc037142e043680ffa705Chia-chi Yeh        }
1108c2bd6162eddad0cdfdafc037142e043680ffa705Chia-chi Yeh        mWifiLock.release();
1109c2bd6162eddad0cdfdafc037142e043680ffa705Chia-chi Yeh        mMyWakeLock.reset(); // in case there's a leak
1110ee59e6a9fc69241b286acb7b55a22b8393c81222Chia-chi Yeh    }
1111ee59e6a9fc69241b286acb7b55a22b8393c81222Chia-chi Yeh
1112ee59e6a9fc69241b286acb7b55a22b8393c81222Chia-chi Yeh    private synchronized void onConnectivityChanged(NetworkInfo info) {
1113ee59e6a9fc69241b286acb7b55a22b8393c81222Chia-chi Yeh        // We only care about the default network, and getActiveNetworkInfo()
1114ee59e6a9fc69241b286acb7b55a22b8393c81222Chia-chi Yeh        // is the only way to distinguish them. However, as broadcasts are
1115ee59e6a9fc69241b286acb7b55a22b8393c81222Chia-chi Yeh        // delivered asynchronously, we might miss DISCONNECTED events from
1116ee59e6a9fc69241b286acb7b55a22b8393c81222Chia-chi Yeh        // getActiveNetworkInfo(), which is critical to our SIP stack. To
1117ee59e6a9fc69241b286acb7b55a22b8393c81222Chia-chi Yeh        // solve this, if it is a DISCONNECTED event to our current network,
1118ee59e6a9fc69241b286acb7b55a22b8393c81222Chia-chi Yeh        // respect it. Otherwise get a new one from getActiveNetworkInfo().
1119c2bd6162eddad0cdfdafc037142e043680ffa705Chia-chi Yeh        if (info == null || info.isConnected() || info.getType() != mNetworkType) {
112012bec5ddf58ad3a69728810480e6194c806567d6Hung-ying Tyan            ConnectivityManager cm = (ConnectivityManager)
112112bec5ddf58ad3a69728810480e6194c806567d6Hung-ying Tyan                    mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
1122ee59e6a9fc69241b286acb7b55a22b8393c81222Chia-chi Yeh            info = cm.getActiveNetworkInfo();
112312bec5ddf58ad3a69728810480e6194c806567d6Hung-ying Tyan        }
112412bec5ddf58ad3a69728810480e6194c806567d6Hung-ying Tyan
1125ee59e6a9fc69241b286acb7b55a22b8393c81222Chia-chi Yeh        // Some devices limit SIP on Wi-Fi. In this case, if we are not on
1126ee59e6a9fc69241b286acb7b55a22b8393c81222Chia-chi Yeh        // Wi-Fi, treat it as a DISCONNECTED event.
1127c2bd6162eddad0cdfdafc037142e043680ffa705Chia-chi Yeh        int networkType = (info != null && info.isConnected()) ? info.getType() : -1;
1128c2bd6162eddad0cdfdafc037142e043680ffa705Chia-chi Yeh        if (mSipOnWifiOnly && networkType != ConnectivityManager.TYPE_WIFI) {
1129c2bd6162eddad0cdfdafc037142e043680ffa705Chia-chi Yeh            networkType = -1;
1130c2bd6162eddad0cdfdafc037142e043680ffa705Chia-chi Yeh        }
11312d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
1132ee59e6a9fc69241b286acb7b55a22b8393c81222Chia-chi Yeh        // Ignore the event if the current active network is not changed.
1133c2bd6162eddad0cdfdafc037142e043680ffa705Chia-chi Yeh        if (mNetworkType == networkType) {
1134ee59e6a9fc69241b286acb7b55a22b8393c81222Chia-chi Yeh            return;
1135ee59e6a9fc69241b286acb7b55a22b8393c81222Chia-chi Yeh        }
1136ee59e6a9fc69241b286acb7b55a22b8393c81222Chia-chi Yeh        if (DEBUG) {
1137ee59e6a9fc69241b286acb7b55a22b8393c81222Chia-chi Yeh            Log.d(TAG, "onConnectivityChanged(): " + mNetworkType +
1138ee59e6a9fc69241b286acb7b55a22b8393c81222Chia-chi Yeh                    " -> " + networkType);
1139ee59e6a9fc69241b286acb7b55a22b8393c81222Chia-chi Yeh        }
11402d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
1141ee59e6a9fc69241b286acb7b55a22b8393c81222Chia-chi Yeh        try {
1142c2bd6162eddad0cdfdafc037142e043680ffa705Chia-chi Yeh            if (mNetworkType != -1) {
1143ee59e6a9fc69241b286acb7b55a22b8393c81222Chia-chi Yeh                mLocalIp = null;
1144ee59e6a9fc69241b286acb7b55a22b8393c81222Chia-chi Yeh                stopPortMappingMeasurement();
1145ee59e6a9fc69241b286acb7b55a22b8393c81222Chia-chi Yeh                for (SipSessionGroupExt group : mSipGroups.values()) {
1146ee59e6a9fc69241b286acb7b55a22b8393c81222Chia-chi Yeh                    group.onConnectivityChanged(false);
1147ee59e6a9fc69241b286acb7b55a22b8393c81222Chia-chi Yeh                }
11482d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
1149ee59e6a9fc69241b286acb7b55a22b8393c81222Chia-chi Yeh            mNetworkType = networkType;
1150b17eae9e227475a323f61519abc8a7d35ddf8828Hung-ying Tyan
1151c2bd6162eddad0cdfdafc037142e043680ffa705Chia-chi Yeh            if (mNetworkType != -1) {
1152ee59e6a9fc69241b286acb7b55a22b8393c81222Chia-chi Yeh                mLocalIp = determineLocalIp();
1153ee59e6a9fc69241b286acb7b55a22b8393c81222Chia-chi Yeh                mKeepAliveInterval = -1;
1154ee59e6a9fc69241b286acb7b55a22b8393c81222Chia-chi Yeh                mLastGoodKeepAliveInterval = DEFAULT_KEEPALIVE_INTERVAL;
1155ee59e6a9fc69241b286acb7b55a22b8393c81222Chia-chi Yeh                for (SipSessionGroupExt group : mSipGroups.values()) {
1156ee59e6a9fc69241b286acb7b55a22b8393c81222Chia-chi Yeh                    group.onConnectivityChanged(true);
11572d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                }
11582d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
1159c2bd6162eddad0cdfdafc037142e043680ffa705Chia-chi Yeh            updateWakeLocks();
1160ee59e6a9fc69241b286acb7b55a22b8393c81222Chia-chi Yeh        } catch (SipException e) {
1161ee59e6a9fc69241b286acb7b55a22b8393c81222Chia-chi Yeh            Log.e(TAG, "onConnectivityChanged()", e);
11622d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
11632d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
11642d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
1165bd57eeafe034cf850225db403700b5dc5db5ebccHung-ying Tyan    private static Looper createLooper() {
1166bd57eeafe034cf850225db403700b5dc5db5ebccHung-ying Tyan        HandlerThread thread = new HandlerThread("SipService.Executor");
1167bd57eeafe034cf850225db403700b5dc5db5ebccHung-ying Tyan        thread.start();
1168bd57eeafe034cf850225db403700b5dc5db5ebccHung-ying Tyan        return thread.getLooper();
1169bd57eeafe034cf850225db403700b5dc5db5ebccHung-ying Tyan    }
1170bd57eeafe034cf850225db403700b5dc5db5ebccHung-ying Tyan
1171bd57eeafe034cf850225db403700b5dc5db5ebccHung-ying Tyan    // Executes immediate tasks in a single thread.
1172bd57eeafe034cf850225db403700b5dc5db5ebccHung-ying Tyan    // Hold/release wake lock for running tasks
11735621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan    private class MyExecutor extends Handler implements Executor {
1174b17eae9e227475a323f61519abc8a7d35ddf8828Hung-ying Tyan        MyExecutor() {
1175b17eae9e227475a323f61519abc8a7d35ddf8828Hung-ying Tyan            super(createLooper());
1176b17eae9e227475a323f61519abc8a7d35ddf8828Hung-ying Tyan        }
1177b17eae9e227475a323f61519abc8a7d35ddf8828Hung-ying Tyan
11785621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        @Override
11795621554033089d1c07d53f56e8cd9787125d6e28Hung-ying Tyan        public void execute(Runnable task) {
1180bd57eeafe034cf850225db403700b5dc5db5ebccHung-ying Tyan            mMyWakeLock.acquire(task);
1181b17eae9e227475a323f61519abc8a7d35ddf8828Hung-ying Tyan            Message.obtain(this, 0/* don't care */, task).sendToTarget();
1182b17eae9e227475a323f61519abc8a7d35ddf8828Hung-ying Tyan        }
1183b17eae9e227475a323f61519abc8a7d35ddf8828Hung-ying Tyan
1184b17eae9e227475a323f61519abc8a7d35ddf8828Hung-ying Tyan        @Override
1185b17eae9e227475a323f61519abc8a7d35ddf8828Hung-ying Tyan        public void handleMessage(Message msg) {
1186b17eae9e227475a323f61519abc8a7d35ddf8828Hung-ying Tyan            if (msg.obj instanceof Runnable) {
1187bd57eeafe034cf850225db403700b5dc5db5ebccHung-ying Tyan                executeInternal((Runnable) msg.obj);
1188b17eae9e227475a323f61519abc8a7d35ddf8828Hung-ying Tyan            } else {
1189b17eae9e227475a323f61519abc8a7d35ddf8828Hung-ying Tyan                Log.w(TAG, "can't handle msg: " + msg);
1190b17eae9e227475a323f61519abc8a7d35ddf8828Hung-ying Tyan            }
1191b17eae9e227475a323f61519abc8a7d35ddf8828Hung-ying Tyan        }
1192bd57eeafe034cf850225db403700b5dc5db5ebccHung-ying Tyan
1193bd57eeafe034cf850225db403700b5dc5db5ebccHung-ying Tyan        private void executeInternal(Runnable task) {
1194bd57eeafe034cf850225db403700b5dc5db5ebccHung-ying Tyan            try {
1195bd57eeafe034cf850225db403700b5dc5db5ebccHung-ying Tyan                task.run();
1196bd57eeafe034cf850225db403700b5dc5db5ebccHung-ying Tyan            } catch (Throwable t) {
1197bd57eeafe034cf850225db403700b5dc5db5ebccHung-ying Tyan                Log.e(TAG, "run task: " + task, t);
1198bd57eeafe034cf850225db403700b5dc5db5ebccHung-ying Tyan            } finally {
1199bd57eeafe034cf850225db403700b5dc5db5ebccHung-ying Tyan                mMyWakeLock.release(task);
1200bd57eeafe034cf850225db403700b5dc5db5ebccHung-ying Tyan            }
1201bd57eeafe034cf850225db403700b5dc5db5ebccHung-ying Tyan        }
1202bd57eeafe034cf850225db403700b5dc5db5ebccHung-ying Tyan    }
12032d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang}
1204