19c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan/*
29c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan * Copyright (C) 2010 The Android Open Source Project
39c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan *
49c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan * Licensed under the Apache License, Version 2.0 (the "License");
59c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan * you may not use this file except in compliance with the License.
69c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan * You may obtain a copy of the License at
79c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan *
89c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan *      http://www.apache.org/licenses/LICENSE-2.0
99c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan *
109c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan * Unless required by applicable law or agreed to in writing, software
119c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan * distributed under the License is distributed on an "AS IS" BASIS,
129c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
139c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan * See the License for the specific language governing permissions and
149c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan * limitations under the License.
159c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan */
169c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
179c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanpackage com.android.server.sip;
189c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
199c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport gov.nist.javax.sip.clientauthutils.AccountManager;
209c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport gov.nist.javax.sip.clientauthutils.UserCredentials;
219c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport gov.nist.javax.sip.header.ProxyAuthenticate;
22f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo syncimport gov.nist.javax.sip.header.ReferTo;
23f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo syncimport gov.nist.javax.sip.header.SIPHeaderNames;
24f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo syncimport gov.nist.javax.sip.header.StatusLine;
259c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport gov.nist.javax.sip.header.WWWAuthenticate;
2622ecc3df834674605daf86f7edf20169b6ca800brepo syncimport gov.nist.javax.sip.header.extensions.ReferredByHeader;
2722ecc3df834674605daf86f7edf20169b6ca800brepo syncimport gov.nist.javax.sip.header.extensions.ReplacesHeader;
289c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport gov.nist.javax.sip.message.SIPMessage;
29f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo syncimport gov.nist.javax.sip.message.SIPResponse;
309c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
319c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport android.net.sip.ISipSession;
329c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport android.net.sip.ISipSessionListener;
339c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport android.net.sip.SipErrorCode;
349c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport android.net.sip.SipProfile;
359c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport android.net.sip.SipSession;
368a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyanimport android.net.sip.SipSessionAdapter;
379c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport android.text.TextUtils;
389329db04f13480ccdff013dcc00cdb96f12a921cWink Savilleimport android.telephony.Rlog;
399c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
409c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport java.io.IOException;
419c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport java.io.UnsupportedEncodingException;
429c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport java.net.DatagramSocket;
431b5111f16c1401437d577dfd547af10aa691d44fChia-chi Yehimport java.net.InetAddress;
449c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport java.net.UnknownHostException;
459c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport java.text.ParseException;
469c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport java.util.EventObject;
479c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport java.util.HashMap;
489c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport java.util.Map;
499c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport java.util.Properties;
509c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
519c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport javax.sip.ClientTransaction;
529c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport javax.sip.Dialog;
539c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport javax.sip.DialogTerminatedEvent;
549c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport javax.sip.IOExceptionEvent;
55e8782e2fdfd256296a0d1fd2a1c3f98d58103eaaHung-ying Tyanimport javax.sip.ObjectInUseException;
569c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport javax.sip.RequestEvent;
579c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport javax.sip.ResponseEvent;
589c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport javax.sip.ServerTransaction;
599c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport javax.sip.SipException;
609c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport javax.sip.SipFactory;
619c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport javax.sip.SipListener;
629c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport javax.sip.SipProvider;
639c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport javax.sip.SipStack;
649c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport javax.sip.TimeoutEvent;
659c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport javax.sip.Transaction;
669c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport javax.sip.TransactionTerminatedEvent;
679c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport javax.sip.address.Address;
689c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport javax.sip.address.SipURI;
699c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport javax.sip.header.CSeqHeader;
705e75bad33a53a468440871e4dbf83d05e6642de4Chia-chi Yehimport javax.sip.header.ContactHeader;
719c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport javax.sip.header.ExpiresHeader;
729c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport javax.sip.header.FromHeader;
73f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo syncimport javax.sip.header.HeaderAddress;
749c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport javax.sip.header.MinExpiresHeader;
75f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo syncimport javax.sip.header.ReferToHeader;
769c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport javax.sip.header.ViaHeader;
779c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport javax.sip.message.Message;
789c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport javax.sip.message.Request;
799c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport javax.sip.message.Response;
809c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
81f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync
829c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan/**
839c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan * Manages {@link ISipSession}'s for a SIP account.
849c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan */
859c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanclass SipSessionGroup implements SipListener {
869c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private static final String TAG = "SipSession";
879329db04f13480ccdff013dcc00cdb96f12a921cWink Saville    private static final boolean DBG = false;
889329db04f13480ccdff013dcc00cdb96f12a921cWink Saville    private static final boolean DBG_PING = false;
899c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private static final String ANONYMOUS = "anonymous";
90b891d6f795fd5ba90455f8071b03404ff0a5b1aaChung-yih Wang    // Limit the size of thread pool to 1 for the order issue when the phone is
91b891d6f795fd5ba90455f8071b03404ff0a5b1aaChung-yih Wang    // waken up from sleep and there are many packets to be processed in the SIP
92b891d6f795fd5ba90455f8071b03404ff0a5b1aaChung-yih Wang    // stack. Note: The default thread pool size in NIST SIP stack is -1 which is
93b891d6f795fd5ba90455f8071b03404ff0a5b1aaChung-yih Wang    // unlimited.
94b891d6f795fd5ba90455f8071b03404ff0a5b1aaChung-yih Wang    private static final String THREAD_POOL_SIZE = "1";
959c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private static final int EXPIRY_TIME = 3600; // in seconds
969c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private static final int CANCEL_CALL_TIMER = 3; // in seconds
97ceb525d85512bde884b0f01a3aa93f2943dcf5f3Hung-ying Tyan    private static final int END_CALL_TIMER = 3; // in seconds
9837f93394fe5c7cfede3a780214fa99a9e2a69133Chia-chi Yeh    private static final int KEEPALIVE_TIMEOUT = 5; // in seconds
990f8ceff66069c98480481241ba9d70dc34448188Hung-ying Tyan    private static final int INCALL_KEEPALIVE_INTERVAL = 10; // in seconds
100acedadc1967457ac2f8981c67f884fd8c0ee853cHung-ying Tyan    private static final long WAKE_LOCK_HOLDING_TIME = 500; // in milliseconds
1019c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
1029c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private static final EventObject DEREGISTER = new EventObject("Deregister");
1039c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private static final EventObject END_CALL = new EventObject("End call");
1049c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
1059c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private final SipProfile mLocalProfile;
1069c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private final String mPassword;
1079c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
1089c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private SipStack mSipStack;
1099c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private SipHelper mSipHelper;
1109c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
1119c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    // session that processes INVITE requests
1129c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private SipSessionImpl mCallReceiverSession;
1139c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private String mLocalIp;
1149c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
1158a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan    private SipWakeupTimer mWakeupTimer;
116acedadc1967457ac2f8981c67f884fd8c0ee853cHung-ying Tyan    private SipWakeLock mWakeLock;
117acedadc1967457ac2f8981c67f884fd8c0ee853cHung-ying Tyan
1189c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    // call-id-to-SipSession map
1199c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private Map<String, SipSessionImpl> mSessionMap =
1209c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            new HashMap<String, SipSessionImpl>();
1219c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
1223efff6c5840a99faadc3ee6197940c3290f65a62Hung-ying Tyan    // external address observed from any response
1233efff6c5840a99faadc3ee6197940c3290f65a62Hung-ying Tyan    private String mExternalIp;
1243efff6c5840a99faadc3ee6197940c3290f65a62Hung-ying Tyan    private int mExternalPort;
1253efff6c5840a99faadc3ee6197940c3290f65a62Hung-ying Tyan
1269c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    /**
1271b5111f16c1401437d577dfd547af10aa691d44fChia-chi Yeh     * @param profile the local profile with password crossed out
1289c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan     * @param password the password of the profile
1299329db04f13480ccdff013dcc00cdb96f12a921cWink Saville     * @throws SipException if cannot assign requested address
1309c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan     */
1311b5111f16c1401437d577dfd547af10aa691d44fChia-chi Yeh    public SipSessionGroup(SipProfile profile, String password,
1321b5111f16c1401437d577dfd547af10aa691d44fChia-chi Yeh            SipWakeupTimer timer, SipWakeLock wakeLock) throws SipException {
1331b5111f16c1401437d577dfd547af10aa691d44fChia-chi Yeh        mLocalProfile = profile;
1349c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        mPassword = password;
1358a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        mWakeupTimer = timer;
136acedadc1967457ac2f8981c67f884fd8c0ee853cHung-ying Tyan        mWakeLock = wakeLock;
1371b5111f16c1401437d577dfd547af10aa691d44fChia-chi Yeh        reset();
1389c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
1399c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
1408a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan    // TODO: remove this method once SipWakeupTimer can better handle variety
1418a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan    // of timeout values
1428a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan    void setWakeupTimer(SipWakeupTimer timer) {
1438a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        mWakeupTimer = timer;
1448a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan    }
1458a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan
1461b5111f16c1401437d577dfd547af10aa691d44fChia-chi Yeh    synchronized void reset() throws SipException {
1479c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        Properties properties = new Properties();
1481b5111f16c1401437d577dfd547af10aa691d44fChia-chi Yeh
1491b5111f16c1401437d577dfd547af10aa691d44fChia-chi Yeh        String protocol = mLocalProfile.getProtocol();
1501b5111f16c1401437d577dfd547af10aa691d44fChia-chi Yeh        int port = mLocalProfile.getPort();
1511b5111f16c1401437d577dfd547af10aa691d44fChia-chi Yeh        String server = mLocalProfile.getProxyAddress();
1521b5111f16c1401437d577dfd547af10aa691d44fChia-chi Yeh
1531b5111f16c1401437d577dfd547af10aa691d44fChia-chi Yeh        if (!TextUtils.isEmpty(server)) {
1541b5111f16c1401437d577dfd547af10aa691d44fChia-chi Yeh            properties.setProperty("javax.sip.OUTBOUND_PROXY",
1551b5111f16c1401437d577dfd547af10aa691d44fChia-chi Yeh                    server + ':' + port + '/' + protocol);
1561b5111f16c1401437d577dfd547af10aa691d44fChia-chi Yeh        } else {
1571b5111f16c1401437d577dfd547af10aa691d44fChia-chi Yeh            server = mLocalProfile.getSipDomain();
1581b5111f16c1401437d577dfd547af10aa691d44fChia-chi Yeh        }
1591b5111f16c1401437d577dfd547af10aa691d44fChia-chi Yeh        if (server.startsWith("[") && server.endsWith("]")) {
1601b5111f16c1401437d577dfd547af10aa691d44fChia-chi Yeh            server = server.substring(1, server.length() - 1);
1611b5111f16c1401437d577dfd547af10aa691d44fChia-chi Yeh        }
1621b5111f16c1401437d577dfd547af10aa691d44fChia-chi Yeh
1631b5111f16c1401437d577dfd547af10aa691d44fChia-chi Yeh        String local = null;
1641b5111f16c1401437d577dfd547af10aa691d44fChia-chi Yeh        try {
1651b5111f16c1401437d577dfd547af10aa691d44fChia-chi Yeh            for (InetAddress remote : InetAddress.getAllByName(server)) {
1661b5111f16c1401437d577dfd547af10aa691d44fChia-chi Yeh                DatagramSocket socket = new DatagramSocket();
1671b5111f16c1401437d577dfd547af10aa691d44fChia-chi Yeh                socket.connect(remote, port);
1681b5111f16c1401437d577dfd547af10aa691d44fChia-chi Yeh                if (socket.isConnected()) {
1691b5111f16c1401437d577dfd547af10aa691d44fChia-chi Yeh                    local = socket.getLocalAddress().getHostAddress();
1701b5111f16c1401437d577dfd547af10aa691d44fChia-chi Yeh                    port = socket.getLocalPort();
1711b5111f16c1401437d577dfd547af10aa691d44fChia-chi Yeh                    socket.close();
1721b5111f16c1401437d577dfd547af10aa691d44fChia-chi Yeh                    break;
1731b5111f16c1401437d577dfd547af10aa691d44fChia-chi Yeh                }
1741b5111f16c1401437d577dfd547af10aa691d44fChia-chi Yeh                socket.close();
1751b5111f16c1401437d577dfd547af10aa691d44fChia-chi Yeh            }
1761b5111f16c1401437d577dfd547af10aa691d44fChia-chi Yeh        } catch (Exception e) {
1771b5111f16c1401437d577dfd547af10aa691d44fChia-chi Yeh            // ignore.
1781b5111f16c1401437d577dfd547af10aa691d44fChia-chi Yeh        }
1791b5111f16c1401437d577dfd547af10aa691d44fChia-chi Yeh        if (local == null) {
1801b5111f16c1401437d577dfd547af10aa691d44fChia-chi Yeh            // We are unable to reach the server. Just bail out.
1811b5111f16c1401437d577dfd547af10aa691d44fChia-chi Yeh            return;
1821b5111f16c1401437d577dfd547af10aa691d44fChia-chi Yeh        }
1831b5111f16c1401437d577dfd547af10aa691d44fChia-chi Yeh
1841b5111f16c1401437d577dfd547af10aa691d44fChia-chi Yeh        close();
1851b5111f16c1401437d577dfd547af10aa691d44fChia-chi Yeh        mLocalIp = local;
1861b5111f16c1401437d577dfd547af10aa691d44fChia-chi Yeh
1879c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        properties.setProperty("javax.sip.STACK_NAME", getStackName());
1887f5530b2b6b0183208a3280600de5e74fc1c672cChung-yih Wang        properties.setProperty(
1897f5530b2b6b0183208a3280600de5e74fc1c672cChung-yih Wang                "gov.nist.javax.sip.THREAD_POOL_SIZE", THREAD_POOL_SIZE);
1901b5111f16c1401437d577dfd547af10aa691d44fChia-chi Yeh        mSipStack = SipFactory.getInstance().createSipStack(properties);
1919c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        try {
1921b5111f16c1401437d577dfd547af10aa691d44fChia-chi Yeh            SipProvider provider = mSipStack.createSipProvider(
1931b5111f16c1401437d577dfd547af10aa691d44fChia-chi Yeh                    mSipStack.createListeningPoint(local, port, protocol));
1949c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            provider.addSipListener(this);
1951b5111f16c1401437d577dfd547af10aa691d44fChia-chi Yeh            mSipHelper = new SipHelper(mSipStack, provider);
1961b5111f16c1401437d577dfd547af10aa691d44fChia-chi Yeh        } catch (SipException e) {
1971b5111f16c1401437d577dfd547af10aa691d44fChia-chi Yeh            throw e;
1981b5111f16c1401437d577dfd547af10aa691d44fChia-chi Yeh        } catch (Exception e) {
1991b5111f16c1401437d577dfd547af10aa691d44fChia-chi Yeh            throw new SipException("failed to initialize SIP stack", e);
2009c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
2013efff6c5840a99faadc3ee6197940c3290f65a62Hung-ying Tyan
2029329db04f13480ccdff013dcc00cdb96f12a921cWink Saville        if (DBG) log("reset: start stack for " + mLocalProfile.getUriString());
2031b5111f16c1401437d577dfd547af10aa691d44fChia-chi Yeh        mSipStack.start();
2049c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
2059c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
2069c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    synchronized void onConnectivityChanged() {
20727820a94e73622f647daf8678180abc541d3a092Hung-ying Tyan        SipSessionImpl[] ss = mSessionMap.values().toArray(
20827820a94e73622f647daf8678180abc541d3a092Hung-ying Tyan                    new SipSessionImpl[mSessionMap.size()]);
20927820a94e73622f647daf8678180abc541d3a092Hung-ying Tyan        // Iterate on the copied array instead of directly on mSessionMap to
21027820a94e73622f647daf8678180abc541d3a092Hung-ying Tyan        // avoid ConcurrentModificationException being thrown when
21127820a94e73622f647daf8678180abc541d3a092Hung-ying Tyan        // SipSessionImpl removes itself from mSessionMap in onError() in the
21227820a94e73622f647daf8678180abc541d3a092Hung-ying Tyan        // following loop.
21327820a94e73622f647daf8678180abc541d3a092Hung-ying Tyan        for (SipSessionImpl s : ss) {
2149c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            s.onError(SipErrorCode.DATA_CONNECTION_LOST,
2159c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    "data connection lost");
2169c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
2179c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
2189c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
2193efff6c5840a99faadc3ee6197940c3290f65a62Hung-ying Tyan    synchronized void resetExternalAddress() {
2209329db04f13480ccdff013dcc00cdb96f12a921cWink Saville        if (DBG) {
2219329db04f13480ccdff013dcc00cdb96f12a921cWink Saville            log("resetExternalAddress: " + mSipStack);
22285caf063c027ba0d1defeaff8850e12b6428c4f7Chia-chi Yeh        }
2233efff6c5840a99faadc3ee6197940c3290f65a62Hung-ying Tyan        mExternalIp = null;
2243efff6c5840a99faadc3ee6197940c3290f65a62Hung-ying Tyan        mExternalPort = 0;
2253efff6c5840a99faadc3ee6197940c3290f65a62Hung-ying Tyan    }
2263efff6c5840a99faadc3ee6197940c3290f65a62Hung-ying Tyan
2279c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    public SipProfile getLocalProfile() {
2289c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        return mLocalProfile;
2299c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
2309c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
2319c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    public String getLocalProfileUri() {
2329c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        return mLocalProfile.getUriString();
2339c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
2349c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
2359c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private String getStackName() {
2369c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        return "stack" + System.currentTimeMillis();
2379c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
2389c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
2399c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    public synchronized void close() {
2409329db04f13480ccdff013dcc00cdb96f12a921cWink Saville        if (DBG) log("close: " + mLocalProfile.getUriString());
2410d603e36cb0d81f55bb90fc37784da3221f13920Hung-ying Tyan        onConnectivityChanged();
2429c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        mSessionMap.clear();
2439c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        closeToNotReceiveCalls();
2449c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        if (mSipStack != null) {
2459c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mSipStack.stop();
2469c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mSipStack = null;
2479c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mSipHelper = null;
2489c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
2491b5111f16c1401437d577dfd547af10aa691d44fChia-chi Yeh        resetExternalAddress();
2509c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
2519c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
2529c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    public synchronized boolean isClosed() {
2539c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        return (mSipStack == null);
2549c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
2559c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
2569c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    // For internal use, require listener not to block in callbacks.
2579c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    public synchronized void openToReceiveCalls(ISipSessionListener listener) {
2589c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        if (mCallReceiverSession == null) {
2599c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mCallReceiverSession = new SipSessionCallReceiverImpl(listener);
2609c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        } else {
2619c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mCallReceiverSession.setListener(listener);
2629c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
2639c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
2649c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
2659c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    public synchronized void closeToNotReceiveCalls() {
2669c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        mCallReceiverSession = null;
2679c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
2689c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
2699c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    public ISipSession createSession(ISipSessionListener listener) {
2709c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        return (isClosed() ? null : new SipSessionImpl(listener));
2719c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
2729c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
273ed3c0fbbd5457f43ff72cec31b8ab0bb3f7a0047Hung-ying Tyan    synchronized boolean containsSession(String callId) {
274ed3c0fbbd5457f43ff72cec31b8ab0bb3f7a0047Hung-ying Tyan        return mSessionMap.containsKey(callId);
275ed3c0fbbd5457f43ff72cec31b8ab0bb3f7a0047Hung-ying Tyan    }
276ed3c0fbbd5457f43ff72cec31b8ab0bb3f7a0047Hung-ying Tyan
2779c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private synchronized SipSessionImpl getSipSession(EventObject event) {
2789c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        String key = SipHelper.getCallId(event);
2799c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        SipSessionImpl session = mSessionMap.get(key);
2809c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        if ((session != null) && isLoggable(session)) {
2819329db04f13480ccdff013dcc00cdb96f12a921cWink Saville            if (DBG) log("getSipSession: event=" + key);
2829329db04f13480ccdff013dcc00cdb96f12a921cWink Saville            if (DBG) log("getSipSession: active sessions:");
2839c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            for (String k : mSessionMap.keySet()) {
2849329db04f13480ccdff013dcc00cdb96f12a921cWink Saville                if (DBG) log("getSipSession: ..." + k + ": " + mSessionMap.get(k));
2859c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
2869c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
2879c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        return ((session != null) ? session : mCallReceiverSession);
2889c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
2899c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
2909c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private synchronized void addSipSession(SipSessionImpl newSession) {
2919c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        removeSipSession(newSession);
2929c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        String key = newSession.getCallId();
2939c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        mSessionMap.put(key, newSession);
2949c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        if (isLoggable(newSession)) {
2959329db04f13480ccdff013dcc00cdb96f12a921cWink Saville            if (DBG) log("addSipSession: key='" + key + "'");
2969c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            for (String k : mSessionMap.keySet()) {
2979329db04f13480ccdff013dcc00cdb96f12a921cWink Saville                if (DBG) log("addSipSession:  " + k + ": " + mSessionMap.get(k));
2989c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
2999c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
3009c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
3019c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
3029c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private synchronized void removeSipSession(SipSessionImpl session) {
3039c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        if (session == mCallReceiverSession) return;
3049c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        String key = session.getCallId();
3059c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        SipSessionImpl s = mSessionMap.remove(key);
3069c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        // sanity check
3079c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        if ((s != null) && (s != session)) {
3089329db04f13480ccdff013dcc00cdb96f12a921cWink Saville            if (DBG) log("removeSession: " + session + " is not associated with key '"
3099c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    + key + "'");
3109c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mSessionMap.put(key, s);
3119c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            for (Map.Entry<String, SipSessionImpl> entry
3129c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    : mSessionMap.entrySet()) {
3139c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                if (entry.getValue() == s) {
3149c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    key = entry.getKey();
3159c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    mSessionMap.remove(key);
3169c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                }
3179c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
3189c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
3199c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
3209c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        if ((s != null) && isLoggable(s)) {
3219329db04f13480ccdff013dcc00cdb96f12a921cWink Saville            if (DBG) log("removeSession: " + session + " @key '" + key + "'");
3229c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            for (String k : mSessionMap.keySet()) {
3239329db04f13480ccdff013dcc00cdb96f12a921cWink Saville                if (DBG) log("removeSession:  " + k + ": " + mSessionMap.get(k));
3249c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
3259c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
3269c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
3279c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
3289329db04f13480ccdff013dcc00cdb96f12a921cWink Saville    @Override
329acedadc1967457ac2f8981c67f884fd8c0ee853cHung-ying Tyan    public void processRequest(final RequestEvent event) {
330acedadc1967457ac2f8981c67f884fd8c0ee853cHung-ying Tyan        if (isRequestEvent(Request.INVITE, event)) {
3319329db04f13480ccdff013dcc00cdb96f12a921cWink Saville            if (DBG) log("processRequest: mWakeLock.acquire got INVITE, thread:"
332acedadc1967457ac2f8981c67f884fd8c0ee853cHung-ying Tyan                    + Thread.currentThread());
333acedadc1967457ac2f8981c67f884fd8c0ee853cHung-ying Tyan            // Acquire a wake lock and keep it for WAKE_LOCK_HOLDING_TIME;
334acedadc1967457ac2f8981c67f884fd8c0ee853cHung-ying Tyan            // should be large enough to bring up the app.
335acedadc1967457ac2f8981c67f884fd8c0ee853cHung-ying Tyan            mWakeLock.acquire(WAKE_LOCK_HOLDING_TIME);
336acedadc1967457ac2f8981c67f884fd8c0ee853cHung-ying Tyan        }
3379c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        process(event);
3389c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
3399c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
3409329db04f13480ccdff013dcc00cdb96f12a921cWink Saville    @Override
3419c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    public void processResponse(ResponseEvent event) {
3429c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        process(event);
3439c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
3449c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
3459329db04f13480ccdff013dcc00cdb96f12a921cWink Saville    @Override
3469c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    public void processIOException(IOExceptionEvent event) {
3479c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        process(event);
3489c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
3499c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
3509329db04f13480ccdff013dcc00cdb96f12a921cWink Saville    @Override
3519c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    public void processTimeout(TimeoutEvent event) {
3529c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        process(event);
3539c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
3549c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
3559329db04f13480ccdff013dcc00cdb96f12a921cWink Saville    @Override
3569c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    public void processTransactionTerminated(TransactionTerminatedEvent event) {
3579c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        process(event);
3589c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
3599c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
3609329db04f13480ccdff013dcc00cdb96f12a921cWink Saville    @Override
3619c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    public void processDialogTerminated(DialogTerminatedEvent event) {
3629c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        process(event);
3639c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
3649c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
3659c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private synchronized void process(EventObject event) {
3669c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        SipSessionImpl session = getSipSession(event);
3679c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        try {
3689c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            boolean isLoggable = isLoggable(session, event);
3699c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            boolean processed = (session != null) && session.process(event);
3709c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (isLoggable && processed) {
3719329db04f13480ccdff013dcc00cdb96f12a921cWink Saville                log("process: event new state after: "
3729c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        + SipSession.State.toString(session.mState));
3739c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
3749c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        } catch (Throwable e) {
3759329db04f13480ccdff013dcc00cdb96f12a921cWink Saville            loge("process: error event=" + event, getRootCause(e));
3769c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            session.onError(e);
3779c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
3789c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
3799c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
3809c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private String extractContent(Message message) {
3819c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        // Currently we do not support secure MIME bodies.
3829c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        byte[] bytes = message.getRawContent();
3839c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        if (bytes != null) {
3849c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            try {
3859c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                if (message instanceof SIPMessage) {
3869c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    return ((SIPMessage) message).getMessageContent();
3879c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                } else {
3889c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    return new String(bytes, "UTF-8");
3899c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                }
3909c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            } catch (UnsupportedEncodingException e) {
3919c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
3929c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
3939c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        return null;
3949c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
3959c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
3963efff6c5840a99faadc3ee6197940c3290f65a62Hung-ying Tyan    private void extractExternalAddress(ResponseEvent evt) {
3973efff6c5840a99faadc3ee6197940c3290f65a62Hung-ying Tyan        Response response = evt.getResponse();
3983efff6c5840a99faadc3ee6197940c3290f65a62Hung-ying Tyan        ViaHeader viaHeader = (ViaHeader)(response.getHeader(
3993efff6c5840a99faadc3ee6197940c3290f65a62Hung-ying Tyan                SIPHeaderNames.VIA));
4003efff6c5840a99faadc3ee6197940c3290f65a62Hung-ying Tyan        if (viaHeader == null) return;
4013efff6c5840a99faadc3ee6197940c3290f65a62Hung-ying Tyan        int rport = viaHeader.getRPort();
4023efff6c5840a99faadc3ee6197940c3290f65a62Hung-ying Tyan        String externalIp = viaHeader.getReceived();
4033efff6c5840a99faadc3ee6197940c3290f65a62Hung-ying Tyan        if ((rport > 0) && (externalIp != null)) {
4043efff6c5840a99faadc3ee6197940c3290f65a62Hung-ying Tyan            mExternalIp = externalIp;
4053efff6c5840a99faadc3ee6197940c3290f65a62Hung-ying Tyan            mExternalPort = rport;
4069329db04f13480ccdff013dcc00cdb96f12a921cWink Saville            if (DBG) {
4079329db04f13480ccdff013dcc00cdb96f12a921cWink Saville                log("extractExternalAddress: external addr " + externalIp + ":" + rport
40885caf063c027ba0d1defeaff8850e12b6428c4f7Chia-chi Yeh                        + " on " + mSipStack);
40985caf063c027ba0d1defeaff8850e12b6428c4f7Chia-chi Yeh            }
41085caf063c027ba0d1defeaff8850e12b6428c4f7Chia-chi Yeh        }
41185caf063c027ba0d1defeaff8850e12b6428c4f7Chia-chi Yeh    }
41285caf063c027ba0d1defeaff8850e12b6428c4f7Chia-chi Yeh
41385caf063c027ba0d1defeaff8850e12b6428c4f7Chia-chi Yeh    private Throwable getRootCause(Throwable exception) {
41485caf063c027ba0d1defeaff8850e12b6428c4f7Chia-chi Yeh        Throwable cause = exception.getCause();
41585caf063c027ba0d1defeaff8850e12b6428c4f7Chia-chi Yeh        while (cause != null) {
41685caf063c027ba0d1defeaff8850e12b6428c4f7Chia-chi Yeh            exception = cause;
41785caf063c027ba0d1defeaff8850e12b6428c4f7Chia-chi Yeh            cause = exception.getCause();
4183efff6c5840a99faadc3ee6197940c3290f65a62Hung-ying Tyan        }
41985caf063c027ba0d1defeaff8850e12b6428c4f7Chia-chi Yeh        return exception;
4203efff6c5840a99faadc3ee6197940c3290f65a62Hung-ying Tyan    }
4213efff6c5840a99faadc3ee6197940c3290f65a62Hung-ying Tyan
422f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync    private SipSessionImpl createNewSession(RequestEvent event,
423f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync            ISipSessionListener listener, ServerTransaction transaction,
424f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync            int newState) throws SipException {
425f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync        SipSessionImpl newSession = new SipSessionImpl(listener);
426f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync        newSession.mServerTransaction = transaction;
427f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync        newSession.mState = newState;
428f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync        newSession.mDialog = newSession.mServerTransaction.getDialog();
429f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync        newSession.mInviteReceived = event;
430f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync        newSession.mPeerProfile = createPeerProfile((HeaderAddress)
431f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync                event.getRequest().getHeader(FromHeader.NAME));
432f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync        newSession.mPeerSessionDescription =
433f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync                extractContent(event.getRequest());
434f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync        return newSession;
435f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync    }
436f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync
4379c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private class SipSessionCallReceiverImpl extends SipSessionImpl {
4389329db04f13480ccdff013dcc00cdb96f12a921cWink Saville        private static final String SSCRI_TAG = "SipSessionCallReceiverImpl";
4399329db04f13480ccdff013dcc00cdb96f12a921cWink Saville        private static final boolean SSCRI_DBG = true;
4409329db04f13480ccdff013dcc00cdb96f12a921cWink Saville
4419c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public SipSessionCallReceiverImpl(ISipSessionListener listener) {
4429c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            super(listener);
4439c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
4449c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
44522ecc3df834674605daf86f7edf20169b6ca800brepo sync        private int processInviteWithReplaces(RequestEvent event,
44622ecc3df834674605daf86f7edf20169b6ca800brepo sync                ReplacesHeader replaces) {
44722ecc3df834674605daf86f7edf20169b6ca800brepo sync            String callId = replaces.getCallId();
44822ecc3df834674605daf86f7edf20169b6ca800brepo sync            SipSessionImpl session = mSessionMap.get(callId);
44922ecc3df834674605daf86f7edf20169b6ca800brepo sync            if (session == null) {
45022ecc3df834674605daf86f7edf20169b6ca800brepo sync                return Response.CALL_OR_TRANSACTION_DOES_NOT_EXIST;
45122ecc3df834674605daf86f7edf20169b6ca800brepo sync            }
45222ecc3df834674605daf86f7edf20169b6ca800brepo sync
45322ecc3df834674605daf86f7edf20169b6ca800brepo sync            Dialog dialog = session.mDialog;
45422ecc3df834674605daf86f7edf20169b6ca800brepo sync            if (dialog == null) return Response.DECLINE;
45522ecc3df834674605daf86f7edf20169b6ca800brepo sync
45622ecc3df834674605daf86f7edf20169b6ca800brepo sync            if (!dialog.getLocalTag().equals(replaces.getToTag()) ||
45722ecc3df834674605daf86f7edf20169b6ca800brepo sync                    !dialog.getRemoteTag().equals(replaces.getFromTag())) {
45822ecc3df834674605daf86f7edf20169b6ca800brepo sync                // No match is found, returns 481.
45922ecc3df834674605daf86f7edf20169b6ca800brepo sync                return Response.CALL_OR_TRANSACTION_DOES_NOT_EXIST;
46022ecc3df834674605daf86f7edf20169b6ca800brepo sync            }
46122ecc3df834674605daf86f7edf20169b6ca800brepo sync
46222ecc3df834674605daf86f7edf20169b6ca800brepo sync            ReferredByHeader referredBy = (ReferredByHeader) event.getRequest()
46322ecc3df834674605daf86f7edf20169b6ca800brepo sync                    .getHeader(ReferredByHeader.NAME);
46422ecc3df834674605daf86f7edf20169b6ca800brepo sync            if ((referredBy == null) ||
46522ecc3df834674605daf86f7edf20169b6ca800brepo sync                    !dialog.getRemoteParty().equals(referredBy.getAddress())) {
46622ecc3df834674605daf86f7edf20169b6ca800brepo sync                return Response.CALL_OR_TRANSACTION_DOES_NOT_EXIST;
46722ecc3df834674605daf86f7edf20169b6ca800brepo sync            }
46822ecc3df834674605daf86f7edf20169b6ca800brepo sync            return Response.OK;
46922ecc3df834674605daf86f7edf20169b6ca800brepo sync        }
47022ecc3df834674605daf86f7edf20169b6ca800brepo sync
47122ecc3df834674605daf86f7edf20169b6ca800brepo sync        private void processNewInviteRequest(RequestEvent event)
47222ecc3df834674605daf86f7edf20169b6ca800brepo sync                throws SipException {
47322ecc3df834674605daf86f7edf20169b6ca800brepo sync            ReplacesHeader replaces = (ReplacesHeader) event.getRequest()
47422ecc3df834674605daf86f7edf20169b6ca800brepo sync                    .getHeader(ReplacesHeader.NAME);
47522ecc3df834674605daf86f7edf20169b6ca800brepo sync            SipSessionImpl newSession = null;
47622ecc3df834674605daf86f7edf20169b6ca800brepo sync            if (replaces != null) {
47722ecc3df834674605daf86f7edf20169b6ca800brepo sync                int response = processInviteWithReplaces(event, replaces);
4789329db04f13480ccdff013dcc00cdb96f12a921cWink Saville                if (SSCRI_DBG) {
4799329db04f13480ccdff013dcc00cdb96f12a921cWink Saville                    log("processNewInviteRequest: " + replaces
48022ecc3df834674605daf86f7edf20169b6ca800brepo sync                            + " response=" + response);
48122ecc3df834674605daf86f7edf20169b6ca800brepo sync                }
48222ecc3df834674605daf86f7edf20169b6ca800brepo sync                if (response == Response.OK) {
48322ecc3df834674605daf86f7edf20169b6ca800brepo sync                    SipSessionImpl replacedSession =
48422ecc3df834674605daf86f7edf20169b6ca800brepo sync                            mSessionMap.get(replaces.getCallId());
48522ecc3df834674605daf86f7edf20169b6ca800brepo sync                    // got INVITE w/ replaces request.
48622ecc3df834674605daf86f7edf20169b6ca800brepo sync                    newSession = createNewSession(event,
48722ecc3df834674605daf86f7edf20169b6ca800brepo sync                            replacedSession.mProxy.getListener(),
488f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync                            mSipHelper.getServerTransaction(event),
489f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync                            SipSession.State.INCOMING_CALL);
49022ecc3df834674605daf86f7edf20169b6ca800brepo sync                    newSession.mProxy.onCallTransferring(newSession,
49122ecc3df834674605daf86f7edf20169b6ca800brepo sync                            newSession.mPeerSessionDescription);
49222ecc3df834674605daf86f7edf20169b6ca800brepo sync                } else {
49322ecc3df834674605daf86f7edf20169b6ca800brepo sync                    mSipHelper.sendResponse(event, response);
49422ecc3df834674605daf86f7edf20169b6ca800brepo sync                }
49522ecc3df834674605daf86f7edf20169b6ca800brepo sync            } else {
49622ecc3df834674605daf86f7edf20169b6ca800brepo sync                // New Incoming call.
49722ecc3df834674605daf86f7edf20169b6ca800brepo sync                newSession = createNewSession(event, mProxy,
498f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync                        mSipHelper.sendRinging(event, generateTag()),
499f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync                        SipSession.State.INCOMING_CALL);
50022ecc3df834674605daf86f7edf20169b6ca800brepo sync                mProxy.onRinging(newSession, newSession.mPeerProfile,
50122ecc3df834674605daf86f7edf20169b6ca800brepo sync                        newSession.mPeerSessionDescription);
50222ecc3df834674605daf86f7edf20169b6ca800brepo sync            }
50322ecc3df834674605daf86f7edf20169b6ca800brepo sync            if (newSession != null) addSipSession(newSession);
50422ecc3df834674605daf86f7edf20169b6ca800brepo sync        }
50522ecc3df834674605daf86f7edf20169b6ca800brepo sync
5069329db04f13480ccdff013dcc00cdb96f12a921cWink Saville        @Override
5079c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public boolean process(EventObject evt) throws SipException {
5089329db04f13480ccdff013dcc00cdb96f12a921cWink Saville            if (isLoggable(this, evt)) log("process: " + this + ": "
5099c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    + SipSession.State.toString(mState) + ": processing "
5109329db04f13480ccdff013dcc00cdb96f12a921cWink Saville                    + logEvt(evt));
5119c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (isRequestEvent(Request.INVITE, evt)) {
51222ecc3df834674605daf86f7edf20169b6ca800brepo sync                processNewInviteRequest((RequestEvent) evt);
5139c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                return true;
5149c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            } else if (isRequestEvent(Request.OPTIONS, evt)) {
5159c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mSipHelper.sendResponse((RequestEvent) evt, Response.OK);
5169c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                return true;
5179c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            } else {
5189c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                return false;
5199c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
5209c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
5219329db04f13480ccdff013dcc00cdb96f12a921cWink Saville
5229329db04f13480ccdff013dcc00cdb96f12a921cWink Saville        private void log(String s) {
5239329db04f13480ccdff013dcc00cdb96f12a921cWink Saville            Rlog.d(SSCRI_TAG, s);
5249329db04f13480ccdff013dcc00cdb96f12a921cWink Saville        }
5259c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
5269c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
5278a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan    static interface KeepAliveProcessCallback {
5288a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        /** Invoked when the response of keeping alive comes back. */
5298a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        void onResponse(boolean portChanged);
5308a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        void onError(int errorCode, String description);
5318a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan    }
5328a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan
5339c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    class SipSessionImpl extends ISipSession.Stub {
5349329db04f13480ccdff013dcc00cdb96f12a921cWink Saville        private static final String SSI_TAG = "SipSessionImpl";
5359329db04f13480ccdff013dcc00cdb96f12a921cWink Saville        private static final boolean SSI_DBG = true;
5369329db04f13480ccdff013dcc00cdb96f12a921cWink Saville
5379c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        SipProfile mPeerProfile;
5389c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        SipSessionListenerProxy mProxy = new SipSessionListenerProxy();
5399c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        int mState = SipSession.State.READY_TO_CALL;
5409c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        RequestEvent mInviteReceived;
5419c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        Dialog mDialog;
5429c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        ServerTransaction mServerTransaction;
5439c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        ClientTransaction mClientTransaction;
5449c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        String mPeerSessionDescription;
5459c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        boolean mInCall;
5468a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        SessionTimer mSessionTimer;
547fcc474636f42243cfce960158373b9c49636a556Hung-ying Tyan        int mAuthenticationRetryCount;
548fcc474636f42243cfce960158373b9c49636a556Hung-ying Tyan
5499329db04f13480ccdff013dcc00cdb96f12a921cWink Saville        private SipKeepAlive mSipKeepAlive;
5509c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
5519329db04f13480ccdff013dcc00cdb96f12a921cWink Saville        private SipSessionImpl mSipSessionImpl;
5520f8ceff66069c98480481241ba9d70dc34448188Hung-ying Tyan
553f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync        // the following three members are used for handling refer request.
554f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync        SipSessionImpl mReferSession;
555f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync        ReferredByHeader mReferredBy;
556f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync        String mReplaces;
557f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync
5589c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        // lightweight timer
5599c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        class SessionTimer {
5609c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            private boolean mRunning = true;
5619c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
5629c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            void start(final int timeout) {
5639c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                new Thread(new Runnable() {
5649329db04f13480ccdff013dcc00cdb96f12a921cWink Saville                    @Override
5659c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    public void run() {
5669c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        sleep(timeout);
5679c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        if (mRunning) timeout();
5689c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    }
5699c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                }, "SipSessionTimerThread").start();
5709c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
5719c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
5729c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            synchronized void cancel() {
5739c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mRunning = false;
5749c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                this.notify();
5759c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
5769c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
5779c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            private void timeout() {
5789c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                synchronized (SipSessionGroup.this) {
5799c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    onError(SipErrorCode.TIME_OUT, "Session timed out!");
5809c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                }
5819c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
5829c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
5839c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            private synchronized void sleep(int timeout) {
5849c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                try {
5859c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    this.wait(timeout * 1000);
5869c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                } catch (InterruptedException e) {
5879329db04f13480ccdff013dcc00cdb96f12a921cWink Saville                    loge("session timer interrupted!", e);
5889c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                }
5899c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
5909c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
5919c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
5929c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public SipSessionImpl(ISipSessionListener listener) {
5939c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            setListener(listener);
5949c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
5959c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
5969c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        SipSessionImpl duplicate() {
5979c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return new SipSessionImpl(mProxy.getListener());
5989c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
5999c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
6009c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private void reset() {
6019c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mInCall = false;
6029c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            removeSipSession(this);
6039c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mPeerProfile = null;
6049c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mState = SipSession.State.READY_TO_CALL;
6059c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mInviteReceived = null;
606e8782e2fdfd256296a0d1fd2a1c3f98d58103eaaHung-ying Tyan            mPeerSessionDescription = null;
607fcc474636f42243cfce960158373b9c49636a556Hung-ying Tyan            mAuthenticationRetryCount = 0;
608f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync            mReferSession = null;
609f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync            mReferredBy = null;
610f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync            mReplaces = null;
611e8782e2fdfd256296a0d1fd2a1c3f98d58103eaaHung-ying Tyan
612e8782e2fdfd256296a0d1fd2a1c3f98d58103eaaHung-ying Tyan            if (mDialog != null) mDialog.delete();
6139c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mDialog = null;
614e8782e2fdfd256296a0d1fd2a1c3f98d58103eaaHung-ying Tyan
615e8782e2fdfd256296a0d1fd2a1c3f98d58103eaaHung-ying Tyan            try {
616e8782e2fdfd256296a0d1fd2a1c3f98d58103eaaHung-ying Tyan                if (mServerTransaction != null) mServerTransaction.terminate();
617e8782e2fdfd256296a0d1fd2a1c3f98d58103eaaHung-ying Tyan            } catch (ObjectInUseException e) {
618e8782e2fdfd256296a0d1fd2a1c3f98d58103eaaHung-ying Tyan                // ignored
619e8782e2fdfd256296a0d1fd2a1c3f98d58103eaaHung-ying Tyan            }
6209c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mServerTransaction = null;
621e8782e2fdfd256296a0d1fd2a1c3f98d58103eaaHung-ying Tyan
622e8782e2fdfd256296a0d1fd2a1c3f98d58103eaaHung-ying Tyan            try {
623e8782e2fdfd256296a0d1fd2a1c3f98d58103eaaHung-ying Tyan                if (mClientTransaction != null) mClientTransaction.terminate();
624e8782e2fdfd256296a0d1fd2a1c3f98d58103eaaHung-ying Tyan            } catch (ObjectInUseException e) {
625e8782e2fdfd256296a0d1fd2a1c3f98d58103eaaHung-ying Tyan                // ignored
626e8782e2fdfd256296a0d1fd2a1c3f98d58103eaaHung-ying Tyan            }
6279c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mClientTransaction = null;
6289c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
6299c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            cancelSessionTimer();
6300f8ceff66069c98480481241ba9d70dc34448188Hung-ying Tyan
6319329db04f13480ccdff013dcc00cdb96f12a921cWink Saville            if (mSipSessionImpl != null) {
6329329db04f13480ccdff013dcc00cdb96f12a921cWink Saville                mSipSessionImpl.stopKeepAliveProcess();
6339329db04f13480ccdff013dcc00cdb96f12a921cWink Saville                mSipSessionImpl = null;
6340f8ceff66069c98480481241ba9d70dc34448188Hung-ying Tyan            }
6359c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
6369c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
6379329db04f13480ccdff013dcc00cdb96f12a921cWink Saville        @Override
6389c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public boolean isInCall() {
6399c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return mInCall;
6409c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
6419c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
6429329db04f13480ccdff013dcc00cdb96f12a921cWink Saville        @Override
6439c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public String getLocalIp() {
6449c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return mLocalIp;
6459c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
6469c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
6479329db04f13480ccdff013dcc00cdb96f12a921cWink Saville        @Override
6489c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public SipProfile getLocalProfile() {
6499c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return mLocalProfile;
6509c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
6519c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
6529329db04f13480ccdff013dcc00cdb96f12a921cWink Saville        @Override
6539c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public SipProfile getPeerProfile() {
6549c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return mPeerProfile;
6559c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
6569c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
6579329db04f13480ccdff013dcc00cdb96f12a921cWink Saville        @Override
6589c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public String getCallId() {
6599c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return SipHelper.getCallId(getTransaction());
6609c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
6619c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
6629c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private Transaction getTransaction() {
6639c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (mClientTransaction != null) return mClientTransaction;
6649c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (mServerTransaction != null) return mServerTransaction;
6659c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return null;
6669c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
6679c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
6689329db04f13480ccdff013dcc00cdb96f12a921cWink Saville        @Override
6699c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public int getState() {
6709c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return mState;
6719c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
6729c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
6739329db04f13480ccdff013dcc00cdb96f12a921cWink Saville        @Override
6749c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public void setListener(ISipSessionListener listener) {
6759c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mProxy.setListener((listener instanceof SipSessionListenerProxy)
6769c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    ? ((SipSessionListenerProxy) listener).getListener()
6779c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    : listener);
6789c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
6799c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
6809c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        // process the command in a new thread
6819c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private void doCommandAsync(final EventObject command) {
6829c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            new Thread(new Runnable() {
6839329db04f13480ccdff013dcc00cdb96f12a921cWink Saville                    @Override
6849c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    public void run() {
6859c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        try {
6869c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            processCommand(command);
687e26eb3274a65c41a6a30bdace1818c5629cca1c8Hung-ying Tyan                        } catch (Throwable e) {
6889329db04f13480ccdff013dcc00cdb96f12a921cWink Saville                            loge("command error: " + command + ": "
6898a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                                    + mLocalProfile.getUriString(),
6908a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                                    getRootCause(e));
6919c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            onError(e);
6929c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        }
6939c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    }
6949c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }, "SipSessionAsyncCmdThread").start();
6959c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
6969c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
6979329db04f13480ccdff013dcc00cdb96f12a921cWink Saville        @Override
6989c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public void makeCall(SipProfile peerProfile, String sessionDescription,
6999c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                int timeout) {
7009c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            doCommandAsync(new MakeCallCommand(peerProfile, sessionDescription,
7019c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    timeout));
7029c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
7039c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
7049329db04f13480ccdff013dcc00cdb96f12a921cWink Saville        @Override
7059c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public void answerCall(String sessionDescription, int timeout) {
706d47a47a131a8e7f80b934aec4cc7a10fd9fe1094Hung-ying Tyan            synchronized (SipSessionGroup.this) {
707d47a47a131a8e7f80b934aec4cc7a10fd9fe1094Hung-ying Tyan                if (mPeerProfile == null) return;
708aef08f1dd08902d0260dec34b2feede626d1bdc6repo sync                doCommandAsync(new MakeCallCommand(mPeerProfile,
709aef08f1dd08902d0260dec34b2feede626d1bdc6repo sync                        sessionDescription, timeout));
7109c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
7119c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
7129c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
7139329db04f13480ccdff013dcc00cdb96f12a921cWink Saville        @Override
7149c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public void endCall() {
7159c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            doCommandAsync(END_CALL);
7169c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
7179c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
7189329db04f13480ccdff013dcc00cdb96f12a921cWink Saville        @Override
7199c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public void changeCall(String sessionDescription, int timeout) {
720d47a47a131a8e7f80b934aec4cc7a10fd9fe1094Hung-ying Tyan            synchronized (SipSessionGroup.this) {
721d47a47a131a8e7f80b934aec4cc7a10fd9fe1094Hung-ying Tyan                if (mPeerProfile == null) return;
722d47a47a131a8e7f80b934aec4cc7a10fd9fe1094Hung-ying Tyan                doCommandAsync(new MakeCallCommand(mPeerProfile,
723d47a47a131a8e7f80b934aec4cc7a10fd9fe1094Hung-ying Tyan                        sessionDescription, timeout));
724d47a47a131a8e7f80b934aec4cc7a10fd9fe1094Hung-ying Tyan            }
7259c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
7269c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
7279329db04f13480ccdff013dcc00cdb96f12a921cWink Saville        @Override
7289c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public void register(int duration) {
7299c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            doCommandAsync(new RegisterCommand(duration));
7309c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
7319c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
7329329db04f13480ccdff013dcc00cdb96f12a921cWink Saville        @Override
7339c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public void unregister() {
7349c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            doCommandAsync(DEREGISTER);
7359c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
7369c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
7379c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private void processCommand(EventObject command) throws SipException {
7389329db04f13480ccdff013dcc00cdb96f12a921cWink Saville            if (isLoggable(command)) log("process cmd: " + command);
7399c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (!process(command)) {
7409c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                onError(SipErrorCode.IN_PROGRESS,
7419c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        "cannot initiate a new transaction to execute: "
7429c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        + command);
7439c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
7449c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
7459c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
7469c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        protected String generateTag() {
7479c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            // 32-bit randomness
7489c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return String.valueOf((long) (Math.random() * 0x100000000L));
7499c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
7509c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
7519329db04f13480ccdff013dcc00cdb96f12a921cWink Saville        @Override
7529c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public String toString() {
7539c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            try {
7549c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                String s = super.toString();
7559c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                return s.substring(s.indexOf("@")) + ":"
7569c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        + SipSession.State.toString(mState);
7579c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            } catch (Throwable e) {
7589c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                return super.toString();
7599c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
7609c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
7619c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
7629c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public boolean process(EventObject evt) throws SipException {
7639329db04f13480ccdff013dcc00cdb96f12a921cWink Saville            if (isLoggable(this, evt)) log(" ~~~~~   " + this + ": "
7649c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    + SipSession.State.toString(mState) + ": processing "
7659329db04f13480ccdff013dcc00cdb96f12a921cWink Saville                    + logEvt(evt));
7669c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            synchronized (SipSessionGroup.this) {
7679c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                if (isClosed()) return false;
7689c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
7699329db04f13480ccdff013dcc00cdb96f12a921cWink Saville                if (mSipKeepAlive != null) {
7708a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                    // event consumed by keepalive process
7719329db04f13480ccdff013dcc00cdb96f12a921cWink Saville                    if (mSipKeepAlive.process(evt)) return true;
7728a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                }
7738a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan
7749c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                Dialog dialog = null;
7759c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                if (evt instanceof RequestEvent) {
7769c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    dialog = ((RequestEvent) evt).getDialog();
7779c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                } else if (evt instanceof ResponseEvent) {
7789c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    dialog = ((ResponseEvent) evt).getDialog();
7793efff6c5840a99faadc3ee6197940c3290f65a62Hung-ying Tyan                    extractExternalAddress((ResponseEvent) evt);
7809c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                }
7819c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                if (dialog != null) mDialog = dialog;
7829c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
7839c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                boolean processed;
7849c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
7859c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                switch (mState) {
7869c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                case SipSession.State.REGISTERING:
7879c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                case SipSession.State.DEREGISTERING:
7889c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    processed = registeringToReady(evt);
7899c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    break;
7909c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                case SipSession.State.READY_TO_CALL:
7919c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    processed = readyForCall(evt);
7929c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    break;
7939c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                case SipSession.State.INCOMING_CALL:
7949c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    processed = incomingCall(evt);
7959c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    break;
7969c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                case SipSession.State.INCOMING_CALL_ANSWERING:
7979c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    processed = incomingCallToInCall(evt);
7989c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    break;
7999c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                case SipSession.State.OUTGOING_CALL:
8009c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                case SipSession.State.OUTGOING_CALL_RING_BACK:
8019c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    processed = outgoingCall(evt);
8029c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    break;
8039c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                case SipSession.State.OUTGOING_CALL_CANCELING:
8049c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    processed = outgoingCallToReady(evt);
8059c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    break;
8069c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                case SipSession.State.IN_CALL:
8079c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    processed = inCall(evt);
8089c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    break;
809ceb525d85512bde884b0f01a3aa93f2943dcf5f3Hung-ying Tyan                case SipSession.State.ENDING_CALL:
810ceb525d85512bde884b0f01a3aa93f2943dcf5f3Hung-ying Tyan                    processed = endingCall(evt);
811ceb525d85512bde884b0f01a3aa93f2943dcf5f3Hung-ying Tyan                    break;
8129c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                default:
8139c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    processed = false;
8149c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                }
8159c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                return (processed || processExceptions(evt));
8169c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
8179c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
8189c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
8199c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private boolean processExceptions(EventObject evt) throws SipException {
8209c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (isRequestEvent(Request.BYE, evt)) {
8219c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                // terminate the call whenever a BYE is received
8229c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mSipHelper.sendResponse((RequestEvent) evt, Response.OK);
8239c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                endCallNormally();
8249c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                return true;
8259c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            } else if (isRequestEvent(Request.CANCEL, evt)) {
8269c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mSipHelper.sendResponse((RequestEvent) evt,
8279c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        Response.CALL_OR_TRANSACTION_DOES_NOT_EXIST);
8289c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                return true;
8299c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            } else if (evt instanceof TransactionTerminatedEvent) {
8309c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                if (isCurrentTransaction((TransactionTerminatedEvent) evt)) {
8319c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    if (evt instanceof TimeoutEvent) {
8329c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        processTimeout((TimeoutEvent) evt);
8339c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    } else {
8349c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        processTransactionTerminated(
8359c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                                (TransactionTerminatedEvent) evt);
8369c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    }
8379c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    return true;
8389c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                }
8399c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            } else if (isRequestEvent(Request.OPTIONS, evt)) {
8409c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mSipHelper.sendResponse((RequestEvent) evt, Response.OK);
8419c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                return true;
8429c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            } else if (evt instanceof DialogTerminatedEvent) {
8439c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                processDialogTerminated((DialogTerminatedEvent) evt);
8449c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                return true;
8459c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
8469c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return false;
8479c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
8489c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
8499c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private void processDialogTerminated(DialogTerminatedEvent event) {
8509c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (mDialog == event.getDialog()) {
8519c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                onError(new SipException("dialog terminated"));
8529c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            } else {
8539329db04f13480ccdff013dcc00cdb96f12a921cWink Saville                if (SSI_DBG) log("not the current dialog; current=" + mDialog
8549c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        + ", terminated=" + event.getDialog());
8559c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
8569c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
8579c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
8589c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private boolean isCurrentTransaction(TransactionTerminatedEvent event) {
8599c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            Transaction current = event.isServerTransaction()
8609c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    ? mServerTransaction
8619c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    : mClientTransaction;
8629c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            Transaction target = event.isServerTransaction()
8639c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    ? event.getServerTransaction()
8649c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    : event.getClientTransaction();
8659c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
8669c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if ((current != target) && (mState != SipSession.State.PINGING)) {
8679329db04f13480ccdff013dcc00cdb96f12a921cWink Saville                if (SSI_DBG) log("not the current transaction; current="
8689c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        + toString(current) + ", target=" + toString(target));
8699c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                return false;
8709c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            } else if (current != null) {
8719329db04f13480ccdff013dcc00cdb96f12a921cWink Saville                if (SSI_DBG) log("transaction terminated: " + toString(current));
8729c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                return true;
8739c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            } else {
8749c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                // no transaction; shouldn't be here; ignored
8759c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                return true;
8769c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
8779c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
8789c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
8799c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private String toString(Transaction transaction) {
8809c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (transaction == null) return "null";
8819c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            Request request = transaction.getRequest();
8829c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            Dialog dialog = transaction.getDialog();
8839c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            CSeqHeader cseq = (CSeqHeader) request.getHeader(CSeqHeader.NAME);
8849c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return String.format("req=%s,%s,s=%s,ds=%s,", request.getMethod(),
8859c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    cseq.getSeqNumber(), transaction.getState(),
8869c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    ((dialog == null) ? "-" : dialog.getState()));
8879c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
8889c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
8899c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private void processTransactionTerminated(
8909c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                TransactionTerminatedEvent event) {
8919c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            switch (mState) {
8929c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                case SipSession.State.IN_CALL:
8939c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                case SipSession.State.READY_TO_CALL:
8949329db04f13480ccdff013dcc00cdb96f12a921cWink Saville                    if (SSI_DBG) log("Transaction terminated; do nothing");
8959c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    break;
8969c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                default:
8979329db04f13480ccdff013dcc00cdb96f12a921cWink Saville                    if (SSI_DBG) log("Transaction terminated early: " + this);
8989c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    onError(SipErrorCode.TRANSACTION_TERMINTED,
8999c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            "transaction terminated");
9009c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
9019c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
9029c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
9039c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private void processTimeout(TimeoutEvent event) {
9049329db04f13480ccdff013dcc00cdb96f12a921cWink Saville            if (SSI_DBG) log("processing Timeout...");
9059c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            switch (mState) {
9069c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                case SipSession.State.REGISTERING:
9079c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                case SipSession.State.DEREGISTERING:
9089c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    reset();
9099c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    mProxy.onRegistrationTimeout(this);
9109c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    break;
9119c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                case SipSession.State.INCOMING_CALL:
9129c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                case SipSession.State.INCOMING_CALL_ANSWERING:
9139c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                case SipSession.State.OUTGOING_CALL:
9149c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                case SipSession.State.OUTGOING_CALL_CANCELING:
9159c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    onError(SipErrorCode.TIME_OUT, event.toString());
9169c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    break;
9179c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
9189c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                default:
9199329db04f13480ccdff013dcc00cdb96f12a921cWink Saville                    if (SSI_DBG) log("   do nothing");
9209c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    break;
9219c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
9229c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
9239c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
9249c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private int getExpiryTime(Response response) {
9255e75bad33a53a468440871e4dbf83d05e6642de4Chia-chi Yeh            int time = -1;
9265e75bad33a53a468440871e4dbf83d05e6642de4Chia-chi Yeh            ContactHeader contact = (ContactHeader) response.getHeader(ContactHeader.NAME);
9275e75bad33a53a468440871e4dbf83d05e6642de4Chia-chi Yeh            if (contact != null) {
9285e75bad33a53a468440871e4dbf83d05e6642de4Chia-chi Yeh                time = contact.getExpires();
9299c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
9305e75bad33a53a468440871e4dbf83d05e6642de4Chia-chi Yeh            ExpiresHeader expires = (ExpiresHeader) response.getHeader(ExpiresHeader.NAME);
9315e75bad33a53a468440871e4dbf83d05e6642de4Chia-chi Yeh            if (expires != null && (time < 0 || time > expires.getExpires())) {
9325e75bad33a53a468440871e4dbf83d05e6642de4Chia-chi Yeh                time = expires.getExpires();
9335e75bad33a53a468440871e4dbf83d05e6642de4Chia-chi Yeh            }
934a3d53b09556994c514640e69d84f40c655347692Chia-chi Yeh            if (time <= 0) {
935a3d53b09556994c514640e69d84f40c655347692Chia-chi Yeh                time = EXPIRY_TIME;
936a3d53b09556994c514640e69d84f40c655347692Chia-chi Yeh            }
9375e75bad33a53a468440871e4dbf83d05e6642de4Chia-chi Yeh            expires = (ExpiresHeader) response.getHeader(MinExpiresHeader.NAME);
9385e75bad33a53a468440871e4dbf83d05e6642de4Chia-chi Yeh            if (expires != null && time < expires.getExpires()) {
9395e75bad33a53a468440871e4dbf83d05e6642de4Chia-chi Yeh                time = expires.getExpires();
9405e75bad33a53a468440871e4dbf83d05e6642de4Chia-chi Yeh            }
9419329db04f13480ccdff013dcc00cdb96f12a921cWink Saville            if (SSI_DBG) {
9429329db04f13480ccdff013dcc00cdb96f12a921cWink Saville                log("Expiry time = " + time);
94385caf063c027ba0d1defeaff8850e12b6428c4f7Chia-chi Yeh            }
944a3d53b09556994c514640e69d84f40c655347692Chia-chi Yeh            return time;
9459c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
9469c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
9479c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private boolean registeringToReady(EventObject evt)
9489c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                throws SipException {
9499c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (expectResponse(Request.REGISTER, evt)) {
9509c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                ResponseEvent event = (ResponseEvent) evt;
9519c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                Response response = event.getResponse();
9529c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
9539c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                int statusCode = response.getStatusCode();
9549c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                switch (statusCode) {
9559c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                case Response.OK:
9569c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    int state = mState;
9579c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    onRegistrationDone((state == SipSession.State.REGISTERING)
9589c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            ? getExpiryTime(((ResponseEvent) evt).getResponse())
9599c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            : -1);
9609c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    return true;
9619c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                case Response.UNAUTHORIZED:
9629c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                case Response.PROXY_AUTHENTICATION_REQUIRED:
963fcc474636f42243cfce960158373b9c49636a556Hung-ying Tyan                    handleAuthentication(event);
9649c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    return true;
9659c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                default:
9669c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    if (statusCode >= 500) {
9679c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        onRegistrationFailed(response);
9689c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        return true;
9699c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    }
9709c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                }
9719c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
9729c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return false;
9739c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
9749c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
9759c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private boolean handleAuthentication(ResponseEvent event)
9769c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                throws SipException {
9779c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            Response response = event.getResponse();
9789c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            String nonce = getNonceFromResponse(response);
979fcc474636f42243cfce960158373b9c49636a556Hung-ying Tyan            if (nonce == null) {
980fcc474636f42243cfce960158373b9c49636a556Hung-ying Tyan                onError(SipErrorCode.SERVER_ERROR,
981fcc474636f42243cfce960158373b9c49636a556Hung-ying Tyan                        "server does not provide challenge");
9829c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                return false;
983fcc474636f42243cfce960158373b9c49636a556Hung-ying Tyan            } else if (mAuthenticationRetryCount < 2) {
9849c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mClientTransaction = mSipHelper.handleChallenge(
9859c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        event, getAccountManager());
9869c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mDialog = mClientTransaction.getDialog();
987fcc474636f42243cfce960158373b9c49636a556Hung-ying Tyan                mAuthenticationRetryCount++;
988fcc474636f42243cfce960158373b9c49636a556Hung-ying Tyan                if (isLoggable(this, event)) {
9899329db04f13480ccdff013dcc00cdb96f12a921cWink Saville                    if (SSI_DBG) log("   authentication retry count="
990fcc474636f42243cfce960158373b9c49636a556Hung-ying Tyan                            + mAuthenticationRetryCount);
991fcc474636f42243cfce960158373b9c49636a556Hung-ying Tyan                }
9929c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                return true;
993fcc474636f42243cfce960158373b9c49636a556Hung-ying Tyan            } else {
994a0b7374a8afc7fb1db2eebb32ecba0f10db083cdHung-ying Tyan                if (crossDomainAuthenticationRequired(response)) {
995a0b7374a8afc7fb1db2eebb32ecba0f10db083cdHung-ying Tyan                    onError(SipErrorCode.CROSS_DOMAIN_AUTHENTICATION,
996a0b7374a8afc7fb1db2eebb32ecba0f10db083cdHung-ying Tyan                            getRealmFromResponse(response));
997a0b7374a8afc7fb1db2eebb32ecba0f10db083cdHung-ying Tyan                } else {
998a0b7374a8afc7fb1db2eebb32ecba0f10db083cdHung-ying Tyan                    onError(SipErrorCode.INVALID_CREDENTIALS,
999a0b7374a8afc7fb1db2eebb32ecba0f10db083cdHung-ying Tyan                            "incorrect username or password");
1000a0b7374a8afc7fb1db2eebb32ecba0f10db083cdHung-ying Tyan                }
1001fcc474636f42243cfce960158373b9c49636a556Hung-ying Tyan                return false;
10029c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
10039c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
10049c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
10059c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private boolean crossDomainAuthenticationRequired(Response response) {
10069c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            String realm = getRealmFromResponse(response);
10079c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (realm == null) realm = "";
10089c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return !mLocalProfile.getSipDomain().trim().equals(realm.trim());
10099c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
10109c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
10119c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private AccountManager getAccountManager() {
10129c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return new AccountManager() {
10139329db04f13480ccdff013dcc00cdb96f12a921cWink Saville                @Override
10149c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                public UserCredentials getCredentials(ClientTransaction
10159c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        challengedTransaction, String realm) {
10169c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    return new UserCredentials() {
10179329db04f13480ccdff013dcc00cdb96f12a921cWink Saville                        @Override
10189c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        public String getUserName() {
10191b666e24bacbe3695ca5fe2f996c177c3f10d5f0Chung-yih Wang                            String username = mLocalProfile.getAuthUserName();
10201b666e24bacbe3695ca5fe2f996c177c3f10d5f0Chung-yih Wang                            return (!TextUtils.isEmpty(username) ? username :
10211b666e24bacbe3695ca5fe2f996c177c3f10d5f0Chung-yih Wang                                    mLocalProfile.getUserName());
10229c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        }
10239c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
10249329db04f13480ccdff013dcc00cdb96f12a921cWink Saville                        @Override
10259c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        public String getPassword() {
10269c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            return mPassword;
10279c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        }
10289c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
10299329db04f13480ccdff013dcc00cdb96f12a921cWink Saville                        @Override
10309c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        public String getSipDomain() {
10319c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            return mLocalProfile.getSipDomain();
10329c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        }
10339c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    };
10349c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                }
10359c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            };
10369c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
10379c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
10389c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private String getRealmFromResponse(Response response) {
10399c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            WWWAuthenticate wwwAuth = (WWWAuthenticate)response.getHeader(
10409c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    SIPHeaderNames.WWW_AUTHENTICATE);
10419c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (wwwAuth != null) return wwwAuth.getRealm();
10429c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            ProxyAuthenticate proxyAuth = (ProxyAuthenticate)response.getHeader(
10439c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    SIPHeaderNames.PROXY_AUTHENTICATE);
10449c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return (proxyAuth == null) ? null : proxyAuth.getRealm();
10459c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
10469c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
10479c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private String getNonceFromResponse(Response response) {
10489c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            WWWAuthenticate wwwAuth = (WWWAuthenticate)response.getHeader(
10499c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    SIPHeaderNames.WWW_AUTHENTICATE);
10509c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (wwwAuth != null) return wwwAuth.getNonce();
10519c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            ProxyAuthenticate proxyAuth = (ProxyAuthenticate)response.getHeader(
10529c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    SIPHeaderNames.PROXY_AUTHENTICATE);
10539c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return (proxyAuth == null) ? null : proxyAuth.getNonce();
10549c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
10559c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
1056f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync        private String getResponseString(int statusCode) {
1057f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync            StatusLine statusLine = new StatusLine();
1058f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync            statusLine.setStatusCode(statusCode);
1059f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync            statusLine.setReasonPhrase(SIPResponse.getReasonPhrase(statusCode));
1060f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync            return statusLine.encode();
1061f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync        }
1062f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync
10639c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private boolean readyForCall(EventObject evt) throws SipException {
10649c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            // expect MakeCallCommand, RegisterCommand, DEREGISTER
10659c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (evt instanceof MakeCallCommand) {
1066d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                mState = SipSession.State.OUTGOING_CALL;
10679c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                MakeCallCommand cmd = (MakeCallCommand) evt;
10689c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mPeerProfile = cmd.getPeerProfile();
1069f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync                if (mReferSession != null) {
1070f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync                    mSipHelper.sendReferNotify(mReferSession.mDialog,
1071f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync                            getResponseString(Response.TRYING));
1072f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync                }
1073f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync                mClientTransaction = mSipHelper.sendInvite(
1074f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync                        mLocalProfile, mPeerProfile, cmd.getSessionDescription(),
1075f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync                        generateTag(), mReferredBy, mReplaces);
10769c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mDialog = mClientTransaction.getDialog();
10779c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                addSipSession(this);
10789c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                startSessionTimer(cmd.getTimeout());
1079e8782e2fdfd256296a0d1fd2a1c3f98d58103eaaHung-ying Tyan                mProxy.onCalling(this);
10809c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                return true;
10819c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            } else if (evt instanceof RegisterCommand) {
1082d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                mState = SipSession.State.REGISTERING;
10839c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                int duration = ((RegisterCommand) evt).getDuration();
10849c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mClientTransaction = mSipHelper.sendRegister(mLocalProfile,
10859c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        generateTag(), duration);
10869c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mDialog = mClientTransaction.getDialog();
10879c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                addSipSession(this);
10889c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mProxy.onRegistering(this);
10899c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                return true;
10909c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            } else if (DEREGISTER == evt) {
1091d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                mState = SipSession.State.DEREGISTERING;
10929c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mClientTransaction = mSipHelper.sendRegister(mLocalProfile,
10939c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        generateTag(), 0);
10949c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mDialog = mClientTransaction.getDialog();
10959c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                addSipSession(this);
10969c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mProxy.onRegistering(this);
10979c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                return true;
10989c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
10999c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return false;
11009c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
11019c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
11029c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private boolean incomingCall(EventObject evt) throws SipException {
11039c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            // expect MakeCallCommand(answering) , END_CALL cmd , Cancel
11049c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (evt instanceof MakeCallCommand) {
11059c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                // answer call
1106d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                mState = SipSession.State.INCOMING_CALL_ANSWERING;
11079c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mServerTransaction = mSipHelper.sendInviteOk(mInviteReceived,
11089c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        mLocalProfile,
11099c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        ((MakeCallCommand) evt).getSessionDescription(),
11103efff6c5840a99faadc3ee6197940c3290f65a62Hung-ying Tyan                        mServerTransaction,
11113efff6c5840a99faadc3ee6197940c3290f65a62Hung-ying Tyan                        mExternalIp, mExternalPort);
11129c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                startSessionTimer(((MakeCallCommand) evt).getTimeout());
11139c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                return true;
11149c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            } else if (END_CALL == evt) {
11159c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mSipHelper.sendInviteBusyHere(mInviteReceived,
11169c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        mServerTransaction);
11179c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                endCallNormally();
11189c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                return true;
11199c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            } else if (isRequestEvent(Request.CANCEL, evt)) {
11209c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                RequestEvent event = (RequestEvent) evt;
11219c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mSipHelper.sendResponse(event, Response.OK);
11229c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mSipHelper.sendInviteRequestTerminated(
11239c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        mInviteReceived.getRequest(), mServerTransaction);
11249c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                endCallNormally();
11259c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                return true;
11269c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
11279c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return false;
11289c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
11299c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
11309329db04f13480ccdff013dcc00cdb96f12a921cWink Saville        private boolean incomingCallToInCall(EventObject evt) {
11319c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            // expect ACK, CANCEL request
11329c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (isRequestEvent(Request.ACK, evt)) {
1133bb18b405c539e483cce67ae207bd7e6263c0d071repo sync                String sdp = extractContent(((RequestEvent) evt).getRequest());
1134bb18b405c539e483cce67ae207bd7e6263c0d071repo sync                if (sdp != null) mPeerSessionDescription = sdp;
1135bb18b405c539e483cce67ae207bd7e6263c0d071repo sync                if (mPeerSessionDescription == null) {
1136bb18b405c539e483cce67ae207bd7e6263c0d071repo sync                    onError(SipErrorCode.CLIENT_ERROR, "peer sdp is empty");
1137bb18b405c539e483cce67ae207bd7e6263c0d071repo sync                } else {
1138bb18b405c539e483cce67ae207bd7e6263c0d071repo sync                    establishCall(false);
1139bb18b405c539e483cce67ae207bd7e6263c0d071repo sync                }
11409c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                return true;
11419c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            } else if (isRequestEvent(Request.CANCEL, evt)) {
11429c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                // http://tools.ietf.org/html/rfc3261#section-9.2
11439c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                // Final response has been sent; do nothing here.
11449c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                return true;
11459c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
11469c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return false;
11479c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
11489c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
11499c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private boolean outgoingCall(EventObject evt) throws SipException {
11509c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (expectResponse(Request.INVITE, evt)) {
11519c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                ResponseEvent event = (ResponseEvent) evt;
11529c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                Response response = event.getResponse();
11539c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
11549c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                int statusCode = response.getStatusCode();
11559c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                switch (statusCode) {
11569c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                case Response.RINGING:
11576ca8ec4793540c6c0ad83574aa52e316a8ed17b4Hung-ying Tyan                case Response.CALL_IS_BEING_FORWARDED:
11586ca8ec4793540c6c0ad83574aa52e316a8ed17b4Hung-ying Tyan                case Response.QUEUED:
11596ca8ec4793540c6c0ad83574aa52e316a8ed17b4Hung-ying Tyan                case Response.SESSION_PROGRESS:
11606ca8ec4793540c6c0ad83574aa52e316a8ed17b4Hung-ying Tyan                    // feedback any provisional responses (except TRYING) as
11616ca8ec4793540c6c0ad83574aa52e316a8ed17b4Hung-ying Tyan                    // ring back for better UX
11629c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    if (mState == SipSession.State.OUTGOING_CALL) {
11639c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        mState = SipSession.State.OUTGOING_CALL_RING_BACK;
11649c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        cancelSessionTimer();
1165e8782e2fdfd256296a0d1fd2a1c3f98d58103eaaHung-ying Tyan                        mProxy.onRingingBack(this);
11669c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    }
11679c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    return true;
11689c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                case Response.OK:
1169f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync                    if (mReferSession != null) {
1170f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync                        mSipHelper.sendReferNotify(mReferSession.mDialog,
1171f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync                                getResponseString(Response.OK));
1172f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync                        // since we don't need to remember the session anymore.
1173f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync                        mReferSession = null;
1174f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync                    }
11759c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    mSipHelper.sendInviteAck(event, mDialog);
11769c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    mPeerSessionDescription = extractContent(response);
11770f8ceff66069c98480481241ba9d70dc34448188Hung-ying Tyan                    establishCall(true);
11789c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    return true;
11799c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                case Response.UNAUTHORIZED:
11809c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                case Response.PROXY_AUTHENTICATION_REQUIRED:
1181a0b7374a8afc7fb1db2eebb32ecba0f10db083cdHung-ying Tyan                    if (handleAuthentication(event)) {
11829c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        addSipSession(this);
11839c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    }
11849c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    return true;
11859c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                case Response.REQUEST_PENDING:
11869329db04f13480ccdff013dcc00cdb96f12a921cWink Saville                    // TODO: rfc3261#section-14.1; re-schedule invite
11879c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    return true;
11889c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                default:
1189f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync                    if (mReferSession != null) {
1190f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync                        mSipHelper.sendReferNotify(mReferSession.mDialog,
1191f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync                                getResponseString(Response.SERVICE_UNAVAILABLE));
1192f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync                    }
11939c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    if (statusCode >= 400) {
11949c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        // error: an ack is sent automatically by the stack
11959c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        onError(response);
11969c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        return true;
11979c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    } else if (statusCode >= 300) {
11989c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        // TODO: handle 3xx (redirect)
11999c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    } else {
12009c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        return true;
12019c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    }
12029c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                }
12039c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                return false;
12049c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            } else if (END_CALL == evt) {
12059c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                // RFC says that UA should not send out cancel when no
12069c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                // response comes back yet. We are cheating for not checking
12079c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                // response.
12089c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mState = SipSession.State.OUTGOING_CALL_CANCELING;
1209d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                mSipHelper.sendCancel(mClientTransaction);
12109c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                startSessionTimer(CANCEL_CALL_TIMER);
12119c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                return true;
1212ed3c0fbbd5457f43ff72cec31b8ab0bb3f7a0047Hung-ying Tyan            } else if (isRequestEvent(Request.INVITE, evt)) {
1213ed3c0fbbd5457f43ff72cec31b8ab0bb3f7a0047Hung-ying Tyan                // Call self? Send BUSY HERE so server may redirect the call to
1214ed3c0fbbd5457f43ff72cec31b8ab0bb3f7a0047Hung-ying Tyan                // voice mailbox.
1215ed3c0fbbd5457f43ff72cec31b8ab0bb3f7a0047Hung-ying Tyan                RequestEvent event = (RequestEvent) evt;
1216ed3c0fbbd5457f43ff72cec31b8ab0bb3f7a0047Hung-ying Tyan                mSipHelper.sendInviteBusyHere(event,
1217ed3c0fbbd5457f43ff72cec31b8ab0bb3f7a0047Hung-ying Tyan                        event.getServerTransaction());
1218ed3c0fbbd5457f43ff72cec31b8ab0bb3f7a0047Hung-ying Tyan                return true;
12199c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
12209c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return false;
12219c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
12229c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
12239c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private boolean outgoingCallToReady(EventObject evt)
12249c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                throws SipException {
12259c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (evt instanceof ResponseEvent) {
12269c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                ResponseEvent event = (ResponseEvent) evt;
12279c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                Response response = event.getResponse();
12289c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                int statusCode = response.getStatusCode();
12299c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                if (expectResponse(Request.CANCEL, evt)) {
12309c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    if (statusCode == Response.OK) {
12319c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        // do nothing; wait for REQUEST_TERMINATED
12329c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        return true;
12339c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    }
12349c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                } else if (expectResponse(Request.INVITE, evt)) {
12359c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    switch (statusCode) {
12369c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        case Response.OK:
12379c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            outgoingCall(evt); // abort Cancel
12389c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            return true;
12399c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        case Response.REQUEST_TERMINATED:
12409c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            endCallNormally();
12419c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            return true;
12429c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    }
12439c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                } else {
12449c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    return false;
12459c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                }
12469c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
12479c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                if (statusCode >= 400) {
12489c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    onError(response);
12499c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    return true;
12509c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                }
12519c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            } else if (evt instanceof TransactionTerminatedEvent) {
12529c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                // rfc3261#section-14.1:
12539c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                // if re-invite gets timed out, terminate the dialog; but
12549c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                // re-invite is not reliable, just let it go and pretend
12559c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                // nothing happened.
12569c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                onError(new SipException("timed out"));
12579c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
12589c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return false;
12599c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
12609c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
1261f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync        private boolean processReferRequest(RequestEvent event)
1262f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync                throws SipException {
1263f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync            try {
1264f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync                ReferToHeader referto = (ReferToHeader) event.getRequest()
1265f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync                        .getHeader(ReferTo.NAME);
1266f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync                Address address = referto.getAddress();
1267f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync                SipURI uri = (SipURI) address.getURI();
1268f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync                String replacesHeader = uri.getHeader(ReplacesHeader.NAME);
1269f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync                String username = uri.getUser();
1270f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync                if (username == null) {
1271f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync                    mSipHelper.sendResponse(event, Response.BAD_REQUEST);
1272f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync                    return false;
1273f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync                }
1274f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync                // send notify accepted
1275f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync                mSipHelper.sendResponse(event, Response.ACCEPTED);
1276f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync                SipSessionImpl newSession = createNewSession(event,
1277f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync                        this.mProxy.getListener(),
1278f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync                        mSipHelper.getServerTransaction(event),
1279f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync                        SipSession.State.READY_TO_CALL);
1280f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync                newSession.mReferSession = this;
1281f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync                newSession.mReferredBy = (ReferredByHeader) event.getRequest()
1282f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync                        .getHeader(ReferredByHeader.NAME);
1283f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync                newSession.mReplaces = replacesHeader;
1284f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync                newSession.mPeerProfile = createPeerProfile(referto);
1285f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync                newSession.mProxy.onCallTransferring(newSession,
1286f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync                        null);
1287f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync                return true;
1288f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync            } catch (IllegalArgumentException e) {
1289f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync                throw new SipException("createPeerProfile()", e);
1290f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync            }
1291f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync        }
1292f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync
12939c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private boolean inCall(EventObject evt) throws SipException {
12949c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            // expect END_CALL cmd, BYE request, hold call (MakeCallCommand)
12959c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            // OK retransmission is handled in SipStack
12969c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (END_CALL == evt) {
12979c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                // rfc3261#section-15.1.1
1298ceb525d85512bde884b0f01a3aa93f2943dcf5f3Hung-ying Tyan                mState = SipSession.State.ENDING_CALL;
12999c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mSipHelper.sendBye(mDialog);
1300ceb525d85512bde884b0f01a3aa93f2943dcf5f3Hung-ying Tyan                mProxy.onCallEnded(this);
1301ceb525d85512bde884b0f01a3aa93f2943dcf5f3Hung-ying Tyan                startSessionTimer(END_CALL_TIMER);
13029c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                return true;
13039c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            } else if (isRequestEvent(Request.INVITE, evt)) {
13049c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                // got Re-INVITE
13059c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mState = SipSession.State.INCOMING_CALL;
1306d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                RequestEvent event = mInviteReceived = (RequestEvent) evt;
13079c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mPeerSessionDescription = extractContent(event.getRequest());
13089c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mServerTransaction = null;
13099c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mProxy.onRinging(this, mPeerProfile, mPeerSessionDescription);
13109c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                return true;
13119c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            } else if (isRequestEvent(Request.BYE, evt)) {
13129c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mSipHelper.sendResponse((RequestEvent) evt, Response.OK);
13139c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                endCallNormally();
13149c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                return true;
1315f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync            } else if (isRequestEvent(Request.REFER, evt)) {
1316f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync                return processReferRequest((RequestEvent) evt);
13179c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            } else if (evt instanceof MakeCallCommand) {
13189c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                // to change call
1319d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                mState = SipSession.State.OUTGOING_CALL;
13209c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mClientTransaction = mSipHelper.sendReinvite(mDialog,
13219c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        ((MakeCallCommand) evt).getSessionDescription());
13229c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                startSessionTimer(((MakeCallCommand) evt).getTimeout());
13239c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                return true;
1324f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync            } else if (evt instanceof ResponseEvent) {
1325f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync                if (expectResponse(Request.NOTIFY, evt)) return true;
13269c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
13279c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return false;
13289c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
13299c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
1330ceb525d85512bde884b0f01a3aa93f2943dcf5f3Hung-ying Tyan        private boolean endingCall(EventObject evt) throws SipException {
1331ceb525d85512bde884b0f01a3aa93f2943dcf5f3Hung-ying Tyan            if (expectResponse(Request.BYE, evt)) {
1332ceb525d85512bde884b0f01a3aa93f2943dcf5f3Hung-ying Tyan                ResponseEvent event = (ResponseEvent) evt;
1333ceb525d85512bde884b0f01a3aa93f2943dcf5f3Hung-ying Tyan                Response response = event.getResponse();
1334ceb525d85512bde884b0f01a3aa93f2943dcf5f3Hung-ying Tyan
1335ceb525d85512bde884b0f01a3aa93f2943dcf5f3Hung-ying Tyan                int statusCode = response.getStatusCode();
1336ceb525d85512bde884b0f01a3aa93f2943dcf5f3Hung-ying Tyan                switch (statusCode) {
1337ceb525d85512bde884b0f01a3aa93f2943dcf5f3Hung-ying Tyan                    case Response.UNAUTHORIZED:
1338ceb525d85512bde884b0f01a3aa93f2943dcf5f3Hung-ying Tyan                    case Response.PROXY_AUTHENTICATION_REQUIRED:
1339ceb525d85512bde884b0f01a3aa93f2943dcf5f3Hung-ying Tyan                        if (handleAuthentication(event)) {
1340ceb525d85512bde884b0f01a3aa93f2943dcf5f3Hung-ying Tyan                            return true;
1341ceb525d85512bde884b0f01a3aa93f2943dcf5f3Hung-ying Tyan                        } else {
1342ceb525d85512bde884b0f01a3aa93f2943dcf5f3Hung-ying Tyan                            // can't authenticate; pass through to end session
1343ceb525d85512bde884b0f01a3aa93f2943dcf5f3Hung-ying Tyan                        }
1344ceb525d85512bde884b0f01a3aa93f2943dcf5f3Hung-ying Tyan                }
1345ceb525d85512bde884b0f01a3aa93f2943dcf5f3Hung-ying Tyan                cancelSessionTimer();
1346ceb525d85512bde884b0f01a3aa93f2943dcf5f3Hung-ying Tyan                reset();
1347ceb525d85512bde884b0f01a3aa93f2943dcf5f3Hung-ying Tyan                return true;
1348ceb525d85512bde884b0f01a3aa93f2943dcf5f3Hung-ying Tyan            }
1349ceb525d85512bde884b0f01a3aa93f2943dcf5f3Hung-ying Tyan            return false;
1350ceb525d85512bde884b0f01a3aa93f2943dcf5f3Hung-ying Tyan        }
1351ceb525d85512bde884b0f01a3aa93f2943dcf5f3Hung-ying Tyan
13529c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        // timeout in seconds
13539c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private void startSessionTimer(int timeout) {
13549c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (timeout > 0) {
13558a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                mSessionTimer = new SessionTimer();
13568a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                mSessionTimer.start(timeout);
13579c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
13589c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
13599c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
13609c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private void cancelSessionTimer() {
13618a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan            if (mSessionTimer != null) {
13628a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                mSessionTimer.cancel();
13638a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                mSessionTimer = null;
13649c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
13659c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
13669c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
13679c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private String createErrorMessage(Response response) {
13683b9456929376a047ef38b5ddb27fed9b11664306Hung-ying Tyan            return String.format("%s (%d)", response.getReasonPhrase(),
13693b9456929376a047ef38b5ddb27fed9b11664306Hung-ying Tyan                    response.getStatusCode());
13709c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
13719c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
13720f8ceff66069c98480481241ba9d70dc34448188Hung-ying Tyan        private void enableKeepAlive() {
13739329db04f13480ccdff013dcc00cdb96f12a921cWink Saville            if (mSipSessionImpl != null) {
13749329db04f13480ccdff013dcc00cdb96f12a921cWink Saville                mSipSessionImpl.stopKeepAliveProcess();
13750f8ceff66069c98480481241ba9d70dc34448188Hung-ying Tyan            } else {
13769329db04f13480ccdff013dcc00cdb96f12a921cWink Saville                mSipSessionImpl = duplicate();
13770f8ceff66069c98480481241ba9d70dc34448188Hung-ying Tyan            }
13780f8ceff66069c98480481241ba9d70dc34448188Hung-ying Tyan            try {
13799329db04f13480ccdff013dcc00cdb96f12a921cWink Saville                mSipSessionImpl.startKeepAliveProcess(
13800f8ceff66069c98480481241ba9d70dc34448188Hung-ying Tyan                        INCALL_KEEPALIVE_INTERVAL, mPeerProfile, null);
13810f8ceff66069c98480481241ba9d70dc34448188Hung-ying Tyan            } catch (SipException e) {
13829329db04f13480ccdff013dcc00cdb96f12a921cWink Saville                loge("keepalive cannot be enabled; ignored", e);
13839329db04f13480ccdff013dcc00cdb96f12a921cWink Saville                mSipSessionImpl.stopKeepAliveProcess();
13840f8ceff66069c98480481241ba9d70dc34448188Hung-ying Tyan            }
13850f8ceff66069c98480481241ba9d70dc34448188Hung-ying Tyan        }
13860f8ceff66069c98480481241ba9d70dc34448188Hung-ying Tyan
13870f8ceff66069c98480481241ba9d70dc34448188Hung-ying Tyan        private void establishCall(boolean enableKeepAlive) {
13889c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mState = SipSession.State.IN_CALL;
13899c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            cancelSessionTimer();
1390108e51ec4c06c75db2a11d1417f1bc8c0545ea8bHung-ying Tyan            if (!mInCall && enableKeepAlive) enableKeepAlive();
1391108e51ec4c06c75db2a11d1417f1bc8c0545ea8bHung-ying Tyan            mInCall = true;
13929c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mProxy.onCallEstablished(this, mPeerSessionDescription);
13939c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
13949c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
13959c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private void endCallNormally() {
13969c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            reset();
13979c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mProxy.onCallEnded(this);
13989c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
13999c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
14009c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private void endCallOnError(int errorCode, String message) {
14019c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            reset();
14029c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mProxy.onError(this, errorCode, message);
14039c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
14049c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
14059c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private void endCallOnBusy() {
14069c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            reset();
14079c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mProxy.onCallBusy(this);
14089c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
14099c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
14109c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private void onError(int errorCode, String message) {
14119c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            cancelSessionTimer();
14129c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            switch (mState) {
14139c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                case SipSession.State.REGISTERING:
14149c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                case SipSession.State.DEREGISTERING:
14159c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    onRegistrationFailed(errorCode, message);
14169c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    break;
14179c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                default:
1418bddf530f0bbd2917d98c3fd0f11920c2b2473154Hung-ying Tyan                    endCallOnError(errorCode, message);
14199c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
14209c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
14219c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
14229c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
14239c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private void onError(Throwable exception) {
14249c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            exception = getRootCause(exception);
14259c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            onError(getErrorCode(exception), exception.toString());
14269c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
14279c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
14289c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private void onError(Response response) {
14299c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            int statusCode = response.getStatusCode();
14309c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (!mInCall && (statusCode == Response.BUSY_HERE)) {
14319c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                endCallOnBusy();
14329c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            } else {
14339c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                onError(getErrorCode(statusCode), createErrorMessage(response));
14349c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
14359c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
14369c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
14379c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private int getErrorCode(int responseStatusCode) {
14389c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            switch (responseStatusCode) {
14399c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                case Response.TEMPORARILY_UNAVAILABLE:
14409c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                case Response.FORBIDDEN:
14419c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                case Response.GONE:
14429c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                case Response.NOT_FOUND:
14439c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                case Response.NOT_ACCEPTABLE:
14449c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                case Response.NOT_ACCEPTABLE_HERE:
14459c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    return SipErrorCode.PEER_NOT_REACHABLE;
14469c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
14479c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                case Response.REQUEST_URI_TOO_LONG:
14489c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                case Response.ADDRESS_INCOMPLETE:
14499c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                case Response.AMBIGUOUS:
14509c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    return SipErrorCode.INVALID_REMOTE_URI;
14519c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
14529c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                case Response.REQUEST_TIMEOUT:
14539c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    return SipErrorCode.TIME_OUT;
14549c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
14559c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                default:
14569c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    if (responseStatusCode < 500) {
14579c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        return SipErrorCode.CLIENT_ERROR;
14589c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    } else {
14599c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        return SipErrorCode.SERVER_ERROR;
14609c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    }
14619c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
14629c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
14639c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
14649c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private int getErrorCode(Throwable exception) {
14659c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            String message = exception.getMessage();
14669c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (exception instanceof UnknownHostException) {
1467e26eb3274a65c41a6a30bdace1818c5629cca1c8Hung-ying Tyan                return SipErrorCode.SERVER_UNREACHABLE;
14689c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            } else if (exception instanceof IOException) {
14699c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                return SipErrorCode.SOCKET_ERROR;
14709c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            } else {
14719c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                return SipErrorCode.CLIENT_ERROR;
14729c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
14739c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
14749c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
14759c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private void onRegistrationDone(int duration) {
14769c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            reset();
14779c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mProxy.onRegistrationDone(this, duration);
14789c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
14799c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
14809c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private void onRegistrationFailed(int errorCode, String message) {
14819c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            reset();
14829c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mProxy.onRegistrationFailed(this, errorCode, message);
14839c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
14849c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
14859c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private void onRegistrationFailed(Response response) {
14869c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            int statusCode = response.getStatusCode();
14879c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            onRegistrationFailed(getErrorCode(statusCode),
14889c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    createErrorMessage(response));
14899c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
14908a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan
14918a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        // Notes: SipSessionListener will be replaced by the keepalive process
14928a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        // @param interval in seconds
14938a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        public void startKeepAliveProcess(int interval,
14948a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                KeepAliveProcessCallback callback) throws SipException {
14958a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan            synchronized (SipSessionGroup.this) {
14968a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                startKeepAliveProcess(interval, mLocalProfile, callback);
14978a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan            }
14988a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        }
14998a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan
15008a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        // Notes: SipSessionListener will be replaced by the keepalive process
15018a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        // @param interval in seconds
15028a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        public void startKeepAliveProcess(int interval, SipProfile peerProfile,
15038a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                KeepAliveProcessCallback callback) throws SipException {
15048a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan            synchronized (SipSessionGroup.this) {
15059329db04f13480ccdff013dcc00cdb96f12a921cWink Saville                if (mSipKeepAlive != null) {
15068a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                    throw new SipException("Cannot create more than one "
15078a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                            + "keepalive process in a SipSession");
15088a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                }
15098a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                mPeerProfile = peerProfile;
15109329db04f13480ccdff013dcc00cdb96f12a921cWink Saville                mSipKeepAlive = new SipKeepAlive();
15119329db04f13480ccdff013dcc00cdb96f12a921cWink Saville                mProxy.setListener(mSipKeepAlive);
15129329db04f13480ccdff013dcc00cdb96f12a921cWink Saville                mSipKeepAlive.start(interval, callback);
15138a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan            }
15148a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        }
15158a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan
15168a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        public void stopKeepAliveProcess() {
15178a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan            synchronized (SipSessionGroup.this) {
15189329db04f13480ccdff013dcc00cdb96f12a921cWink Saville                if (mSipKeepAlive != null) {
15199329db04f13480ccdff013dcc00cdb96f12a921cWink Saville                    mSipKeepAlive.stop();
15209329db04f13480ccdff013dcc00cdb96f12a921cWink Saville                    mSipKeepAlive = null;
15218a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                }
15228a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan            }
15238a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        }
15248a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan
15259329db04f13480ccdff013dcc00cdb96f12a921cWink Saville        class SipKeepAlive extends SipSessionAdapter implements Runnable {
15269329db04f13480ccdff013dcc00cdb96f12a921cWink Saville            private static final String SKA_TAG = "SipKeepAlive";
15279329db04f13480ccdff013dcc00cdb96f12a921cWink Saville            private static final boolean SKA_DBG = true;
15289329db04f13480ccdff013dcc00cdb96f12a921cWink Saville
15298a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan            private boolean mRunning = false;
15308a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan            private KeepAliveProcessCallback mCallback;
15318a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan
15328a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan            private boolean mPortChanged = false;
15338a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan            private int mRPort = 0;
153444ccfb03bb349a90546ddb3dc0063cbf4aaddaf1Hung-ying Tyan            private int mInterval; // just for debugging
15358a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan
15368a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan            // @param interval in seconds
15378a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan            void start(int interval, KeepAliveProcessCallback callback) {
15388a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                if (mRunning) return;
15398a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                mRunning = true;
154044ccfb03bb349a90546ddb3dc0063cbf4aaddaf1Hung-ying Tyan                mInterval = interval;
15418a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                mCallback = new KeepAliveProcessCallbackProxy(callback);
15428a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                mWakeupTimer.set(interval * 1000, this);
15439329db04f13480ccdff013dcc00cdb96f12a921cWink Saville                if (SKA_DBG) {
15449329db04f13480ccdff013dcc00cdb96f12a921cWink Saville                    log("start keepalive:"
15458a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                            + mLocalProfile.getUriString());
15468a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                }
15478a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan
15488a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                // No need to run the first time in a separate thread for now
15498a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                run();
15508a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan            }
15518a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan
15528a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan            // return true if the event is consumed
15539329db04f13480ccdff013dcc00cdb96f12a921cWink Saville            boolean process(EventObject evt) {
15548a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                if (mRunning && (mState == SipSession.State.PINGING)) {
15558a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                    if (evt instanceof ResponseEvent) {
15568a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                        if (parseOptionsResult(evt)) {
15578a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                            if (mPortChanged) {
15583efff6c5840a99faadc3ee6197940c3290f65a62Hung-ying Tyan                                resetExternalAddress();
15598a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                                stop();
15608a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                            } else {
15618a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                                cancelSessionTimer();
15628a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                                removeSipSession(SipSessionImpl.this);
15638a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                            }
15648a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                            mCallback.onResponse(mPortChanged);
15658a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                            return true;
15668a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                        }
15678a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                    }
15688a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                }
15698a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                return false;
15708a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan            }
15718a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan
15728a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan            // SipSessionAdapter
15738a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan            // To react to the session timeout event and network error.
15748a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan            @Override
15758a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan            public void onError(ISipSession session, int errorCode, String message) {
15768a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                stop();
15778a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                mCallback.onError(errorCode, message);
15788a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan            }
15798a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan
15808a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan            // SipWakeupTimer timeout handler
15818a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan            // To send out keepalive message.
15828a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan            @Override
15838a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan            public void run() {
15848a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                synchronized (SipSessionGroup.this) {
15858a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                    if (!mRunning) return;
15868a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan
15879329db04f13480ccdff013dcc00cdb96f12a921cWink Saville                    if (DBG_PING) {
15883efff6c5840a99faadc3ee6197940c3290f65a62Hung-ying Tyan                        String peerUri = (mPeerProfile == null)
15893efff6c5840a99faadc3ee6197940c3290f65a62Hung-ying Tyan                                ? "null"
15903efff6c5840a99faadc3ee6197940c3290f65a62Hung-ying Tyan                                : mPeerProfile.getUriString();
15919329db04f13480ccdff013dcc00cdb96f12a921cWink Saville                        log("keepalive: " + mLocalProfile.getUriString()
15923efff6c5840a99faadc3ee6197940c3290f65a62Hung-ying Tyan                                + " --> " + peerUri + ", interval=" + mInterval);
15938a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                    }
15948a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                    try {
15958a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                        sendKeepAlive();
15968a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                    } catch (Throwable t) {
15979329db04f13480ccdff013dcc00cdb96f12a921cWink Saville                        if (SKA_DBG) {
15989329db04f13480ccdff013dcc00cdb96f12a921cWink Saville                            loge("keepalive error: "
159985caf063c027ba0d1defeaff8850e12b6428c4f7Chia-chi Yeh                                    + mLocalProfile.getUriString(), getRootCause(t));
160085caf063c027ba0d1defeaff8850e12b6428c4f7Chia-chi Yeh                        }
16018a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                        // It's possible that the keepalive process is being stopped
16028a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                        // during session.sendKeepAlive() so need to check mRunning
16038a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                        // again here.
16048a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                        if (mRunning) SipSessionImpl.this.onError(t);
16058a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                    }
16068a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                }
16078a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan            }
16088a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan
16098a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan            void stop() {
16108a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                synchronized (SipSessionGroup.this) {
16119329db04f13480ccdff013dcc00cdb96f12a921cWink Saville                    if (SKA_DBG) {
16129329db04f13480ccdff013dcc00cdb96f12a921cWink Saville                        log("stop keepalive:" + mLocalProfile.getUriString()
16138a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                                + ",RPort=" + mRPort);
16148a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                    }
16158a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                    mRunning = false;
16168a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                    mWakeupTimer.cancel(this);
16178a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                    reset();
16188a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                }
16198a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan            }
16208a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan
16219329db04f13480ccdff013dcc00cdb96f12a921cWink Saville            private void sendKeepAlive() throws SipException {
16228a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                synchronized (SipSessionGroup.this) {
16238a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                    mState = SipSession.State.PINGING;
16248a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                    mClientTransaction = mSipHelper.sendOptions(
16258a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                            mLocalProfile, mPeerProfile, generateTag());
16268a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                    mDialog = mClientTransaction.getDialog();
16278a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                    addSipSession(SipSessionImpl.this);
16288a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan
16298a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                    startSessionTimer(KEEPALIVE_TIMEOUT);
16308a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                    // when timed out, onError() will be called with SipErrorCode.TIME_OUT
16318a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                }
16328a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan            }
16338a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan
16348a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan            private boolean parseOptionsResult(EventObject evt) {
16358a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                if (expectResponse(Request.OPTIONS, evt)) {
16368a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                    ResponseEvent event = (ResponseEvent) evt;
16378a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                    int rPort = getRPortFromResponse(event.getResponse());
16388a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                    if (rPort != -1) {
16398a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                        if (mRPort == 0) mRPort = rPort;
16408a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                        if (mRPort != rPort) {
16418a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                            mPortChanged = true;
16429329db04f13480ccdff013dcc00cdb96f12a921cWink Saville                            if (SKA_DBG) log(String.format(
16438a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                                    "rport is changed: %d <> %d", mRPort, rPort));
16448a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                            mRPort = rPort;
16458a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                        } else {
16469329db04f13480ccdff013dcc00cdb96f12a921cWink Saville                            if (SKA_DBG) log("rport is the same: " + rPort);
16478a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                        }
16488a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                    } else {
16499329db04f13480ccdff013dcc00cdb96f12a921cWink Saville                        if (SKA_DBG) log("peer did not respond rport");
16508a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                    }
16518a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                    return true;
16528a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                }
16538a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                return false;
16548a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan            }
16558a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan
16568a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan            private int getRPortFromResponse(Response response) {
16578a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                ViaHeader viaHeader = (ViaHeader)(response.getHeader(
16588a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                        SIPHeaderNames.VIA));
16598a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                return (viaHeader == null) ? -1 : viaHeader.getRPort();
16608a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan            }
16619329db04f13480ccdff013dcc00cdb96f12a921cWink Saville
16629329db04f13480ccdff013dcc00cdb96f12a921cWink Saville            private void log(String s) {
16639329db04f13480ccdff013dcc00cdb96f12a921cWink Saville                Rlog.d(SKA_TAG, s);
16649329db04f13480ccdff013dcc00cdb96f12a921cWink Saville            }
16659329db04f13480ccdff013dcc00cdb96f12a921cWink Saville        }
16669329db04f13480ccdff013dcc00cdb96f12a921cWink Saville
16679329db04f13480ccdff013dcc00cdb96f12a921cWink Saville        private void log(String s) {
16689329db04f13480ccdff013dcc00cdb96f12a921cWink Saville            Rlog.d(SSI_TAG, s);
16698a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        }
16709c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
16719c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
16729c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    /**
16739c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan     * @return true if the event is a request event matching the specified
16749c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan     *      method; false otherwise
16759c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan     */
16769c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private static boolean isRequestEvent(String method, EventObject event) {
16779c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        try {
16789c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (event instanceof RequestEvent) {
16799c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                RequestEvent requestEvent = (RequestEvent) event;
16809c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                return method.equals(requestEvent.getRequest().getMethod());
16819c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
16829c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        } catch (Throwable e) {
16839c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
16849c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        return false;
16859c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
16869c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
16879c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private static String getCseqMethod(Message message) {
16889c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        return ((CSeqHeader) message.getHeader(CSeqHeader.NAME)).getMethod();
16899c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
16909c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
16919c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    /**
16929c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan     * @return true if the event is a response event and the CSeqHeader method
16939c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan     * match the given arguments; false otherwise
16949c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan     */
16959c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private static boolean expectResponse(
16969c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            String expectedMethod, EventObject evt) {
16979c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        if (evt instanceof ResponseEvent) {
16989c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            ResponseEvent event = (ResponseEvent) evt;
16999c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            Response response = event.getResponse();
17009c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return expectedMethod.equalsIgnoreCase(getCseqMethod(response));
17019c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
17029c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        return false;
17039c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
17049c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
1705f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync    private static SipProfile createPeerProfile(HeaderAddress header)
17069c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            throws SipException {
17079c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        try {
1708f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync            Address address = header.getAddress();
17099c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            SipURI uri = (SipURI) address.getURI();
17109c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            String username = uri.getUser();
17119c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (username == null) username = ANONYMOUS;
1712007493a9f98959c350c3787230df9afa346ee938Hung-ying Tyan            int port = uri.getPort();
1713007493a9f98959c350c3787230df9afa346ee938Hung-ying Tyan            SipProfile.Builder builder =
1714007493a9f98959c350c3787230df9afa346ee938Hung-ying Tyan                    new SipProfile.Builder(username, uri.getHost())
1715007493a9f98959c350c3787230df9afa346ee938Hung-ying Tyan                    .setDisplayName(address.getDisplayName());
1716007493a9f98959c350c3787230df9afa346ee938Hung-ying Tyan            if (port > 0) builder.setPort(port);
1717007493a9f98959c350c3787230df9afa346ee938Hung-ying Tyan            return builder.build();
17189c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        } catch (IllegalArgumentException e) {
17199c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            throw new SipException("createPeerProfile()", e);
17209c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        } catch (ParseException e) {
17219c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            throw new SipException("createPeerProfile()", e);
17229c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
17239c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
17249c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
17259c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private static boolean isLoggable(SipSessionImpl s) {
17269c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        if (s != null) {
17279c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            switch (s.mState) {
17289c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                case SipSession.State.PINGING:
17299329db04f13480ccdff013dcc00cdb96f12a921cWink Saville                    return DBG_PING;
17309c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
17319c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
17329329db04f13480ccdff013dcc00cdb96f12a921cWink Saville        return DBG;
17339c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
17349c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
1735ed3c0fbbd5457f43ff72cec31b8ab0bb3f7a0047Hung-ying Tyan    private static boolean isLoggable(EventObject evt) {
1736ed3c0fbbd5457f43ff72cec31b8ab0bb3f7a0047Hung-ying Tyan        return isLoggable(null, evt);
1737ed3c0fbbd5457f43ff72cec31b8ab0bb3f7a0047Hung-ying Tyan    }
1738ed3c0fbbd5457f43ff72cec31b8ab0bb3f7a0047Hung-ying Tyan
17399c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private static boolean isLoggable(SipSessionImpl s, EventObject evt) {
17409c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        if (!isLoggable(s)) return false;
17419c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        if (evt == null) return false;
17429c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
17438a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        if (evt instanceof ResponseEvent) {
17449c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            Response response = ((ResponseEvent) evt).getResponse();
17459c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (Request.OPTIONS.equals(response.getHeader(CSeqHeader.NAME))) {
17469329db04f13480ccdff013dcc00cdb96f12a921cWink Saville                return DBG_PING;
17479c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
17489329db04f13480ccdff013dcc00cdb96f12a921cWink Saville            return DBG;
17499c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        } else if (evt instanceof RequestEvent) {
17508a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan            if (isRequestEvent(Request.OPTIONS, evt)) {
17519329db04f13480ccdff013dcc00cdb96f12a921cWink Saville                return DBG_PING;
17528a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan            }
17539329db04f13480ccdff013dcc00cdb96f12a921cWink Saville            return DBG;
17549c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
17559c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        return false;
17569c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
17579c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
17589329db04f13480ccdff013dcc00cdb96f12a921cWink Saville    private static String logEvt(EventObject evt) {
17599c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        if (evt instanceof RequestEvent) {
17609c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return ((RequestEvent) evt).getRequest().toString();
17619c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        } else if (evt instanceof ResponseEvent) {
17629c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return ((ResponseEvent) evt).getResponse().toString();
17639c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        } else {
17649c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return evt.toString();
17659c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
17669c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
17679c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
17689c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private class RegisterCommand extends EventObject {
17699c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private int mDuration;
17709c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
17719c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public RegisterCommand(int duration) {
17729c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            super(SipSessionGroup.this);
17739c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mDuration = duration;
17749c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
17759c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
17769c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public int getDuration() {
17779c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return mDuration;
17789c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
17799c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
17809c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
17819c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private class MakeCallCommand extends EventObject {
17829c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private String mSessionDescription;
17839c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private int mTimeout; // in seconds
17849c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
17859c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public MakeCallCommand(SipProfile peerProfile,
17869c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                String sessionDescription, int timeout) {
17879c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            super(peerProfile);
17889c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mSessionDescription = sessionDescription;
17899c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mTimeout = timeout;
17909c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
17919c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
17929c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public SipProfile getPeerProfile() {
17939c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return (SipProfile) getSource();
17949c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
17959c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
17969c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public String getSessionDescription() {
17979c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return mSessionDescription;
17989c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
17999c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
18009c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public int getTimeout() {
18019c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return mTimeout;
18029c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
18039c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
18048a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan
18058a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan    /** Class to help safely run KeepAliveProcessCallback in a different thread. */
18068a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan    static class KeepAliveProcessCallbackProxy implements KeepAliveProcessCallback {
18079329db04f13480ccdff013dcc00cdb96f12a921cWink Saville        private static final String KAPCP_TAG = "KeepAliveProcessCallbackProxy";
18088a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        private KeepAliveProcessCallback mCallback;
18098a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan
18108a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        KeepAliveProcessCallbackProxy(KeepAliveProcessCallback callback) {
18118a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan            mCallback = callback;
18128a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        }
18138a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan
18148a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        private void proxy(Runnable runnable) {
18158a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan            // One thread for each calling back.
18168a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan            // Note: Guarantee ordering if the issue becomes important. Currently,
18178a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan            // the chance of handling two callback events at a time is none.
18188a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan            new Thread(runnable, "SIP-KeepAliveProcessCallbackThread").start();
18198a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        }
18208a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan
18219329db04f13480ccdff013dcc00cdb96f12a921cWink Saville        @Override
18228a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        public void onResponse(final boolean portChanged) {
18238a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan            if (mCallback == null) return;
18248a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan            proxy(new Runnable() {
18259329db04f13480ccdff013dcc00cdb96f12a921cWink Saville                @Override
18268a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                public void run() {
18278a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                    try {
18288a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                        mCallback.onResponse(portChanged);
18298a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                    } catch (Throwable t) {
18309329db04f13480ccdff013dcc00cdb96f12a921cWink Saville                        loge("onResponse", t);
18318a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                    }
18328a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                }
18338a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan            });
18348a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        }
18358a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan
18369329db04f13480ccdff013dcc00cdb96f12a921cWink Saville        @Override
18378a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        public void onError(final int errorCode, final String description) {
18388a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan            if (mCallback == null) return;
18398a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan            proxy(new Runnable() {
18409329db04f13480ccdff013dcc00cdb96f12a921cWink Saville                @Override
18418a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                public void run() {
18428a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                    try {
18438a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                        mCallback.onError(errorCode, description);
18448a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                    } catch (Throwable t) {
18459329db04f13480ccdff013dcc00cdb96f12a921cWink Saville                        loge("onError", t);
18468a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                    }
18478a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan                }
18488a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan            });
18498a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan        }
18509329db04f13480ccdff013dcc00cdb96f12a921cWink Saville
18519329db04f13480ccdff013dcc00cdb96f12a921cWink Saville        private void loge(String s, Throwable t) {
18529329db04f13480ccdff013dcc00cdb96f12a921cWink Saville            Rlog.e(KAPCP_TAG, s, t);
18539329db04f13480ccdff013dcc00cdb96f12a921cWink Saville        }
18549329db04f13480ccdff013dcc00cdb96f12a921cWink Saville    }
18559329db04f13480ccdff013dcc00cdb96f12a921cWink Saville
18569329db04f13480ccdff013dcc00cdb96f12a921cWink Saville    private void log(String s) {
18579329db04f13480ccdff013dcc00cdb96f12a921cWink Saville        Rlog.d(TAG, s);
18589329db04f13480ccdff013dcc00cdb96f12a921cWink Saville    }
18599329db04f13480ccdff013dcc00cdb96f12a921cWink Saville
18609329db04f13480ccdff013dcc00cdb96f12a921cWink Saville    private void loge(String s, Throwable t) {
18619329db04f13480ccdff013dcc00cdb96f12a921cWink Saville        Rlog.e(TAG, s, t);
18628a044dee1690c3dc7b5f467461ce4fa65c565a66Hung-ying Tyan    }
18639c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan}
1864