12d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang/*
22d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang * Copyright (C) 2010 The Android Open Source Project
32d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang *
42d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang * Licensed under the Apache License, Version 2.0 (the "License");
52d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang * you may not use this file except in compliance with the License.
62d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang * You may obtain a copy of the License at
72d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang *
82d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang *      http://www.apache.org/licenses/LICENSE-2.0
92d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang *
102d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang * Unless required by applicable law or agreed to in writing, software
112d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang * distributed under the License is distributed on an "AS IS" BASIS,
122d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
132d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang * See the License for the specific language governing permissions and
142d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang * limitations under the License.
152d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang */
162d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
172d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangpackage com.android.server.sip;
182d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
192d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport gov.nist.javax.sip.clientauthutils.AccountManager;
202d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport gov.nist.javax.sip.clientauthutils.UserCredentials;
217d137e40cd36290c6bfb5beaf66f4018ae92c97fChung-yih Wangimport gov.nist.javax.sip.header.ProxyAuthenticate;
22307f15faafa5a38d9b3b314df22778cd11685d7brepo syncimport gov.nist.javax.sip.header.ReferTo;
23307f15faafa5a38d9b3b314df22778cd11685d7brepo syncimport gov.nist.javax.sip.header.SIPHeaderNames;
24307f15faafa5a38d9b3b314df22778cd11685d7brepo syncimport gov.nist.javax.sip.header.StatusLine;
252d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport gov.nist.javax.sip.header.WWWAuthenticate;
261aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo syncimport gov.nist.javax.sip.header.extensions.ReferredByHeader;
271aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo syncimport gov.nist.javax.sip.header.extensions.ReplacesHeader;
2895b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yehimport gov.nist.javax.sip.message.SIPMessage;
29307f15faafa5a38d9b3b314df22778cd11685d7brepo syncimport gov.nist.javax.sip.message.SIPResponse;
302d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
312d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport android.net.sip.ISipSession;
322d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport android.net.sip.ISipSessionListener;
33903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyanimport android.net.sip.SipErrorCode;
342d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport android.net.sip.SipProfile;
3584a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyanimport android.net.sip.SipSession;
364a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyanimport android.net.sip.SipSessionAdapter;
372d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport android.text.TextUtils;
382d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport android.util.Log;
392d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
402d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport java.io.IOException;
4195b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yehimport java.io.UnsupportedEncodingException;
422d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport java.net.DatagramSocket;
432dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yehimport java.net.InetAddress;
44903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyanimport java.net.UnknownHostException;
452d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport java.text.ParseException;
462d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport java.util.Collection;
472d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport java.util.EventObject;
482d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport java.util.HashMap;
492d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport java.util.Map;
502d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport java.util.Properties;
512d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
522d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.ClientTransaction;
532d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.Dialog;
542d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.DialogTerminatedEvent;
552d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.IOExceptionEvent;
562d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.ListeningPoint;
579ea96c6cade1f25d4d77dcbd24854df431548b36Hung-ying Tyanimport javax.sip.ObjectInUseException;
582d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.RequestEvent;
592d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.ResponseEvent;
602d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.ServerTransaction;
612d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.SipException;
622d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.SipFactory;
632d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.SipListener;
642d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.SipProvider;
652d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.SipStack;
662d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.TimeoutEvent;
672d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.Transaction;
682d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.TransactionState;
692d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.TransactionTerminatedEvent;
70903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyanimport javax.sip.TransactionUnavailableException;
712d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.address.Address;
722d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.address.SipURI;
732d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.header.CSeqHeader;
745f760064e1975a50e4abb63e560731c8b2c7b56cChia-chi Yehimport javax.sip.header.ContactHeader;
752d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.header.ExpiresHeader;
762d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.header.FromHeader;
77307f15faafa5a38d9b3b314df22778cd11685d7brepo syncimport javax.sip.header.HeaderAddress;
782d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.header.MinExpiresHeader;
79307f15faafa5a38d9b3b314df22778cd11685d7brepo syncimport javax.sip.header.ReferToHeader;
802d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.header.ViaHeader;
812d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.message.Message;
822d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.message.Request;
832d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.message.Response;
842d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
85307f15faafa5a38d9b3b314df22778cd11685d7brepo sync
862d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang/**
872d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang * Manages {@link ISipSession}'s for a SIP account.
882d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang */
892d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangclass SipSessionGroup implements SipListener {
902d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private static final String TAG = "SipSession";
91cb6ee06f62c20ae036a206667097f20b837b11abChia-chi Yeh    private static final boolean DEBUG = false;
92cb6ee06f62c20ae036a206667097f20b837b11abChia-chi Yeh    private static final boolean DEBUG_PING = false;
932d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private static final String ANONYMOUS = "anonymous";
9466cc5355a137e291cc1e3c5d871e1d9cd35ee0abChung-yih Wang    // Limit the size of thread pool to 1 for the order issue when the phone is
9566cc5355a137e291cc1e3c5d871e1d9cd35ee0abChung-yih Wang    // waken up from sleep and there are many packets to be processed in the SIP
9666cc5355a137e291cc1e3c5d871e1d9cd35ee0abChung-yih Wang    // stack. Note: The default thread pool size in NIST SIP stack is -1 which is
9766cc5355a137e291cc1e3c5d871e1d9cd35ee0abChung-yih Wang    // unlimited.
9866cc5355a137e291cc1e3c5d871e1d9cd35ee0abChung-yih Wang    private static final String THREAD_POOL_SIZE = "1";
999352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan    private static final int EXPIRY_TIME = 3600; // in seconds
100194bbcce9ba15634500f542b9ea017b2cf154b45Hung-ying Tyan    private static final int CANCEL_CALL_TIMER = 3; // in seconds
101dc5bbe965f7a66238c3f9a6694f4410b3d52af3bHung-ying Tyan    private static final int END_CALL_TIMER = 3; // in seconds
102d17b6d526648c372be761097e55c19767d5dba7dChia-chi Yeh    private static final int KEEPALIVE_TIMEOUT = 5; // in seconds
103233718c3c5a4f5b4f564af93cb2e42d80a900904Hung-ying Tyan    private static final int INCALL_KEEPALIVE_INTERVAL = 10; // in seconds
10428f63c06894b9ca9252f43bc54a098c0a785d4b4Hung-ying Tyan    private static final long WAKE_LOCK_HOLDING_TIME = 500; // in milliseconds
1052d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
1062d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private static final EventObject DEREGISTER = new EventObject("Deregister");
1072d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private static final EventObject END_CALL = new EventObject("End call");
1082d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private static final EventObject HOLD_CALL = new EventObject("Hold call");
1092d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private static final EventObject CONTINUE_CALL
1102d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            = new EventObject("Continue call");
1112d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
1122d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private final SipProfile mLocalProfile;
1132d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private final String mPassword;
1142d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
1152d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private SipStack mSipStack;
1162d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private SipHelper mSipHelper;
1172d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
1182d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    // session that processes INVITE requests
1192d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private SipSessionImpl mCallReceiverSession;
1202d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private String mLocalIp;
1212d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
1224a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan    private SipWakeupTimer mWakeupTimer;
12328f63c06894b9ca9252f43bc54a098c0a785d4b4Hung-ying Tyan    private SipWakeLock mWakeLock;
12428f63c06894b9ca9252f43bc54a098c0a785d4b4Hung-ying Tyan
1252d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    // call-id-to-SipSession map
1262d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private Map<String, SipSessionImpl> mSessionMap =
1272d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            new HashMap<String, SipSessionImpl>();
1282d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
12999705b52ec952012bedc4aa8e1f62caff80a6a2fHung-ying Tyan    // external address observed from any response
13099705b52ec952012bedc4aa8e1f62caff80a6a2fHung-ying Tyan    private String mExternalIp;
13199705b52ec952012bedc4aa8e1f62caff80a6a2fHung-ying Tyan    private int mExternalPort;
13299705b52ec952012bedc4aa8e1f62caff80a6a2fHung-ying Tyan
1332d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    /**
1342dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh     * @param profile the local profile with password crossed out
1352d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang     * @param password the password of the profile
1362d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang     * @throws IOException if cannot assign requested address
1372d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang     */
1382dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh    public SipSessionGroup(SipProfile profile, String password,
1392dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh            SipWakeupTimer timer, SipWakeLock wakeLock) throws SipException {
1402dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh        mLocalProfile = profile;
1412d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        mPassword = password;
1424a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan        mWakeupTimer = timer;
14328f63c06894b9ca9252f43bc54a098c0a785d4b4Hung-ying Tyan        mWakeLock = wakeLock;
1442dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh        reset();
1452d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
1462d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
1474a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan    // TODO: remove this method once SipWakeupTimer can better handle variety
1484a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan    // of timeout values
1494a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan    void setWakeupTimer(SipWakeupTimer timer) {
1504a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan        mWakeupTimer = timer;
1514a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan    }
1524a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan
1532dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh    synchronized void reset() throws SipException {
1542d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        Properties properties = new Properties();
1552dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh
1562dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh        String protocol = mLocalProfile.getProtocol();
1572dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh        int port = mLocalProfile.getPort();
1582dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh        String server = mLocalProfile.getProxyAddress();
1592dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh
1602dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh        if (!TextUtils.isEmpty(server)) {
1612dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh            properties.setProperty("javax.sip.OUTBOUND_PROXY",
1622dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh                    server + ':' + port + '/' + protocol);
1632dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh        } else {
1642dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh            server = mLocalProfile.getSipDomain();
1652dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh        }
1662dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh        if (server.startsWith("[") && server.endsWith("]")) {
1672dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh            server = server.substring(1, server.length() - 1);
1682dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh        }
1692dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh
1702dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh        String local = null;
1712dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh        try {
1722dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh            for (InetAddress remote : InetAddress.getAllByName(server)) {
1732dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh                DatagramSocket socket = new DatagramSocket();
1742dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh                socket.connect(remote, port);
1752dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh                if (socket.isConnected()) {
1762dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh                    local = socket.getLocalAddress().getHostAddress();
1772dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh                    port = socket.getLocalPort();
1782dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh                    socket.close();
1792dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh                    break;
1802dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh                }
1812dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh                socket.close();
1822dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh            }
1832dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh        } catch (Exception e) {
1842dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh            // ignore.
1852dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh        }
1862dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh        if (local == null) {
1872dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh            // We are unable to reach the server. Just bail out.
1882dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh            return;
1892dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh        }
1902dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh
1912dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh        close();
1922dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh        mLocalIp = local;
1932dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh
1942d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        properties.setProperty("javax.sip.STACK_NAME", getStackName());
195b4116c09fb9784551911ea0a10b4dd321ff9aa7dChung-yih Wang        properties.setProperty(
196b4116c09fb9784551911ea0a10b4dd321ff9aa7dChung-yih Wang                "gov.nist.javax.sip.THREAD_POOL_SIZE", THREAD_POOL_SIZE);
1972dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh        mSipStack = SipFactory.getInstance().createSipStack(properties);
1982d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        try {
1992dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh            SipProvider provider = mSipStack.createSipProvider(
2002dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh                    mSipStack.createListeningPoint(local, port, protocol));
2012d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            provider.addSipListener(this);
2022dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh            mSipHelper = new SipHelper(mSipStack, provider);
2032dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh        } catch (SipException e) {
2042dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh            throw e;
2052dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh        } catch (Exception e) {
2062dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh            throw new SipException("failed to initialize SIP stack", e);
2072d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
20899705b52ec952012bedc4aa8e1f62caff80a6a2fHung-ying Tyan
2092dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh        Log.d(TAG, " start stack for " + mLocalProfile.getUriString());
2102dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh        mSipStack.start();
2112d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
2122d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
213d231aa880ab006d51ffe03454c1fc082f1c97bb8Hung-ying Tyan    synchronized void onConnectivityChanged() {
214ebc886c857a702d788c2bdfb9e0b3d20746ad745Hung-ying Tyan        SipSessionImpl[] ss = mSessionMap.values().toArray(
215ebc886c857a702d788c2bdfb9e0b3d20746ad745Hung-ying Tyan                    new SipSessionImpl[mSessionMap.size()]);
216ebc886c857a702d788c2bdfb9e0b3d20746ad745Hung-ying Tyan        // Iterate on the copied array instead of directly on mSessionMap to
217ebc886c857a702d788c2bdfb9e0b3d20746ad745Hung-ying Tyan        // avoid ConcurrentModificationException being thrown when
218ebc886c857a702d788c2bdfb9e0b3d20746ad745Hung-ying Tyan        // SipSessionImpl removes itself from mSessionMap in onError() in the
219ebc886c857a702d788c2bdfb9e0b3d20746ad745Hung-ying Tyan        // following loop.
220ebc886c857a702d788c2bdfb9e0b3d20746ad745Hung-ying Tyan        for (SipSessionImpl s : ss) {
221d231aa880ab006d51ffe03454c1fc082f1c97bb8Hung-ying Tyan            s.onError(SipErrorCode.DATA_CONNECTION_LOST,
222d231aa880ab006d51ffe03454c1fc082f1c97bb8Hung-ying Tyan                    "data connection lost");
223d231aa880ab006d51ffe03454c1fc082f1c97bb8Hung-ying Tyan        }
224d231aa880ab006d51ffe03454c1fc082f1c97bb8Hung-ying Tyan    }
225d231aa880ab006d51ffe03454c1fc082f1c97bb8Hung-ying Tyan
22699705b52ec952012bedc4aa8e1f62caff80a6a2fHung-ying Tyan    synchronized void resetExternalAddress() {
227cb6ee06f62c20ae036a206667097f20b837b11abChia-chi Yeh        if (DEBUG) {
228cb6ee06f62c20ae036a206667097f20b837b11abChia-chi Yeh            Log.d(TAG, " reset external addr on " + mSipStack);
229cb6ee06f62c20ae036a206667097f20b837b11abChia-chi Yeh        }
23099705b52ec952012bedc4aa8e1f62caff80a6a2fHung-ying Tyan        mExternalIp = null;
23199705b52ec952012bedc4aa8e1f62caff80a6a2fHung-ying Tyan        mExternalPort = 0;
23299705b52ec952012bedc4aa8e1f62caff80a6a2fHung-ying Tyan    }
23399705b52ec952012bedc4aa8e1f62caff80a6a2fHung-ying Tyan
2342d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    public SipProfile getLocalProfile() {
2352d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        return mLocalProfile;
2362d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
2372d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
2382d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    public String getLocalProfileUri() {
2392d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        return mLocalProfile.getUriString();
2402d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
2412d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
2422d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private String getStackName() {
2432d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        return "stack" + System.currentTimeMillis();
2442d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
2452d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
2462d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    public synchronized void close() {
2472d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        Log.d(TAG, " close stack for " + mLocalProfile.getUriString());
2485d0c5cf2d6c6e82bcdce95d72d9000a934b2f354Hung-ying Tyan        onConnectivityChanged();
2492d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        mSessionMap.clear();
2502d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        closeToNotReceiveCalls();
2512d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        if (mSipStack != null) {
2522d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            mSipStack.stop();
2532d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            mSipStack = null;
2542d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            mSipHelper = null;
2552d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
2562dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh        resetExternalAddress();
2572d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
2582d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
2592d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    public synchronized boolean isClosed() {
2602d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        return (mSipStack == null);
2612d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
2622d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
2632d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    // For internal use, require listener not to block in callbacks.
2642d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    public synchronized void openToReceiveCalls(ISipSessionListener listener) {
2652d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        if (mCallReceiverSession == null) {
2662d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            mCallReceiverSession = new SipSessionCallReceiverImpl(listener);
2672d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        } else {
2682d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            mCallReceiverSession.setListener(listener);
2692d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
2702d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
2712d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
2722d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    public synchronized void closeToNotReceiveCalls() {
2732d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        mCallReceiverSession = null;
2742d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
2752d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
2762d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    public ISipSession createSession(ISipSessionListener listener) {
2772d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        return (isClosed() ? null : new SipSessionImpl(listener));
2782d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
2792d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
2800a6e717fb6846f66b8dc853e079f2166307bfc60Hung-ying Tyan    synchronized boolean containsSession(String callId) {
2810a6e717fb6846f66b8dc853e079f2166307bfc60Hung-ying Tyan        return mSessionMap.containsKey(callId);
2820a6e717fb6846f66b8dc853e079f2166307bfc60Hung-ying Tyan    }
2830a6e717fb6846f66b8dc853e079f2166307bfc60Hung-ying Tyan
2842d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private synchronized SipSessionImpl getSipSession(EventObject event) {
2852d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        String key = SipHelper.getCallId(event);
2862d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        SipSessionImpl session = mSessionMap.get(key);
287c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan        if ((session != null) && isLoggable(session)) {
288c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan            Log.d(TAG, "session key from event: " + key);
289c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan            Log.d(TAG, "active sessions:");
290c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan            for (String k : mSessionMap.keySet()) {
291c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan                Log.d(TAG, " ..." + k + ": " + mSessionMap.get(k));
292c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan            }
293c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan        }
2942d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        return ((session != null) ? session : mCallReceiverSession);
2952d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
2962d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
2972d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private synchronized void addSipSession(SipSessionImpl newSession) {
2982d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        removeSipSession(newSession);
2992d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        String key = newSession.getCallId();
3002d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        mSessionMap.put(key, newSession);
301c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan        if (isLoggable(newSession)) {
302c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan            Log.d(TAG, "+++  add a session with key:  '" + key + "'");
303c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan            for (String k : mSessionMap.keySet()) {
304c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan                Log.d(TAG, "  " + k + ": " + mSessionMap.get(k));
305c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan            }
3062d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
3072d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
3082d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
3092d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private synchronized void removeSipSession(SipSessionImpl session) {
3102d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        if (session == mCallReceiverSession) return;
3112d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        String key = session.getCallId();
3122d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        SipSessionImpl s = mSessionMap.remove(key);
3132d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        // sanity check
3142d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        if ((s != null) && (s != session)) {
3152d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            Log.w(TAG, "session " + session + " is not associated with key '"
3162d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    + key + "'");
3172d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            mSessionMap.put(key, s);
3182d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            for (Map.Entry<String, SipSessionImpl> entry
3192d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    : mSessionMap.entrySet()) {
3202d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                if (entry.getValue() == s) {
3212d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    key = entry.getKey();
3222d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    mSessionMap.remove(key);
3232d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                }
3242d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
3252d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
3262d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
327c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan        if ((s != null) && isLoggable(s)) {
328c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan            Log.d(TAG, "remove session " + session + " @key '" + key + "'");
329c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan            for (String k : mSessionMap.keySet()) {
330c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan                Log.d(TAG, "  " + k + ": " + mSessionMap.get(k));
331c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan            }
3322d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
3332d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
3342d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
33528f63c06894b9ca9252f43bc54a098c0a785d4b4Hung-ying Tyan    public void processRequest(final RequestEvent event) {
33628f63c06894b9ca9252f43bc54a098c0a785d4b4Hung-ying Tyan        if (isRequestEvent(Request.INVITE, event)) {
33728f63c06894b9ca9252f43bc54a098c0a785d4b4Hung-ying Tyan            if (DEBUG) Log.d(TAG, "<<<<< got INVITE, thread:"
33828f63c06894b9ca9252f43bc54a098c0a785d4b4Hung-ying Tyan                    + Thread.currentThread());
33928f63c06894b9ca9252f43bc54a098c0a785d4b4Hung-ying Tyan            // Acquire a wake lock and keep it for WAKE_LOCK_HOLDING_TIME;
34028f63c06894b9ca9252f43bc54a098c0a785d4b4Hung-ying Tyan            // should be large enough to bring up the app.
34128f63c06894b9ca9252f43bc54a098c0a785d4b4Hung-ying Tyan            mWakeLock.acquire(WAKE_LOCK_HOLDING_TIME);
34228f63c06894b9ca9252f43bc54a098c0a785d4b4Hung-ying Tyan        }
3432d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        process(event);
3442d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
3452d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
3462d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    public void processResponse(ResponseEvent event) {
3472d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        process(event);
3482d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
3492d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
3502d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    public void processIOException(IOExceptionEvent event) {
3512d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        process(event);
3522d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
3532d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
3542d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    public void processTimeout(TimeoutEvent event) {
3552d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        process(event);
3562d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
3572d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
3582d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    public void processTransactionTerminated(TransactionTerminatedEvent event) {
3592d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        process(event);
3602d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
3612d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
3622d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    public void processDialogTerminated(DialogTerminatedEvent event) {
3632d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        process(event);
3642d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
3652d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
3662d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private synchronized void process(EventObject event) {
3672d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        SipSessionImpl session = getSipSession(event);
3682d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        try {
369c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan            boolean isLoggable = isLoggable(session, event);
370c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan            boolean processed = (session != null) && session.process(event);
371c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan            if (isLoggable && processed) {
37297963794af1e18674dd111e3ad344d90b16c922cHung-ying Tyan                Log.d(TAG, "new state after: "
37384a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                        + SipSession.State.toString(session.mState));
3742d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
3752d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        } catch (Throwable e) {
376cb6ee06f62c20ae036a206667097f20b837b11abChia-chi Yeh            Log.w(TAG, "event process error: " + event, getRootCause(e));
3772d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            session.onError(e);
3782d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
3792d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
3802d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
38195b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh    private String extractContent(Message message) {
38295b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh        // Currently we do not support secure MIME bodies.
38395b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh        byte[] bytes = message.getRawContent();
38495b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh        if (bytes != null) {
38595b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh            try {
38695b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh                if (message instanceof SIPMessage) {
38795b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh                    return ((SIPMessage) message).getMessageContent();
38895b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh                } else {
38995b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh                    return new String(bytes, "UTF-8");
39095b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh                }
39195b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh            } catch (UnsupportedEncodingException e) {
39295b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh            }
39395b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh        }
39495b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh        return null;
39595b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh    }
39695b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh
39799705b52ec952012bedc4aa8e1f62caff80a6a2fHung-ying Tyan    private void extractExternalAddress(ResponseEvent evt) {
39899705b52ec952012bedc4aa8e1f62caff80a6a2fHung-ying Tyan        Response response = evt.getResponse();
39999705b52ec952012bedc4aa8e1f62caff80a6a2fHung-ying Tyan        ViaHeader viaHeader = (ViaHeader)(response.getHeader(
40099705b52ec952012bedc4aa8e1f62caff80a6a2fHung-ying Tyan                SIPHeaderNames.VIA));
40199705b52ec952012bedc4aa8e1f62caff80a6a2fHung-ying Tyan        if (viaHeader == null) return;
40299705b52ec952012bedc4aa8e1f62caff80a6a2fHung-ying Tyan        int rport = viaHeader.getRPort();
40399705b52ec952012bedc4aa8e1f62caff80a6a2fHung-ying Tyan        String externalIp = viaHeader.getReceived();
40499705b52ec952012bedc4aa8e1f62caff80a6a2fHung-ying Tyan        if ((rport > 0) && (externalIp != null)) {
40599705b52ec952012bedc4aa8e1f62caff80a6a2fHung-ying Tyan            mExternalIp = externalIp;
40699705b52ec952012bedc4aa8e1f62caff80a6a2fHung-ying Tyan            mExternalPort = rport;
407cb6ee06f62c20ae036a206667097f20b837b11abChia-chi Yeh            if (DEBUG) {
408cb6ee06f62c20ae036a206667097f20b837b11abChia-chi Yeh                Log.d(TAG, " got external addr " + externalIp + ":" + rport
409cb6ee06f62c20ae036a206667097f20b837b11abChia-chi Yeh                        + " on " + mSipStack);
410cb6ee06f62c20ae036a206667097f20b837b11abChia-chi Yeh            }
411cb6ee06f62c20ae036a206667097f20b837b11abChia-chi Yeh        }
412cb6ee06f62c20ae036a206667097f20b837b11abChia-chi Yeh    }
413cb6ee06f62c20ae036a206667097f20b837b11abChia-chi Yeh
414cb6ee06f62c20ae036a206667097f20b837b11abChia-chi Yeh    private Throwable getRootCause(Throwable exception) {
415cb6ee06f62c20ae036a206667097f20b837b11abChia-chi Yeh        Throwable cause = exception.getCause();
416cb6ee06f62c20ae036a206667097f20b837b11abChia-chi Yeh        while (cause != null) {
417cb6ee06f62c20ae036a206667097f20b837b11abChia-chi Yeh            exception = cause;
418cb6ee06f62c20ae036a206667097f20b837b11abChia-chi Yeh            cause = exception.getCause();
41999705b52ec952012bedc4aa8e1f62caff80a6a2fHung-ying Tyan        }
420cb6ee06f62c20ae036a206667097f20b837b11abChia-chi Yeh        return exception;
42199705b52ec952012bedc4aa8e1f62caff80a6a2fHung-ying Tyan    }
42299705b52ec952012bedc4aa8e1f62caff80a6a2fHung-ying Tyan
423307f15faafa5a38d9b3b314df22778cd11685d7brepo sync    private SipSessionImpl createNewSession(RequestEvent event,
424307f15faafa5a38d9b3b314df22778cd11685d7brepo sync            ISipSessionListener listener, ServerTransaction transaction,
425307f15faafa5a38d9b3b314df22778cd11685d7brepo sync            int newState) throws SipException {
426307f15faafa5a38d9b3b314df22778cd11685d7brepo sync        SipSessionImpl newSession = new SipSessionImpl(listener);
427307f15faafa5a38d9b3b314df22778cd11685d7brepo sync        newSession.mServerTransaction = transaction;
428307f15faafa5a38d9b3b314df22778cd11685d7brepo sync        newSession.mState = newState;
429307f15faafa5a38d9b3b314df22778cd11685d7brepo sync        newSession.mDialog = newSession.mServerTransaction.getDialog();
430307f15faafa5a38d9b3b314df22778cd11685d7brepo sync        newSession.mInviteReceived = event;
431307f15faafa5a38d9b3b314df22778cd11685d7brepo sync        newSession.mPeerProfile = createPeerProfile((HeaderAddress)
432307f15faafa5a38d9b3b314df22778cd11685d7brepo sync                event.getRequest().getHeader(FromHeader.NAME));
433307f15faafa5a38d9b3b314df22778cd11685d7brepo sync        newSession.mPeerSessionDescription =
434307f15faafa5a38d9b3b314df22778cd11685d7brepo sync                extractContent(event.getRequest());
435307f15faafa5a38d9b3b314df22778cd11685d7brepo sync        return newSession;
436307f15faafa5a38d9b3b314df22778cd11685d7brepo sync    }
437307f15faafa5a38d9b3b314df22778cd11685d7brepo sync
4382d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private class SipSessionCallReceiverImpl extends SipSessionImpl {
4392d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public SipSessionCallReceiverImpl(ISipSessionListener listener) {
4402d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            super(listener);
4412d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
4422d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
4431aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync        private int processInviteWithReplaces(RequestEvent event,
4441aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync                ReplacesHeader replaces) {
4451aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync            String callId = replaces.getCallId();
4461aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync            SipSessionImpl session = mSessionMap.get(callId);
4471aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync            if (session == null) {
4481aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync                return Response.CALL_OR_TRANSACTION_DOES_NOT_EXIST;
4491aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync            }
4501aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync
4511aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync            Dialog dialog = session.mDialog;
4521aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync            if (dialog == null) return Response.DECLINE;
4531aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync
4541aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync            if (!dialog.getLocalTag().equals(replaces.getToTag()) ||
4551aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync                    !dialog.getRemoteTag().equals(replaces.getFromTag())) {
4561aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync                // No match is found, returns 481.
4571aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync                return Response.CALL_OR_TRANSACTION_DOES_NOT_EXIST;
4581aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync            }
4591aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync
4601aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync            ReferredByHeader referredBy = (ReferredByHeader) event.getRequest()
4611aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync                    .getHeader(ReferredByHeader.NAME);
4621aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync            if ((referredBy == null) ||
4631aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync                    !dialog.getRemoteParty().equals(referredBy.getAddress())) {
4641aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync                return Response.CALL_OR_TRANSACTION_DOES_NOT_EXIST;
4651aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync            }
4661aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync            return Response.OK;
4671aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync        }
4681aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync
4691aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync        private void processNewInviteRequest(RequestEvent event)
4701aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync                throws SipException {
4711aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync            ReplacesHeader replaces = (ReplacesHeader) event.getRequest()
4721aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync                    .getHeader(ReplacesHeader.NAME);
4731aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync            SipSessionImpl newSession = null;
4741aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync            if (replaces != null) {
4751aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync                int response = processInviteWithReplaces(event, replaces);
4761aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync                if (DEBUG) {
4771aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync                    Log.v(TAG, "ReplacesHeader: " + replaces
4781aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync                            + " response=" + response);
4791aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync                }
4801aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync                if (response == Response.OK) {
4811aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync                    SipSessionImpl replacedSession =
4821aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync                            mSessionMap.get(replaces.getCallId());
4831aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync                    // got INVITE w/ replaces request.
4841aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync                    newSession = createNewSession(event,
4851aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync                            replacedSession.mProxy.getListener(),
486307f15faafa5a38d9b3b314df22778cd11685d7brepo sync                            mSipHelper.getServerTransaction(event),
487307f15faafa5a38d9b3b314df22778cd11685d7brepo sync                            SipSession.State.INCOMING_CALL);
4881aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync                    newSession.mProxy.onCallTransferring(newSession,
4891aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync                            newSession.mPeerSessionDescription);
4901aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync                } else {
4911aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync                    mSipHelper.sendResponse(event, response);
4921aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync                }
4931aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync            } else {
4941aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync                // New Incoming call.
4951aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync                newSession = createNewSession(event, mProxy,
496307f15faafa5a38d9b3b314df22778cd11685d7brepo sync                        mSipHelper.sendRinging(event, generateTag()),
497307f15faafa5a38d9b3b314df22778cd11685d7brepo sync                        SipSession.State.INCOMING_CALL);
4981aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync                mProxy.onRinging(newSession, newSession.mPeerProfile,
4991aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync                        newSession.mPeerSessionDescription);
5001aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync            }
5011aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync            if (newSession != null) addSipSession(newSession);
5021aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync        }
5031aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync
5042d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public boolean process(EventObject evt) throws SipException {
505c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan            if (isLoggable(this, evt)) Log.d(TAG, " ~~~~~   " + this + ": "
50684a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                    + SipSession.State.toString(mState) + ": processing "
50797963794af1e18674dd111e3ad344d90b16c922cHung-ying Tyan                    + log(evt));
5082d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            if (isRequestEvent(Request.INVITE, evt)) {
5091aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync                processNewInviteRequest((RequestEvent) evt);
5102d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return true;
5110b4d2fb11405e2e785ec30cabe7bb311c654c0d2Chung-yih Wang            } else if (isRequestEvent(Request.OPTIONS, evt)) {
5120b4d2fb11405e2e785ec30cabe7bb311c654c0d2Chung-yih Wang                mSipHelper.sendResponse((RequestEvent) evt, Response.OK);
5130b4d2fb11405e2e785ec30cabe7bb311c654c0d2Chung-yih Wang                return true;
5142d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            } else {
5152d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return false;
5162d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
5172d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
5182d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
5192d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
5204a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan    static interface KeepAliveProcessCallback {
5214a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan        /** Invoked when the response of keeping alive comes back. */
5224a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan        void onResponse(boolean portChanged);
5234a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan        void onError(int errorCode, String description);
5244a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan    }
5254a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan
5262d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    class SipSessionImpl extends ISipSession.Stub {
5272d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        SipProfile mPeerProfile;
5282d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        SipSessionListenerProxy mProxy = new SipSessionListenerProxy();
52984a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan        int mState = SipSession.State.READY_TO_CALL;
5302d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        RequestEvent mInviteReceived;
5312d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        Dialog mDialog;
5322d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        ServerTransaction mServerTransaction;
5332d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        ClientTransaction mClientTransaction;
53495b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh        String mPeerSessionDescription;
5352d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        boolean mInCall;
5364a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan        SessionTimer mSessionTimer;
537ee8a884f3504c981be8a1d6888b4590a0a394e05Hung-ying Tyan        int mAuthenticationRetryCount;
538ee8a884f3504c981be8a1d6888b4590a0a394e05Hung-ying Tyan
5394a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan        private KeepAliveProcess mKeepAliveProcess;
5409352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan
541233718c3c5a4f5b4f564af93cb2e42d80a900904Hung-ying Tyan        private SipSessionImpl mKeepAliveSession;
542233718c3c5a4f5b4f564af93cb2e42d80a900904Hung-ying Tyan
543307f15faafa5a38d9b3b314df22778cd11685d7brepo sync        // the following three members are used for handling refer request.
544307f15faafa5a38d9b3b314df22778cd11685d7brepo sync        SipSessionImpl mReferSession;
545307f15faafa5a38d9b3b314df22778cd11685d7brepo sync        ReferredByHeader mReferredBy;
546307f15faafa5a38d9b3b314df22778cd11685d7brepo sync        String mReplaces;
547307f15faafa5a38d9b3b314df22778cd11685d7brepo sync
5489352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan        // lightweight timer
5499352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan        class SessionTimer {
5509352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan            private boolean mRunning = true;
5519352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan
5529352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan            void start(final int timeout) {
5539352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                new Thread(new Runnable() {
5549352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                    public void run() {
5559352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                        sleep(timeout);
5569352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                        if (mRunning) timeout();
5579352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                    }
55884a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                }, "SipSessionTimerThread").start();
5599352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan            }
5609352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan
5619352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan            synchronized void cancel() {
5629352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                mRunning = false;
5639352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                this.notify();
5649352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan            }
5659352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan
5669352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan            private void timeout() {
5679352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                synchronized (SipSessionGroup.this) {
5689352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                    onError(SipErrorCode.TIME_OUT, "Session timed out!");
5699352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                }
5709352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan            }
5719352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan
5729352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan            private synchronized void sleep(int timeout) {
5739352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                try {
5749352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                    this.wait(timeout * 1000);
5759352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                } catch (InterruptedException e) {
5769352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                    Log.e(TAG, "session timer interrupted!");
5779352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                }
5789352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan            }
5799352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan        }
5802d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
5812d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public SipSessionImpl(ISipSessionListener listener) {
5822d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            setListener(listener);
5832d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
5842d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
5852d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        SipSessionImpl duplicate() {
5862d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return new SipSessionImpl(mProxy.getListener());
5872d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
5882d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
5892d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private void reset() {
5902d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            mInCall = false;
5912d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            removeSipSession(this);
5922d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            mPeerProfile = null;
59384a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan            mState = SipSession.State.READY_TO_CALL;
5942d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            mInviteReceived = null;
5959ea96c6cade1f25d4d77dcbd24854df431548b36Hung-ying Tyan            mPeerSessionDescription = null;
596ee8a884f3504c981be8a1d6888b4590a0a394e05Hung-ying Tyan            mAuthenticationRetryCount = 0;
597307f15faafa5a38d9b3b314df22778cd11685d7brepo sync            mReferSession = null;
598307f15faafa5a38d9b3b314df22778cd11685d7brepo sync            mReferredBy = null;
599307f15faafa5a38d9b3b314df22778cd11685d7brepo sync            mReplaces = null;
6009ea96c6cade1f25d4d77dcbd24854df431548b36Hung-ying Tyan
6019ea96c6cade1f25d4d77dcbd24854df431548b36Hung-ying Tyan            if (mDialog != null) mDialog.delete();
6022d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            mDialog = null;
6039ea96c6cade1f25d4d77dcbd24854df431548b36Hung-ying Tyan
6049ea96c6cade1f25d4d77dcbd24854df431548b36Hung-ying Tyan            try {
6059ea96c6cade1f25d4d77dcbd24854df431548b36Hung-ying Tyan                if (mServerTransaction != null) mServerTransaction.terminate();
6069ea96c6cade1f25d4d77dcbd24854df431548b36Hung-ying Tyan            } catch (ObjectInUseException e) {
6079ea96c6cade1f25d4d77dcbd24854df431548b36Hung-ying Tyan                // ignored
6089ea96c6cade1f25d4d77dcbd24854df431548b36Hung-ying Tyan            }
6092d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            mServerTransaction = null;
6109ea96c6cade1f25d4d77dcbd24854df431548b36Hung-ying Tyan
6119ea96c6cade1f25d4d77dcbd24854df431548b36Hung-ying Tyan            try {
6129ea96c6cade1f25d4d77dcbd24854df431548b36Hung-ying Tyan                if (mClientTransaction != null) mClientTransaction.terminate();
6139ea96c6cade1f25d4d77dcbd24854df431548b36Hung-ying Tyan            } catch (ObjectInUseException e) {
6149ea96c6cade1f25d4d77dcbd24854df431548b36Hung-ying Tyan                // ignored
6159ea96c6cade1f25d4d77dcbd24854df431548b36Hung-ying Tyan            }
6162d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            mClientTransaction = null;
6179352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan
6189352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan            cancelSessionTimer();
619233718c3c5a4f5b4f564af93cb2e42d80a900904Hung-ying Tyan
620233718c3c5a4f5b4f564af93cb2e42d80a900904Hung-ying Tyan            if (mKeepAliveSession != null) {
621233718c3c5a4f5b4f564af93cb2e42d80a900904Hung-ying Tyan                mKeepAliveSession.stopKeepAliveProcess();
622233718c3c5a4f5b4f564af93cb2e42d80a900904Hung-ying Tyan                mKeepAliveSession = null;
623233718c3c5a4f5b4f564af93cb2e42d80a900904Hung-ying Tyan            }
6242d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
6252d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
6262d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public boolean isInCall() {
6272d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return mInCall;
6282d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
6292d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
6302d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public String getLocalIp() {
6312d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return mLocalIp;
6322d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
6332d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
6342d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public SipProfile getLocalProfile() {
6352d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return mLocalProfile;
6362d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
6372d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
6382d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public SipProfile getPeerProfile() {
6392d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return mPeerProfile;
6402d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
6412d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
6422d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public String getCallId() {
6432d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return SipHelper.getCallId(getTransaction());
6442d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
6452d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
6462d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private Transaction getTransaction() {
6472d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            if (mClientTransaction != null) return mClientTransaction;
6482d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            if (mServerTransaction != null) return mServerTransaction;
6492d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return null;
6502d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
6512d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
65297963794af1e18674dd111e3ad344d90b16c922cHung-ying Tyan        public int getState() {
65397963794af1e18674dd111e3ad344d90b16c922cHung-ying Tyan            return mState;
6542d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
6552d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
6562d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public void setListener(ISipSessionListener listener) {
6572d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            mProxy.setListener((listener instanceof SipSessionListenerProxy)
6582d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    ? ((SipSessionListenerProxy) listener).getListener()
6592d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    : listener);
6602d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
6612d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
662dba514c6d8e8263d4b8f31cb2fdebfc1d4f84c35Hung-ying Tyan        // process the command in a new thread
663dba514c6d8e8263d4b8f31cb2fdebfc1d4f84c35Hung-ying Tyan        private void doCommandAsync(final EventObject command) {
664dba514c6d8e8263d4b8f31cb2fdebfc1d4f84c35Hung-ying Tyan            new Thread(new Runnable() {
665dba514c6d8e8263d4b8f31cb2fdebfc1d4f84c35Hung-ying Tyan                    public void run() {
666dba514c6d8e8263d4b8f31cb2fdebfc1d4f84c35Hung-ying Tyan                        try {
667dba514c6d8e8263d4b8f31cb2fdebfc1d4f84c35Hung-ying Tyan                            processCommand(command);
668c6548fd9eda7b58f5a2e2a9c01e3c7cafd42fafbHung-ying Tyan                        } catch (Throwable e) {
6694a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                            Log.w(TAG, "command error: " + command + ": "
6704a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                                    + mLocalProfile.getUriString(),
6714a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                                    getRootCause(e));
6723d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                            onError(e);
673dba514c6d8e8263d4b8f31cb2fdebfc1d4f84c35Hung-ying Tyan                        }
674dba514c6d8e8263d4b8f31cb2fdebfc1d4f84c35Hung-ying Tyan                    }
67584a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan            }, "SipSessionAsyncCmdThread").start();
676dba514c6d8e8263d4b8f31cb2fdebfc1d4f84c35Hung-ying Tyan        }
677dba514c6d8e8263d4b8f31cb2fdebfc1d4f84c35Hung-ying Tyan
6789352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan        public void makeCall(SipProfile peerProfile, String sessionDescription,
6799352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                int timeout) {
6809352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan            doCommandAsync(new MakeCallCommand(peerProfile, sessionDescription,
6819352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                    timeout));
6822d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
6832d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
6849352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan        public void answerCall(String sessionDescription, int timeout) {
68506e8cdc0f81ead604d5adf9d7b3f982e10226fd2Hung-ying Tyan            synchronized (SipSessionGroup.this) {
68606e8cdc0f81ead604d5adf9d7b3f982e10226fd2Hung-ying Tyan                if (mPeerProfile == null) return;
687c133781723f64d1321685d02ad6a208286bf0a42repo sync                doCommandAsync(new MakeCallCommand(mPeerProfile,
688c133781723f64d1321685d02ad6a208286bf0a42repo sync                        sessionDescription, timeout));
6892d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
6902d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
6912d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
6922d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public void endCall() {
693dba514c6d8e8263d4b8f31cb2fdebfc1d4f84c35Hung-ying Tyan            doCommandAsync(END_CALL);
6942d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
6952d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
6969352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan        public void changeCall(String sessionDescription, int timeout) {
69706e8cdc0f81ead604d5adf9d7b3f982e10226fd2Hung-ying Tyan            synchronized (SipSessionGroup.this) {
69806e8cdc0f81ead604d5adf9d7b3f982e10226fd2Hung-ying Tyan                if (mPeerProfile == null) return;
69906e8cdc0f81ead604d5adf9d7b3f982e10226fd2Hung-ying Tyan                doCommandAsync(new MakeCallCommand(mPeerProfile,
70006e8cdc0f81ead604d5adf9d7b3f982e10226fd2Hung-ying Tyan                        sessionDescription, timeout));
70106e8cdc0f81ead604d5adf9d7b3f982e10226fd2Hung-ying Tyan            }
7022d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
7032d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
7042d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public void register(int duration) {
705dba514c6d8e8263d4b8f31cb2fdebfc1d4f84c35Hung-ying Tyan            doCommandAsync(new RegisterCommand(duration));
7062d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
7072d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
7082d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public void unregister() {
709dba514c6d8e8263d4b8f31cb2fdebfc1d4f84c35Hung-ying Tyan            doCommandAsync(DEREGISTER);
7102d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
7112d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
7122d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private void processCommand(EventObject command) throws SipException {
7130a6e717fb6846f66b8dc853e079f2166307bfc60Hung-ying Tyan            if (isLoggable(command)) Log.d(TAG, "process cmd: " + command);
7142d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            if (!process(command)) {
7153d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                onError(SipErrorCode.IN_PROGRESS,
7163d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                        "cannot initiate a new transaction to execute: "
7173d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                        + command);
7182d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
7192d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
7202d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
7212d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        protected String generateTag() {
7222d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            // 32-bit randomness
7232d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return String.valueOf((long) (Math.random() * 0x100000000L));
7242d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
7252d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
7262d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public String toString() {
7272d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            try {
7282d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                String s = super.toString();
72997963794af1e18674dd111e3ad344d90b16c922cHung-ying Tyan                return s.substring(s.indexOf("@")) + ":"
73084a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                        + SipSession.State.toString(mState);
7312d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            } catch (Throwable e) {
7322d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return super.toString();
7332d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
7342d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
7352d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
7362d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public boolean process(EventObject evt) throws SipException {
737c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan            if (isLoggable(this, evt)) Log.d(TAG, " ~~~~~   " + this + ": "
73884a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                    + SipSession.State.toString(mState) + ": processing "
73997963794af1e18674dd111e3ad344d90b16c922cHung-ying Tyan                    + log(evt));
7402d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            synchronized (SipSessionGroup.this) {
7412d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                if (isClosed()) return false;
7422d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
7434a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                if (mKeepAliveProcess != null) {
7444a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                    // event consumed by keepalive process
7454a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                    if (mKeepAliveProcess.process(evt)) return true;
7464a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                }
7474a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan
7482d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                Dialog dialog = null;
7492d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                if (evt instanceof RequestEvent) {
7502d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    dialog = ((RequestEvent) evt).getDialog();
7512d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                } else if (evt instanceof ResponseEvent) {
7522d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    dialog = ((ResponseEvent) evt).getDialog();
75399705b52ec952012bedc4aa8e1f62caff80a6a2fHung-ying Tyan                    extractExternalAddress((ResponseEvent) evt);
7542d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                }
7552d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                if (dialog != null) mDialog = dialog;
7562d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
7572d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                boolean processed;
7582d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
7592d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                switch (mState) {
76084a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                case SipSession.State.REGISTERING:
76184a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                case SipSession.State.DEREGISTERING:
7622d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    processed = registeringToReady(evt);
7632d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    break;
76484a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                case SipSession.State.READY_TO_CALL:
7652d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    processed = readyForCall(evt);
7662d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    break;
76784a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                case SipSession.State.INCOMING_CALL:
7682d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    processed = incomingCall(evt);
7692d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    break;
77084a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                case SipSession.State.INCOMING_CALL_ANSWERING:
7712d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    processed = incomingCallToInCall(evt);
7722d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    break;
77384a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                case SipSession.State.OUTGOING_CALL:
77484a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                case SipSession.State.OUTGOING_CALL_RING_BACK:
7752d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    processed = outgoingCall(evt);
7762d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    break;
77784a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                case SipSession.State.OUTGOING_CALL_CANCELING:
7782d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    processed = outgoingCallToReady(evt);
7792d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    break;
78084a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                case SipSession.State.IN_CALL:
7812d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    processed = inCall(evt);
7822d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    break;
783dc5bbe965f7a66238c3f9a6694f4410b3d52af3bHung-ying Tyan                case SipSession.State.ENDING_CALL:
784dc5bbe965f7a66238c3f9a6694f4410b3d52af3bHung-ying Tyan                    processed = endingCall(evt);
785dc5bbe965f7a66238c3f9a6694f4410b3d52af3bHung-ying Tyan                    break;
7862d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                default:
7872d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    processed = false;
7882d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                }
7892d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return (processed || processExceptions(evt));
7902d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
7912d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
7922d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
7932d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private boolean processExceptions(EventObject evt) throws SipException {
7942d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            if (isRequestEvent(Request.BYE, evt)) {
7952d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                // terminate the call whenever a BYE is received
7962d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mSipHelper.sendResponse((RequestEvent) evt, Response.OK);
7972d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                endCallNormally();
7982d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return true;
7992d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            } else if (isRequestEvent(Request.CANCEL, evt)) {
8002d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mSipHelper.sendResponse((RequestEvent) evt,
8012d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        Response.CALL_OR_TRANSACTION_DOES_NOT_EXIST);
8022d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return true;
8032d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            } else if (evt instanceof TransactionTerminatedEvent) {
804025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan                if (isCurrentTransaction((TransactionTerminatedEvent) evt)) {
805025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan                    if (evt instanceof TimeoutEvent) {
806025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan                        processTimeout((TimeoutEvent) evt);
807025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan                    } else {
808025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan                        processTransactionTerminated(
809025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan                                (TransactionTerminatedEvent) evt);
810025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan                    }
811025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan                    return true;
8122d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                }
8130b4d2fb11405e2e785ec30cabe7bb311c654c0d2Chung-yih Wang            } else if (isRequestEvent(Request.OPTIONS, evt)) {
8140b4d2fb11405e2e785ec30cabe7bb311c654c0d2Chung-yih Wang                mSipHelper.sendResponse((RequestEvent) evt, Response.OK);
8150b4d2fb11405e2e785ec30cabe7bb311c654c0d2Chung-yih Wang                return true;
8162d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            } else if (evt instanceof DialogTerminatedEvent) {
8172d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                processDialogTerminated((DialogTerminatedEvent) evt);
8182d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return true;
8192d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
8202d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return false;
8212d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
8222d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
8232d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private void processDialogTerminated(DialogTerminatedEvent event) {
8242d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            if (mDialog == event.getDialog()) {
8252d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                onError(new SipException("dialog terminated"));
8262d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            } else {
8272d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                Log.d(TAG, "not the current dialog; current=" + mDialog
8282d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        + ", terminated=" + event.getDialog());
8292d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
8302d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
8312d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
832025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan        private boolean isCurrentTransaction(TransactionTerminatedEvent event) {
833025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan            Transaction current = event.isServerTransaction()
834025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan                    ? mServerTransaction
835025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan                    : mClientTransaction;
836025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan            Transaction target = event.isServerTransaction()
837025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan                    ? event.getServerTransaction()
838025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan                    : event.getClientTransaction();
839025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan
840025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan            if ((current != target) && (mState != SipSession.State.PINGING)) {
841025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan                Log.d(TAG, "not the current transaction; current="
842025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan                        + toString(current) + ", target=" + toString(target));
843025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan                return false;
844025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan            } else if (current != null) {
845025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan                Log.d(TAG, "transaction terminated: " + toString(current));
846025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan                return true;
847fccd5bc78f94b7dcfbcf78ddca83719c9cd1a74fHung-ying Tyan            } else {
848fccd5bc78f94b7dcfbcf78ddca83719c9cd1a74fHung-ying Tyan                // no transaction; shouldn't be here; ignored
849fccd5bc78f94b7dcfbcf78ddca83719c9cd1a74fHung-ying Tyan                return true;
850025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan            }
851025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan        }
852025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan
853025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan        private String toString(Transaction transaction) {
854025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan            if (transaction == null) return "null";
855025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan            Request request = transaction.getRequest();
856025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan            Dialog dialog = transaction.getDialog();
857025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan            CSeqHeader cseq = (CSeqHeader) request.getHeader(CSeqHeader.NAME);
858025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan            return String.format("req=%s,%s,s=%s,ds=%s,", request.getMethod(),
859025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan                    cseq.getSeqNumber(), transaction.getState(),
860025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan                    ((dialog == null) ? "-" : dialog.getState()));
861025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan        }
862025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan
8633d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan        private void processTransactionTerminated(
8643d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                TransactionTerminatedEvent event) {
8653d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan            switch (mState) {
86684a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                case SipSession.State.IN_CALL:
86784a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                case SipSession.State.READY_TO_CALL:
8683d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                    Log.d(TAG, "Transaction terminated; do nothing");
8693d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                    break;
8703d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                default:
8713d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                    Log.d(TAG, "Transaction terminated early: " + this);
8723d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                    onError(SipErrorCode.TRANSACTION_TERMINTED,
8733d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                            "transaction terminated");
8743d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan            }
8753d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan        }
8763d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan
8772d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private void processTimeout(TimeoutEvent event) {
878025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan            Log.d(TAG, "processing Timeout...");
8792d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            switch (mState) {
88084a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                case SipSession.State.REGISTERING:
88184a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                case SipSession.State.DEREGISTERING:
8823d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                    reset();
8833d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                    mProxy.onRegistrationTimeout(this);
8843d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                    break;
88584a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                case SipSession.State.INCOMING_CALL:
88684a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                case SipSession.State.INCOMING_CALL_ANSWERING:
88784a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                case SipSession.State.OUTGOING_CALL:
88884a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                case SipSession.State.OUTGOING_CALL_CANCELING:
8893d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                    onError(SipErrorCode.TIME_OUT, event.toString());
8903d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                    break;
8912d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
8923d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                default:
8933d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                    Log.d(TAG, "   do nothing");
8943d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                    break;
8952d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
8962d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
8972d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
8982d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private int getExpiryTime(Response response) {
8995f760064e1975a50e4abb63e560731c8b2c7b56cChia-chi Yeh            int time = -1;
9005f760064e1975a50e4abb63e560731c8b2c7b56cChia-chi Yeh            ContactHeader contact = (ContactHeader) response.getHeader(ContactHeader.NAME);
9015f760064e1975a50e4abb63e560731c8b2c7b56cChia-chi Yeh            if (contact != null) {
9025f760064e1975a50e4abb63e560731c8b2c7b56cChia-chi Yeh                time = contact.getExpires();
9032d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
9045f760064e1975a50e4abb63e560731c8b2c7b56cChia-chi Yeh            ExpiresHeader expires = (ExpiresHeader) response.getHeader(ExpiresHeader.NAME);
9055f760064e1975a50e4abb63e560731c8b2c7b56cChia-chi Yeh            if (expires != null && (time < 0 || time > expires.getExpires())) {
9065f760064e1975a50e4abb63e560731c8b2c7b56cChia-chi Yeh                time = expires.getExpires();
9075f760064e1975a50e4abb63e560731c8b2c7b56cChia-chi Yeh            }
90854eabd6c929c6f56da28421839b0ef2945cda876Chia-chi Yeh            if (time <= 0) {
90954eabd6c929c6f56da28421839b0ef2945cda876Chia-chi Yeh                time = EXPIRY_TIME;
91054eabd6c929c6f56da28421839b0ef2945cda876Chia-chi Yeh            }
9115f760064e1975a50e4abb63e560731c8b2c7b56cChia-chi Yeh            expires = (ExpiresHeader) response.getHeader(MinExpiresHeader.NAME);
9125f760064e1975a50e4abb63e560731c8b2c7b56cChia-chi Yeh            if (expires != null && time < expires.getExpires()) {
9135f760064e1975a50e4abb63e560731c8b2c7b56cChia-chi Yeh                time = expires.getExpires();
9145f760064e1975a50e4abb63e560731c8b2c7b56cChia-chi Yeh            }
915cb6ee06f62c20ae036a206667097f20b837b11abChia-chi Yeh            if (DEBUG) {
916cb6ee06f62c20ae036a206667097f20b837b11abChia-chi Yeh                Log.v(TAG, "Expiry time = " + time);
917cb6ee06f62c20ae036a206667097f20b837b11abChia-chi Yeh            }
91854eabd6c929c6f56da28421839b0ef2945cda876Chia-chi Yeh            return time;
9192d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
9202d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
9212d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private boolean registeringToReady(EventObject evt)
9222d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                throws SipException {
9232d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            if (expectResponse(Request.REGISTER, evt)) {
9242d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                ResponseEvent event = (ResponseEvent) evt;
9252d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                Response response = event.getResponse();
9262d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
9272d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                int statusCode = response.getStatusCode();
9282d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                switch (statusCode) {
9292d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                case Response.OK:
93097963794af1e18674dd111e3ad344d90b16c922cHung-ying Tyan                    int state = mState;
93184a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                    onRegistrationDone((state == SipSession.State.REGISTERING)
9322d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                            ? getExpiryTime(((ResponseEvent) evt).getResponse())
9332d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                            : -1);
9342d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    return true;
9352d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                case Response.UNAUTHORIZED:
9362d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                case Response.PROXY_AUTHENTICATION_REQUIRED:
937ee8a884f3504c981be8a1d6888b4590a0a394e05Hung-ying Tyan                    handleAuthentication(event);
9382d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    return true;
9392d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                default:
9402d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    if (statusCode >= 500) {
9413d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                        onRegistrationFailed(response);
9422d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        return true;
9432d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    }
9442d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                }
9452d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
9462d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return false;
9472d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
9482d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
949903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan        private boolean handleAuthentication(ResponseEvent event)
950903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                throws SipException {
951903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan            Response response = event.getResponse();
952903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan            String nonce = getNonceFromResponse(response);
953ee8a884f3504c981be8a1d6888b4590a0a394e05Hung-ying Tyan            if (nonce == null) {
954ee8a884f3504c981be8a1d6888b4590a0a394e05Hung-ying Tyan                onError(SipErrorCode.SERVER_ERROR,
955ee8a884f3504c981be8a1d6888b4590a0a394e05Hung-ying Tyan                        "server does not provide challenge");
956903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                return false;
957ee8a884f3504c981be8a1d6888b4590a0a394e05Hung-ying Tyan            } else if (mAuthenticationRetryCount < 2) {
958903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                mClientTransaction = mSipHelper.handleChallenge(
959903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                        event, getAccountManager());
960903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                mDialog = mClientTransaction.getDialog();
961ee8a884f3504c981be8a1d6888b4590a0a394e05Hung-ying Tyan                mAuthenticationRetryCount++;
962ee8a884f3504c981be8a1d6888b4590a0a394e05Hung-ying Tyan                if (isLoggable(this, event)) {
963ee8a884f3504c981be8a1d6888b4590a0a394e05Hung-ying Tyan                    Log.d(TAG, "   authentication retry count="
964ee8a884f3504c981be8a1d6888b4590a0a394e05Hung-ying Tyan                            + mAuthenticationRetryCount);
965ee8a884f3504c981be8a1d6888b4590a0a394e05Hung-ying Tyan                }
966903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                return true;
967ee8a884f3504c981be8a1d6888b4590a0a394e05Hung-ying Tyan            } else {
968a936b256eb1611b5d8b88d0cd61f21225152cc82Hung-ying Tyan                if (crossDomainAuthenticationRequired(response)) {
969a936b256eb1611b5d8b88d0cd61f21225152cc82Hung-ying Tyan                    onError(SipErrorCode.CROSS_DOMAIN_AUTHENTICATION,
970a936b256eb1611b5d8b88d0cd61f21225152cc82Hung-ying Tyan                            getRealmFromResponse(response));
971a936b256eb1611b5d8b88d0cd61f21225152cc82Hung-ying Tyan                } else {
972a936b256eb1611b5d8b88d0cd61f21225152cc82Hung-ying Tyan                    onError(SipErrorCode.INVALID_CREDENTIALS,
973a936b256eb1611b5d8b88d0cd61f21225152cc82Hung-ying Tyan                            "incorrect username or password");
974a936b256eb1611b5d8b88d0cd61f21225152cc82Hung-ying Tyan                }
975ee8a884f3504c981be8a1d6888b4590a0a394e05Hung-ying Tyan                return false;
976903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan            }
977903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan        }
978903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan
97900a22064efef4f574e439079aae2deae1a087a31Hung-ying Tyan        private boolean crossDomainAuthenticationRequired(Response response) {
98000a22064efef4f574e439079aae2deae1a087a31Hung-ying Tyan            String realm = getRealmFromResponse(response);
98100a22064efef4f574e439079aae2deae1a087a31Hung-ying Tyan            if (realm == null) realm = "";
98200a22064efef4f574e439079aae2deae1a087a31Hung-ying Tyan            return !mLocalProfile.getSipDomain().trim().equals(realm.trim());
98300a22064efef4f574e439079aae2deae1a087a31Hung-ying Tyan        }
98400a22064efef4f574e439079aae2deae1a087a31Hung-ying Tyan
9852d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private AccountManager getAccountManager() {
9862d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return new AccountManager() {
9872d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                public UserCredentials getCredentials(ClientTransaction
9882d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        challengedTransaction, String realm) {
9892d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    return new UserCredentials() {
9902d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        public String getUserName() {
9910f7de88cb9eef781117fa2f2b69ba2698237637eChung-yih Wang                            String username = mLocalProfile.getAuthUserName();
9920f7de88cb9eef781117fa2f2b69ba2698237637eChung-yih Wang                            return (!TextUtils.isEmpty(username) ? username :
9930f7de88cb9eef781117fa2f2b69ba2698237637eChung-yih Wang                                    mLocalProfile.getUserName());
9942d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        }
9952d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
9962d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        public String getPassword() {
9972d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                            return mPassword;
9982d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        }
9992d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
10002d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        public String getSipDomain() {
10012d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                            return mLocalProfile.getSipDomain();
10022d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        }
10032d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    };
10042d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                }
10052d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            };
10062d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
10072d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
100800a22064efef4f574e439079aae2deae1a087a31Hung-ying Tyan        private String getRealmFromResponse(Response response) {
100900a22064efef4f574e439079aae2deae1a087a31Hung-ying Tyan            WWWAuthenticate wwwAuth = (WWWAuthenticate)response.getHeader(
101000a22064efef4f574e439079aae2deae1a087a31Hung-ying Tyan                    SIPHeaderNames.WWW_AUTHENTICATE);
101100a22064efef4f574e439079aae2deae1a087a31Hung-ying Tyan            if (wwwAuth != null) return wwwAuth.getRealm();
101200a22064efef4f574e439079aae2deae1a087a31Hung-ying Tyan            ProxyAuthenticate proxyAuth = (ProxyAuthenticate)response.getHeader(
101300a22064efef4f574e439079aae2deae1a087a31Hung-ying Tyan                    SIPHeaderNames.PROXY_AUTHENTICATE);
101400a22064efef4f574e439079aae2deae1a087a31Hung-ying Tyan            return (proxyAuth == null) ? null : proxyAuth.getRealm();
101500a22064efef4f574e439079aae2deae1a087a31Hung-ying Tyan        }
101600a22064efef4f574e439079aae2deae1a087a31Hung-ying Tyan
10172d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private String getNonceFromResponse(Response response) {
10187d137e40cd36290c6bfb5beaf66f4018ae92c97fChung-yih Wang            WWWAuthenticate wwwAuth = (WWWAuthenticate)response.getHeader(
10197d137e40cd36290c6bfb5beaf66f4018ae92c97fChung-yih Wang                    SIPHeaderNames.WWW_AUTHENTICATE);
10207d137e40cd36290c6bfb5beaf66f4018ae92c97fChung-yih Wang            if (wwwAuth != null) return wwwAuth.getNonce();
10217d137e40cd36290c6bfb5beaf66f4018ae92c97fChung-yih Wang            ProxyAuthenticate proxyAuth = (ProxyAuthenticate)response.getHeader(
10227d137e40cd36290c6bfb5beaf66f4018ae92c97fChung-yih Wang                    SIPHeaderNames.PROXY_AUTHENTICATE);
10237d137e40cd36290c6bfb5beaf66f4018ae92c97fChung-yih Wang            return (proxyAuth == null) ? null : proxyAuth.getNonce();
10242d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
10252d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
1026307f15faafa5a38d9b3b314df22778cd11685d7brepo sync        private String getResponseString(int statusCode) {
1027307f15faafa5a38d9b3b314df22778cd11685d7brepo sync            StatusLine statusLine = new StatusLine();
1028307f15faafa5a38d9b3b314df22778cd11685d7brepo sync            statusLine.setStatusCode(statusCode);
1029307f15faafa5a38d9b3b314df22778cd11685d7brepo sync            statusLine.setReasonPhrase(SIPResponse.getReasonPhrase(statusCode));
1030307f15faafa5a38d9b3b314df22778cd11685d7brepo sync            return statusLine.encode();
1031307f15faafa5a38d9b3b314df22778cd11685d7brepo sync        }
1032307f15faafa5a38d9b3b314df22778cd11685d7brepo sync
10332d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private boolean readyForCall(EventObject evt) throws SipException {
10342d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            // expect MakeCallCommand, RegisterCommand, DEREGISTER
10352d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            if (evt instanceof MakeCallCommand) {
1036fb3a98b1d8d0ad040980d509c4c5341928b9460bHung-ying Tyan                mState = SipSession.State.OUTGOING_CALL;
10372d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                MakeCallCommand cmd = (MakeCallCommand) evt;
10382d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mPeerProfile = cmd.getPeerProfile();
1039307f15faafa5a38d9b3b314df22778cd11685d7brepo sync                if (mReferSession != null) {
1040307f15faafa5a38d9b3b314df22778cd11685d7brepo sync                    mSipHelper.sendReferNotify(mReferSession.mDialog,
1041307f15faafa5a38d9b3b314df22778cd11685d7brepo sync                            getResponseString(Response.TRYING));
1042307f15faafa5a38d9b3b314df22778cd11685d7brepo sync                }
1043307f15faafa5a38d9b3b314df22778cd11685d7brepo sync                mClientTransaction = mSipHelper.sendInvite(
1044307f15faafa5a38d9b3b314df22778cd11685d7brepo sync                        mLocalProfile, mPeerProfile, cmd.getSessionDescription(),
1045307f15faafa5a38d9b3b314df22778cd11685d7brepo sync                        generateTag(), mReferredBy, mReplaces);
10462d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mDialog = mClientTransaction.getDialog();
10472d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                addSipSession(this);
10489352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                startSessionTimer(cmd.getTimeout());
10499ea96c6cade1f25d4d77dcbd24854df431548b36Hung-ying Tyan                mProxy.onCalling(this);
10502d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return true;
10512d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            } else if (evt instanceof RegisterCommand) {
1052fb3a98b1d8d0ad040980d509c4c5341928b9460bHung-ying Tyan                mState = SipSession.State.REGISTERING;
10532d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                int duration = ((RegisterCommand) evt).getDuration();
10542d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mClientTransaction = mSipHelper.sendRegister(mLocalProfile,
10552d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        generateTag(), duration);
10562d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mDialog = mClientTransaction.getDialog();
10572d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                addSipSession(this);
10582d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mProxy.onRegistering(this);
10592d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return true;
10602d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            } else if (DEREGISTER == evt) {
1061fb3a98b1d8d0ad040980d509c4c5341928b9460bHung-ying Tyan                mState = SipSession.State.DEREGISTERING;
10622d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mClientTransaction = mSipHelper.sendRegister(mLocalProfile,
10632d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        generateTag(), 0);
10642d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mDialog = mClientTransaction.getDialog();
10652d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                addSipSession(this);
10662d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mProxy.onRegistering(this);
10672d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return true;
10682d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
10692d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return false;
10702d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
10712d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
10722d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private boolean incomingCall(EventObject evt) throws SipException {
10732d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            // expect MakeCallCommand(answering) , END_CALL cmd , Cancel
10742d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            if (evt instanceof MakeCallCommand) {
10752d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                // answer call
1076fb3a98b1d8d0ad040980d509c4c5341928b9460bHung-ying Tyan                mState = SipSession.State.INCOMING_CALL_ANSWERING;
10772d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mServerTransaction = mSipHelper.sendInviteOk(mInviteReceived,
10782d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        mLocalProfile,
10792d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        ((MakeCallCommand) evt).getSessionDescription(),
108099705b52ec952012bedc4aa8e1f62caff80a6a2fHung-ying Tyan                        mServerTransaction,
108199705b52ec952012bedc4aa8e1f62caff80a6a2fHung-ying Tyan                        mExternalIp, mExternalPort);
10829352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                startSessionTimer(((MakeCallCommand) evt).getTimeout());
10832d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return true;
10842d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            } else if (END_CALL == evt) {
10852d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mSipHelper.sendInviteBusyHere(mInviteReceived,
10862d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        mServerTransaction);
10872d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                endCallNormally();
10882d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return true;
10892d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            } else if (isRequestEvent(Request.CANCEL, evt)) {
10902d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                RequestEvent event = (RequestEvent) evt;
10912d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mSipHelper.sendResponse(event, Response.OK);
10922d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mSipHelper.sendInviteRequestTerminated(
10932d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        mInviteReceived.getRequest(), mServerTransaction);
10942d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                endCallNormally();
10952d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return true;
10962d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
10972d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return false;
10982d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
10992d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
11002d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private boolean incomingCallToInCall(EventObject evt)
11012d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                throws SipException {
11022d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            // expect ACK, CANCEL request
11032d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            if (isRequestEvent(Request.ACK, evt)) {
11042093561a58e602450f6e4f2aae4831edd1b840f4repo sync                String sdp = extractContent(((RequestEvent) evt).getRequest());
11052093561a58e602450f6e4f2aae4831edd1b840f4repo sync                if (sdp != null) mPeerSessionDescription = sdp;
11062093561a58e602450f6e4f2aae4831edd1b840f4repo sync                if (mPeerSessionDescription == null) {
11072093561a58e602450f6e4f2aae4831edd1b840f4repo sync                    onError(SipErrorCode.CLIENT_ERROR, "peer sdp is empty");
11082093561a58e602450f6e4f2aae4831edd1b840f4repo sync                } else {
11092093561a58e602450f6e4f2aae4831edd1b840f4repo sync                    establishCall(false);
11102093561a58e602450f6e4f2aae4831edd1b840f4repo sync                }
11112d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return true;
11122d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            } else if (isRequestEvent(Request.CANCEL, evt)) {
11132d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                // http://tools.ietf.org/html/rfc3261#section-9.2
11142d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                // Final response has been sent; do nothing here.
11152d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return true;
11162d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
11172d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return false;
11182d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
11192d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
11202d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private boolean outgoingCall(EventObject evt) throws SipException {
11212d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            if (expectResponse(Request.INVITE, evt)) {
11222d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                ResponseEvent event = (ResponseEvent) evt;
11232d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                Response response = event.getResponse();
11242d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
11252d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                int statusCode = response.getStatusCode();
11262d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                switch (statusCode) {
11272d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                case Response.RINGING:
11286057cd00d95c756b78f22c67279cb982bc0674efHung-ying Tyan                case Response.CALL_IS_BEING_FORWARDED:
11296057cd00d95c756b78f22c67279cb982bc0674efHung-ying Tyan                case Response.QUEUED:
11306057cd00d95c756b78f22c67279cb982bc0674efHung-ying Tyan                case Response.SESSION_PROGRESS:
11316057cd00d95c756b78f22c67279cb982bc0674efHung-ying Tyan                    // feedback any provisional responses (except TRYING) as
11326057cd00d95c756b78f22c67279cb982bc0674efHung-ying Tyan                    // ring back for better UX
113384a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                    if (mState == SipSession.State.OUTGOING_CALL) {
113484a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                        mState = SipSession.State.OUTGOING_CALL_RING_BACK;
11359352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                        cancelSessionTimer();
11369ea96c6cade1f25d4d77dcbd24854df431548b36Hung-ying Tyan                        mProxy.onRingingBack(this);
11372d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    }
11382d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    return true;
11392d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                case Response.OK:
1140307f15faafa5a38d9b3b314df22778cd11685d7brepo sync                    if (mReferSession != null) {
1141307f15faafa5a38d9b3b314df22778cd11685d7brepo sync                        mSipHelper.sendReferNotify(mReferSession.mDialog,
1142307f15faafa5a38d9b3b314df22778cd11685d7brepo sync                                getResponseString(Response.OK));
1143307f15faafa5a38d9b3b314df22778cd11685d7brepo sync                        // since we don't need to remember the session anymore.
1144307f15faafa5a38d9b3b314df22778cd11685d7brepo sync                        mReferSession = null;
1145307f15faafa5a38d9b3b314df22778cd11685d7brepo sync                    }
11462d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    mSipHelper.sendInviteAck(event, mDialog);
114795b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh                    mPeerSessionDescription = extractContent(response);
1148233718c3c5a4f5b4f564af93cb2e42d80a900904Hung-ying Tyan                    establishCall(true);
11492d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    return true;
11500e0633828928481658c0e09e5893f6214b57ba38Chung-yih Wang                case Response.UNAUTHORIZED:
11512d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                case Response.PROXY_AUTHENTICATION_REQUIRED:
1152a936b256eb1611b5d8b88d0cd61f21225152cc82Hung-ying Tyan                    if (handleAuthentication(event)) {
1153903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                        addSipSession(this);
1154903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                    }
11552d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    return true;
11562d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                case Response.REQUEST_PENDING:
11572d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    // TODO:
11582d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    // rfc3261#section-14.1; re-schedule invite
11592d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    return true;
11602d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                default:
1161307f15faafa5a38d9b3b314df22778cd11685d7brepo sync                    if (mReferSession != null) {
1162307f15faafa5a38d9b3b314df22778cd11685d7brepo sync                        mSipHelper.sendReferNotify(mReferSession.mDialog,
1163307f15faafa5a38d9b3b314df22778cd11685d7brepo sync                                getResponseString(Response.SERVICE_UNAVAILABLE));
1164307f15faafa5a38d9b3b314df22778cd11685d7brepo sync                    }
11652d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    if (statusCode >= 400) {
11662d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        // error: an ack is sent automatically by the stack
1167903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                        onError(response);
11682d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        return true;
11692d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    } else if (statusCode >= 300) {
11702d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        // TODO: handle 3xx (redirect)
11712d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    } else {
11722d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        return true;
11732d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    }
11742d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                }
11752d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return false;
11762d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            } else if (END_CALL == evt) {
11772d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                // RFC says that UA should not send out cancel when no
11782d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                // response comes back yet. We are cheating for not checking
11792d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                // response.
118084a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                mState = SipSession.State.OUTGOING_CALL_CANCELING;
1181fb3a98b1d8d0ad040980d509c4c5341928b9460bHung-ying Tyan                mSipHelper.sendCancel(mClientTransaction);
11829352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                startSessionTimer(CANCEL_CALL_TIMER);
11832d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return true;
11840a6e717fb6846f66b8dc853e079f2166307bfc60Hung-ying Tyan            } else if (isRequestEvent(Request.INVITE, evt)) {
11850a6e717fb6846f66b8dc853e079f2166307bfc60Hung-ying Tyan                // Call self? Send BUSY HERE so server may redirect the call to
11860a6e717fb6846f66b8dc853e079f2166307bfc60Hung-ying Tyan                // voice mailbox.
11870a6e717fb6846f66b8dc853e079f2166307bfc60Hung-ying Tyan                RequestEvent event = (RequestEvent) evt;
11880a6e717fb6846f66b8dc853e079f2166307bfc60Hung-ying Tyan                mSipHelper.sendInviteBusyHere(event,
11890a6e717fb6846f66b8dc853e079f2166307bfc60Hung-ying Tyan                        event.getServerTransaction());
11900a6e717fb6846f66b8dc853e079f2166307bfc60Hung-ying Tyan                return true;
11912d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
11922d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return false;
11932d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
11942d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
11952d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private boolean outgoingCallToReady(EventObject evt)
11962d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                throws SipException {
11972d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            if (evt instanceof ResponseEvent) {
11982d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                ResponseEvent event = (ResponseEvent) evt;
11992d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                Response response = event.getResponse();
12002d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                int statusCode = response.getStatusCode();
12012d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                if (expectResponse(Request.CANCEL, evt)) {
1202025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan                    if (statusCode == Response.OK) {
1203025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan                        // do nothing; wait for REQUEST_TERMINATED
1204025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan                        return true;
1205025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan                    }
1206025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan                } else if (expectResponse(Request.INVITE, evt)) {
12079352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                    switch (statusCode) {
12089352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                        case Response.OK:
1209025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan                            outgoingCall(evt); // abort Cancel
12109352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                            return true;
12119352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                        case Response.REQUEST_TERMINATED:
12129352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                            endCallNormally();
12139352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                            return true;
12142d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    }
12152d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                } else {
12162d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    return false;
12172d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                }
12182d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
12192d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                if (statusCode >= 400) {
12203d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                    onError(response);
12212d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    return true;
12222d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                }
12232d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            } else if (evt instanceof TransactionTerminatedEvent) {
12242d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                // rfc3261#section-14.1:
12252d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                // if re-invite gets timed out, terminate the dialog; but
12262d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                // re-invite is not reliable, just let it go and pretend
12272d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                // nothing happened.
12282d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                onError(new SipException("timed out"));
12292d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
12302d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return false;
12312d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
12322d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
1233307f15faafa5a38d9b3b314df22778cd11685d7brepo sync        private boolean processReferRequest(RequestEvent event)
1234307f15faafa5a38d9b3b314df22778cd11685d7brepo sync                throws SipException {
1235307f15faafa5a38d9b3b314df22778cd11685d7brepo sync            try {
1236307f15faafa5a38d9b3b314df22778cd11685d7brepo sync                ReferToHeader referto = (ReferToHeader) event.getRequest()
1237307f15faafa5a38d9b3b314df22778cd11685d7brepo sync                        .getHeader(ReferTo.NAME);
1238307f15faafa5a38d9b3b314df22778cd11685d7brepo sync                Address address = referto.getAddress();
1239307f15faafa5a38d9b3b314df22778cd11685d7brepo sync                SipURI uri = (SipURI) address.getURI();
1240307f15faafa5a38d9b3b314df22778cd11685d7brepo sync                String replacesHeader = uri.getHeader(ReplacesHeader.NAME);
1241307f15faafa5a38d9b3b314df22778cd11685d7brepo sync                String username = uri.getUser();
1242307f15faafa5a38d9b3b314df22778cd11685d7brepo sync                if (username == null) {
1243307f15faafa5a38d9b3b314df22778cd11685d7brepo sync                    mSipHelper.sendResponse(event, Response.BAD_REQUEST);
1244307f15faafa5a38d9b3b314df22778cd11685d7brepo sync                    return false;
1245307f15faafa5a38d9b3b314df22778cd11685d7brepo sync                }
1246307f15faafa5a38d9b3b314df22778cd11685d7brepo sync                // send notify accepted
1247307f15faafa5a38d9b3b314df22778cd11685d7brepo sync                mSipHelper.sendResponse(event, Response.ACCEPTED);
1248307f15faafa5a38d9b3b314df22778cd11685d7brepo sync                SipSessionImpl newSession = createNewSession(event,
1249307f15faafa5a38d9b3b314df22778cd11685d7brepo sync                        this.mProxy.getListener(),
1250307f15faafa5a38d9b3b314df22778cd11685d7brepo sync                        mSipHelper.getServerTransaction(event),
1251307f15faafa5a38d9b3b314df22778cd11685d7brepo sync                        SipSession.State.READY_TO_CALL);
1252307f15faafa5a38d9b3b314df22778cd11685d7brepo sync                newSession.mReferSession = this;
1253307f15faafa5a38d9b3b314df22778cd11685d7brepo sync                newSession.mReferredBy = (ReferredByHeader) event.getRequest()
1254307f15faafa5a38d9b3b314df22778cd11685d7brepo sync                        .getHeader(ReferredByHeader.NAME);
1255307f15faafa5a38d9b3b314df22778cd11685d7brepo sync                newSession.mReplaces = replacesHeader;
1256307f15faafa5a38d9b3b314df22778cd11685d7brepo sync                newSession.mPeerProfile = createPeerProfile(referto);
1257307f15faafa5a38d9b3b314df22778cd11685d7brepo sync                newSession.mProxy.onCallTransferring(newSession,
1258307f15faafa5a38d9b3b314df22778cd11685d7brepo sync                        null);
1259307f15faafa5a38d9b3b314df22778cd11685d7brepo sync                return true;
1260307f15faafa5a38d9b3b314df22778cd11685d7brepo sync            } catch (IllegalArgumentException e) {
1261307f15faafa5a38d9b3b314df22778cd11685d7brepo sync                throw new SipException("createPeerProfile()", e);
1262307f15faafa5a38d9b3b314df22778cd11685d7brepo sync            }
1263307f15faafa5a38d9b3b314df22778cd11685d7brepo sync        }
1264307f15faafa5a38d9b3b314df22778cd11685d7brepo sync
12652d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private boolean inCall(EventObject evt) throws SipException {
12662d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            // expect END_CALL cmd, BYE request, hold call (MakeCallCommand)
12672d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            // OK retransmission is handled in SipStack
12682d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            if (END_CALL == evt) {
12692d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                // rfc3261#section-15.1.1
1270dc5bbe965f7a66238c3f9a6694f4410b3d52af3bHung-ying Tyan                mState = SipSession.State.ENDING_CALL;
12712d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mSipHelper.sendBye(mDialog);
1272dc5bbe965f7a66238c3f9a6694f4410b3d52af3bHung-ying Tyan                mProxy.onCallEnded(this);
1273dc5bbe965f7a66238c3f9a6694f4410b3d52af3bHung-ying Tyan                startSessionTimer(END_CALL_TIMER);
12742d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return true;
12752d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            } else if (isRequestEvent(Request.INVITE, evt)) {
12762d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                // got Re-INVITE
127784a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                mState = SipSession.State.INCOMING_CALL;
1278fb3a98b1d8d0ad040980d509c4c5341928b9460bHung-ying Tyan                RequestEvent event = mInviteReceived = (RequestEvent) evt;
127995b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh                mPeerSessionDescription = extractContent(event.getRequest());
12802d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mServerTransaction = null;
12812d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mProxy.onRinging(this, mPeerProfile, mPeerSessionDescription);
12822d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return true;
12832d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            } else if (isRequestEvent(Request.BYE, evt)) {
12842d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mSipHelper.sendResponse((RequestEvent) evt, Response.OK);
12852d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                endCallNormally();
12862d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return true;
1287307f15faafa5a38d9b3b314df22778cd11685d7brepo sync            } else if (isRequestEvent(Request.REFER, evt)) {
1288307f15faafa5a38d9b3b314df22778cd11685d7brepo sync                return processReferRequest((RequestEvent) evt);
12892d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            } else if (evt instanceof MakeCallCommand) {
12902d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                // to change call
1291fb3a98b1d8d0ad040980d509c4c5341928b9460bHung-ying Tyan                mState = SipSession.State.OUTGOING_CALL;
12922d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mClientTransaction = mSipHelper.sendReinvite(mDialog,
12932d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        ((MakeCallCommand) evt).getSessionDescription());
12949352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                startSessionTimer(((MakeCallCommand) evt).getTimeout());
12952d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return true;
1296307f15faafa5a38d9b3b314df22778cd11685d7brepo sync            } else if (evt instanceof ResponseEvent) {
1297307f15faafa5a38d9b3b314df22778cd11685d7brepo sync                if (expectResponse(Request.NOTIFY, evt)) return true;
12982d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
12992d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return false;
13002d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
13012d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
1302dc5bbe965f7a66238c3f9a6694f4410b3d52af3bHung-ying Tyan        private boolean endingCall(EventObject evt) throws SipException {
1303dc5bbe965f7a66238c3f9a6694f4410b3d52af3bHung-ying Tyan            if (expectResponse(Request.BYE, evt)) {
1304dc5bbe965f7a66238c3f9a6694f4410b3d52af3bHung-ying Tyan                ResponseEvent event = (ResponseEvent) evt;
1305dc5bbe965f7a66238c3f9a6694f4410b3d52af3bHung-ying Tyan                Response response = event.getResponse();
1306dc5bbe965f7a66238c3f9a6694f4410b3d52af3bHung-ying Tyan
1307dc5bbe965f7a66238c3f9a6694f4410b3d52af3bHung-ying Tyan                int statusCode = response.getStatusCode();
1308dc5bbe965f7a66238c3f9a6694f4410b3d52af3bHung-ying Tyan                switch (statusCode) {
1309dc5bbe965f7a66238c3f9a6694f4410b3d52af3bHung-ying Tyan                    case Response.UNAUTHORIZED:
1310dc5bbe965f7a66238c3f9a6694f4410b3d52af3bHung-ying Tyan                    case Response.PROXY_AUTHENTICATION_REQUIRED:
1311dc5bbe965f7a66238c3f9a6694f4410b3d52af3bHung-ying Tyan                        if (handleAuthentication(event)) {
1312dc5bbe965f7a66238c3f9a6694f4410b3d52af3bHung-ying Tyan                            return true;
1313dc5bbe965f7a66238c3f9a6694f4410b3d52af3bHung-ying Tyan                        } else {
1314dc5bbe965f7a66238c3f9a6694f4410b3d52af3bHung-ying Tyan                            // can't authenticate; pass through to end session
1315dc5bbe965f7a66238c3f9a6694f4410b3d52af3bHung-ying Tyan                        }
1316dc5bbe965f7a66238c3f9a6694f4410b3d52af3bHung-ying Tyan                }
1317dc5bbe965f7a66238c3f9a6694f4410b3d52af3bHung-ying Tyan                cancelSessionTimer();
1318dc5bbe965f7a66238c3f9a6694f4410b3d52af3bHung-ying Tyan                reset();
1319dc5bbe965f7a66238c3f9a6694f4410b3d52af3bHung-ying Tyan                return true;
1320dc5bbe965f7a66238c3f9a6694f4410b3d52af3bHung-ying Tyan            }
1321dc5bbe965f7a66238c3f9a6694f4410b3d52af3bHung-ying Tyan            return false;
1322dc5bbe965f7a66238c3f9a6694f4410b3d52af3bHung-ying Tyan        }
1323dc5bbe965f7a66238c3f9a6694f4410b3d52af3bHung-ying Tyan
13249352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan        // timeout in seconds
13259352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan        private void startSessionTimer(int timeout) {
13269352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan            if (timeout > 0) {
13274a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                mSessionTimer = new SessionTimer();
13284a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                mSessionTimer.start(timeout);
13299352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan            }
13309352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan        }
13319352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan
13329352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan        private void cancelSessionTimer() {
13334a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan            if (mSessionTimer != null) {
13344a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                mSessionTimer.cancel();
13354a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                mSessionTimer = null;
13369352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan            }
13379352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan        }
13389352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan
1339903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan        private String createErrorMessage(Response response) {
1340624d5b4e8c20516516d0bff74479b9f5abdfe61cHung-ying Tyan            return String.format("%s (%d)", response.getReasonPhrase(),
1341624d5b4e8c20516516d0bff74479b9f5abdfe61cHung-ying Tyan                    response.getStatusCode());
1342903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan        }
1343903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan
1344233718c3c5a4f5b4f564af93cb2e42d80a900904Hung-ying Tyan        private void enableKeepAlive() {
1345233718c3c5a4f5b4f564af93cb2e42d80a900904Hung-ying Tyan            if (mKeepAliveSession != null) {
1346233718c3c5a4f5b4f564af93cb2e42d80a900904Hung-ying Tyan                mKeepAliveSession.stopKeepAliveProcess();
1347233718c3c5a4f5b4f564af93cb2e42d80a900904Hung-ying Tyan            } else {
1348233718c3c5a4f5b4f564af93cb2e42d80a900904Hung-ying Tyan                mKeepAliveSession = duplicate();
1349233718c3c5a4f5b4f564af93cb2e42d80a900904Hung-ying Tyan            }
1350233718c3c5a4f5b4f564af93cb2e42d80a900904Hung-ying Tyan            try {
1351233718c3c5a4f5b4f564af93cb2e42d80a900904Hung-ying Tyan                mKeepAliveSession.startKeepAliveProcess(
1352233718c3c5a4f5b4f564af93cb2e42d80a900904Hung-ying Tyan                        INCALL_KEEPALIVE_INTERVAL, mPeerProfile, null);
1353233718c3c5a4f5b4f564af93cb2e42d80a900904Hung-ying Tyan            } catch (SipException e) {
1354233718c3c5a4f5b4f564af93cb2e42d80a900904Hung-ying Tyan                Log.w(TAG, "keepalive cannot be enabled; ignored", e);
1355233718c3c5a4f5b4f564af93cb2e42d80a900904Hung-ying Tyan                mKeepAliveSession.stopKeepAliveProcess();
1356233718c3c5a4f5b4f564af93cb2e42d80a900904Hung-ying Tyan            }
1357233718c3c5a4f5b4f564af93cb2e42d80a900904Hung-ying Tyan        }
1358233718c3c5a4f5b4f564af93cb2e42d80a900904Hung-ying Tyan
1359233718c3c5a4f5b4f564af93cb2e42d80a900904Hung-ying Tyan        private void establishCall(boolean enableKeepAlive) {
136084a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan            mState = SipSession.State.IN_CALL;
13619352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan            cancelSessionTimer();
13628ba4566c01c5848b378d1d86e9041730f5b5a13fHung-ying Tyan            if (!mInCall && enableKeepAlive) enableKeepAlive();
13638ba4566c01c5848b378d1d86e9041730f5b5a13fHung-ying Tyan            mInCall = true;
13642d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            mProxy.onCallEstablished(this, mPeerSessionDescription);
13652d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
13662d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
13672d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private void endCallNormally() {
13682d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            reset();
13692d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            mProxy.onCallEnded(this);
13702d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
13712d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
137297963794af1e18674dd111e3ad344d90b16c922cHung-ying Tyan        private void endCallOnError(int errorCode, String message) {
13732d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            reset();
137497963794af1e18674dd111e3ad344d90b16c922cHung-ying Tyan            mProxy.onError(this, errorCode, message);
1375903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan        }
1376903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan
1377903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan        private void endCallOnBusy() {
1378903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan            reset();
1379903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan            mProxy.onCallBusy(this);
13802d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
13812d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
138297963794af1e18674dd111e3ad344d90b16c922cHung-ying Tyan        private void onError(int errorCode, String message) {
13839352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan            cancelSessionTimer();
13843d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan            switch (mState) {
138584a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                case SipSession.State.REGISTERING:
138684a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                case SipSession.State.DEREGISTERING:
13873d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                    onRegistrationFailed(errorCode, message);
13883d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                    break;
13893d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                default:
13904189d99b6e4877352049b7447b7f0734ef99b9e8Hung-ying Tyan                    endCallOnError(errorCode, message);
13912d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
13922d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
13932d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
13943d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan
13953d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan        private void onError(Throwable exception) {
13963d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan            exception = getRootCause(exception);
13973d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan            onError(getErrorCode(exception), exception.toString());
13983d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan        }
13993d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan
1400903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan        private void onError(Response response) {
14013d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan            int statusCode = response.getStatusCode();
1402ae076d3981fda732d54b6c6e37e5659b2e7ba130Hung-ying Tyan            if (!mInCall && (statusCode == Response.BUSY_HERE)) {
14033d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                endCallOnBusy();
1404903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan            } else {
14053d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                onError(getErrorCode(statusCode), createErrorMessage(response));
1406903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan            }
1407903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan        }
1408903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan
140997963794af1e18674dd111e3ad344d90b16c922cHung-ying Tyan        private int getErrorCode(int responseStatusCode) {
1410903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan            switch (responseStatusCode) {
1411ae076d3981fda732d54b6c6e37e5659b2e7ba130Hung-ying Tyan                case Response.TEMPORARILY_UNAVAILABLE:
1412ae076d3981fda732d54b6c6e37e5659b2e7ba130Hung-ying Tyan                case Response.FORBIDDEN:
1413ae076d3981fda732d54b6c6e37e5659b2e7ba130Hung-ying Tyan                case Response.GONE:
1414903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                case Response.NOT_FOUND:
1415ae076d3981fda732d54b6c6e37e5659b2e7ba130Hung-ying Tyan                case Response.NOT_ACCEPTABLE:
1416ae076d3981fda732d54b6c6e37e5659b2e7ba130Hung-ying Tyan                case Response.NOT_ACCEPTABLE_HERE:
1417ae076d3981fda732d54b6c6e37e5659b2e7ba130Hung-ying Tyan                    return SipErrorCode.PEER_NOT_REACHABLE;
1418ae076d3981fda732d54b6c6e37e5659b2e7ba130Hung-ying Tyan
1419ae076d3981fda732d54b6c6e37e5659b2e7ba130Hung-ying Tyan                case Response.REQUEST_URI_TOO_LONG:
1420903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                case Response.ADDRESS_INCOMPLETE:
1421ae076d3981fda732d54b6c6e37e5659b2e7ba130Hung-ying Tyan                case Response.AMBIGUOUS:
1422903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                    return SipErrorCode.INVALID_REMOTE_URI;
1423ae076d3981fda732d54b6c6e37e5659b2e7ba130Hung-ying Tyan
1424903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                case Response.REQUEST_TIMEOUT:
1425903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                    return SipErrorCode.TIME_OUT;
1426ae076d3981fda732d54b6c6e37e5659b2e7ba130Hung-ying Tyan
1427903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                default:
1428903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                    if (responseStatusCode < 500) {
1429903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                        return SipErrorCode.CLIENT_ERROR;
1430903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                    } else {
1431903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                        return SipErrorCode.SERVER_ERROR;
1432903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                    }
1433903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan            }
1434903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan        }
1435903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan
143697963794af1e18674dd111e3ad344d90b16c922cHung-ying Tyan        private int getErrorCode(Throwable exception) {
1437903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan            String message = exception.getMessage();
1438903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan            if (exception instanceof UnknownHostException) {
1439c6548fd9eda7b58f5a2e2a9c01e3c7cafd42fafbHung-ying Tyan                return SipErrorCode.SERVER_UNREACHABLE;
1440903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan            } else if (exception instanceof IOException) {
1441903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                return SipErrorCode.SOCKET_ERROR;
1442903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan            } else {
1443903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                return SipErrorCode.CLIENT_ERROR;
1444903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan            }
1445903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan        }
1446903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan
14472d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private void onRegistrationDone(int duration) {
14483d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan            reset();
14492d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            mProxy.onRegistrationDone(this, duration);
14502d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
14512d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
145297963794af1e18674dd111e3ad344d90b16c922cHung-ying Tyan        private void onRegistrationFailed(int errorCode, String message) {
14533d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan            reset();
145497963794af1e18674dd111e3ad344d90b16c922cHung-ying Tyan            mProxy.onRegistrationFailed(this, errorCode, message);
1455903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan        }
1456903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan
14572d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private void onRegistrationFailed(Throwable exception) {
1458903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan            exception = getRootCause(exception);
1459903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan            onRegistrationFailed(getErrorCode(exception),
1460903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                    exception.toString());
14612d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
14623d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan
14633d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan        private void onRegistrationFailed(Response response) {
14643d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan            int statusCode = response.getStatusCode();
14653d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan            onRegistrationFailed(getErrorCode(statusCode),
14663d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                    createErrorMessage(response));
14673d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan        }
14684a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan
14694a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan        // Notes: SipSessionListener will be replaced by the keepalive process
14704a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan        // @param interval in seconds
14714a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan        public void startKeepAliveProcess(int interval,
14724a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                KeepAliveProcessCallback callback) throws SipException {
14734a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan            synchronized (SipSessionGroup.this) {
14744a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                startKeepAliveProcess(interval, mLocalProfile, callback);
14754a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan            }
14764a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan        }
14774a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan
14784a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan        // Notes: SipSessionListener will be replaced by the keepalive process
14794a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan        // @param interval in seconds
14804a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan        public void startKeepAliveProcess(int interval, SipProfile peerProfile,
14814a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                KeepAliveProcessCallback callback) throws SipException {
14824a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan            synchronized (SipSessionGroup.this) {
14834a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                if (mKeepAliveProcess != null) {
14844a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                    throw new SipException("Cannot create more than one "
14854a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                            + "keepalive process in a SipSession");
14864a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                }
14874a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                mPeerProfile = peerProfile;
14884a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                mKeepAliveProcess = new KeepAliveProcess();
14894a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                mProxy.setListener(mKeepAliveProcess);
14904a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                mKeepAliveProcess.start(interval, callback);
14914a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan            }
14924a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan        }
14934a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan
14944a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan        public void stopKeepAliveProcess() {
14954a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan            synchronized (SipSessionGroup.this) {
14964a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                if (mKeepAliveProcess != null) {
14974a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                    mKeepAliveProcess.stop();
14984a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                    mKeepAliveProcess = null;
14994a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                }
15004a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan            }
15014a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan        }
15024a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan
15034a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan        class KeepAliveProcess extends SipSessionAdapter implements Runnable {
15044a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan            private static final String TAG = "SipKeepAlive";
15054a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan            private boolean mRunning = false;
15064a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan            private KeepAliveProcessCallback mCallback;
15074a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan
15084a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan            private boolean mPortChanged = false;
15094a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan            private int mRPort = 0;
1510e65f3a896f03bba5327ce4f3989c0422855450caHung-ying Tyan            private int mInterval; // just for debugging
15114a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan
15124a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan            // @param interval in seconds
15134a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan            void start(int interval, KeepAliveProcessCallback callback) {
15144a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                if (mRunning) return;
15154a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                mRunning = true;
1516e65f3a896f03bba5327ce4f3989c0422855450caHung-ying Tyan                mInterval = interval;
15174a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                mCallback = new KeepAliveProcessCallbackProxy(callback);
15184a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                mWakeupTimer.set(interval * 1000, this);
15194a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                if (DEBUG) {
15204a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                    Log.d(TAG, "start keepalive:"
15214a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                            + mLocalProfile.getUriString());
15224a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                }
15234a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan
15244a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                // No need to run the first time in a separate thread for now
15254a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                run();
15264a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan            }
15274a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan
15284a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan            // return true if the event is consumed
15294a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan            boolean process(EventObject evt) throws SipException {
15304a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                if (mRunning && (mState == SipSession.State.PINGING)) {
15314a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                    if (evt instanceof ResponseEvent) {
15324a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                        if (parseOptionsResult(evt)) {
15334a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                            if (mPortChanged) {
153499705b52ec952012bedc4aa8e1f62caff80a6a2fHung-ying Tyan                                resetExternalAddress();
15354a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                                stop();
15364a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                            } else {
15374a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                                cancelSessionTimer();
15384a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                                removeSipSession(SipSessionImpl.this);
15394a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                            }
15404a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                            mCallback.onResponse(mPortChanged);
15414a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                            return true;
15424a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                        }
15434a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                    }
15444a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                }
15454a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                return false;
15464a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan            }
15474a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan
15484a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan            // SipSessionAdapter
15494a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan            // To react to the session timeout event and network error.
15504a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan            @Override
15514a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan            public void onError(ISipSession session, int errorCode, String message) {
15524a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                stop();
15534a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                mCallback.onError(errorCode, message);
15544a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan            }
15554a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan
15564a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan            // SipWakeupTimer timeout handler
15574a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan            // To send out keepalive message.
15584a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan            @Override
15594a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan            public void run() {
15604a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                synchronized (SipSessionGroup.this) {
15614a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                    if (!mRunning) return;
15624a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan
15634a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                    if (DEBUG_PING) {
156499705b52ec952012bedc4aa8e1f62caff80a6a2fHung-ying Tyan                        String peerUri = (mPeerProfile == null)
156599705b52ec952012bedc4aa8e1f62caff80a6a2fHung-ying Tyan                                ? "null"
156699705b52ec952012bedc4aa8e1f62caff80a6a2fHung-ying Tyan                                : mPeerProfile.getUriString();
15674a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                        Log.d(TAG, "keepalive: " + mLocalProfile.getUriString()
156899705b52ec952012bedc4aa8e1f62caff80a6a2fHung-ying Tyan                                + " --> " + peerUri + ", interval=" + mInterval);
15694a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                    }
15704a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                    try {
15714a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                        sendKeepAlive();
15724a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                    } catch (Throwable t) {
1573cb6ee06f62c20ae036a206667097f20b837b11abChia-chi Yeh                        if (DEBUG) {
1574cb6ee06f62c20ae036a206667097f20b837b11abChia-chi Yeh                            Log.w(TAG, "keepalive error: "
1575cb6ee06f62c20ae036a206667097f20b837b11abChia-chi Yeh                                    + mLocalProfile.getUriString(), getRootCause(t));
1576cb6ee06f62c20ae036a206667097f20b837b11abChia-chi Yeh                        }
15774a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                        // It's possible that the keepalive process is being stopped
15784a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                        // during session.sendKeepAlive() so need to check mRunning
15794a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                        // again here.
15804a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                        if (mRunning) SipSessionImpl.this.onError(t);
15814a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                    }
15824a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                }
15834a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan            }
15844a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan
15854a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan            void stop() {
15864a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                synchronized (SipSessionGroup.this) {
15874a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                    if (DEBUG) {
15884a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                        Log.d(TAG, "stop keepalive:" + mLocalProfile.getUriString()
15894a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                                + ",RPort=" + mRPort);
15904a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                    }
15914a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                    mRunning = false;
15924a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                    mWakeupTimer.cancel(this);
15934a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                    reset();
15944a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                }
15954a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan            }
15964a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan
15974a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan            private void sendKeepAlive() throws SipException, InterruptedException {
15984a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                synchronized (SipSessionGroup.this) {
15994a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                    mState = SipSession.State.PINGING;
16004a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                    mClientTransaction = mSipHelper.sendOptions(
16014a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                            mLocalProfile, mPeerProfile, generateTag());
16024a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                    mDialog = mClientTransaction.getDialog();
16034a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                    addSipSession(SipSessionImpl.this);
16044a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan
16054a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                    startSessionTimer(KEEPALIVE_TIMEOUT);
16064a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                    // when timed out, onError() will be called with SipErrorCode.TIME_OUT
16074a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                }
16084a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan            }
16094a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan
16104a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan            private boolean parseOptionsResult(EventObject evt) {
16114a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                if (expectResponse(Request.OPTIONS, evt)) {
16124a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                    ResponseEvent event = (ResponseEvent) evt;
16134a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                    int rPort = getRPortFromResponse(event.getResponse());
16144a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                    if (rPort != -1) {
16154a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                        if (mRPort == 0) mRPort = rPort;
16164a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                        if (mRPort != rPort) {
16174a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                            mPortChanged = true;
16184a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                            if (DEBUG) Log.d(TAG, String.format(
16194a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                                    "rport is changed: %d <> %d", mRPort, rPort));
16204a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                            mRPort = rPort;
16214a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                        } else {
16224a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                            if (DEBUG) Log.d(TAG, "rport is the same: " + rPort);
16234a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                        }
16244a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                    } else {
16254a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                        if (DEBUG) Log.w(TAG, "peer did not respond rport");
16264a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                    }
16274a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                    return true;
16284a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                }
16294a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                return false;
16304a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan            }
16314a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan
16324a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan            private int getRPortFromResponse(Response response) {
16334a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                ViaHeader viaHeader = (ViaHeader)(response.getHeader(
16344a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                        SIPHeaderNames.VIA));
16354a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                return (viaHeader == null) ? -1 : viaHeader.getRPort();
16364a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan            }
16374a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan        }
16382d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
16392d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
16402d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    /**
16412d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang     * @return true if the event is a request event matching the specified
16422d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang     *      method; false otherwise
16432d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang     */
16442d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private static boolean isRequestEvent(String method, EventObject event) {
16452d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        try {
16462d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            if (event instanceof RequestEvent) {
16472d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                RequestEvent requestEvent = (RequestEvent) event;
16482d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return method.equals(requestEvent.getRequest().getMethod());
16492d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
16502d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        } catch (Throwable e) {
16512d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
16522d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        return false;
16532d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
16542d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
16552d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private static String getCseqMethod(Message message) {
16562d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        return ((CSeqHeader) message.getHeader(CSeqHeader.NAME)).getMethod();
16572d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
16582d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
16592d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    /**
16602d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang     * @return true if the event is a response event and the CSeqHeader method
16612d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang     * match the given arguments; false otherwise
16622d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang     */
16632d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private static boolean expectResponse(
16642d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            String expectedMethod, EventObject evt) {
16652d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        if (evt instanceof ResponseEvent) {
16662d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            ResponseEvent event = (ResponseEvent) evt;
16672d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            Response response = event.getResponse();
16682d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return expectedMethod.equalsIgnoreCase(getCseqMethod(response));
16692d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
16702d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        return false;
16712d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
16722d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
16732d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    /**
16742d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang     * @return true if the event is a response event and the response code and
16752d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang     *      CSeqHeader method match the given arguments; false otherwise
16762d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang     */
16772d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private static boolean expectResponse(
16782d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            int responseCode, String expectedMethod, EventObject evt) {
16792d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        if (evt instanceof ResponseEvent) {
16802d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            ResponseEvent event = (ResponseEvent) evt;
16812d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            Response response = event.getResponse();
16822d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            if (response.getStatusCode() == responseCode) {
16832d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return expectedMethod.equalsIgnoreCase(getCseqMethod(response));
16842d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
16852d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
16862d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        return false;
16872d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
16882d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
1689307f15faafa5a38d9b3b314df22778cd11685d7brepo sync    private static SipProfile createPeerProfile(HeaderAddress header)
16902d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            throws SipException {
16912d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        try {
1692307f15faafa5a38d9b3b314df22778cd11685d7brepo sync            Address address = header.getAddress();
16932d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            SipURI uri = (SipURI) address.getURI();
16942d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            String username = uri.getUser();
16952d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            if (username == null) username = ANONYMOUS;
169658ee2acba8953814cc4bf65d2f28f7dd498b5779Hung-ying Tyan            int port = uri.getPort();
169758ee2acba8953814cc4bf65d2f28f7dd498b5779Hung-ying Tyan            SipProfile.Builder builder =
169858ee2acba8953814cc4bf65d2f28f7dd498b5779Hung-ying Tyan                    new SipProfile.Builder(username, uri.getHost())
169958ee2acba8953814cc4bf65d2f28f7dd498b5779Hung-ying Tyan                    .setDisplayName(address.getDisplayName());
170058ee2acba8953814cc4bf65d2f28f7dd498b5779Hung-ying Tyan            if (port > 0) builder.setPort(port);
170158ee2acba8953814cc4bf65d2f28f7dd498b5779Hung-ying Tyan            return builder.build();
170299bf4e45c4566172189735b34b368b76660ca57aHung-ying Tyan        } catch (IllegalArgumentException e) {
17032d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            throw new SipException("createPeerProfile()", e);
17042d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        } catch (ParseException e) {
17052d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            throw new SipException("createPeerProfile()", e);
17062d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
17072d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
17082d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
1709c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan    private static boolean isLoggable(SipSessionImpl s) {
1710c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan        if (s != null) {
1711c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan            switch (s.mState) {
171284a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                case SipSession.State.PINGING:
1713c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan                    return DEBUG_PING;
1714c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan            }
1715c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan        }
1716c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan        return DEBUG;
1717c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan    }
1718c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan
17190a6e717fb6846f66b8dc853e079f2166307bfc60Hung-ying Tyan    private static boolean isLoggable(EventObject evt) {
17200a6e717fb6846f66b8dc853e079f2166307bfc60Hung-ying Tyan        return isLoggable(null, evt);
17210a6e717fb6846f66b8dc853e079f2166307bfc60Hung-ying Tyan    }
17220a6e717fb6846f66b8dc853e079f2166307bfc60Hung-ying Tyan
1723c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan    private static boolean isLoggable(SipSessionImpl s, EventObject evt) {
1724c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan        if (!isLoggable(s)) return false;
1725c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan        if (evt == null) return false;
1726c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan
17274a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan        if (evt instanceof ResponseEvent) {
1728c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan            Response response = ((ResponseEvent) evt).getResponse();
1729c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan            if (Request.OPTIONS.equals(response.getHeader(CSeqHeader.NAME))) {
1730c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan                return DEBUG_PING;
1731c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan            }
1732c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan            return DEBUG;
1733c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan        } else if (evt instanceof RequestEvent) {
17344a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan            if (isRequestEvent(Request.OPTIONS, evt)) {
17354a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                return DEBUG_PING;
17364a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan            }
1737c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan            return DEBUG;
1738c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan        }
1739c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan        return false;
1740c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan    }
1741c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan
17422d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private static String log(EventObject evt) {
17432d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        if (evt instanceof RequestEvent) {
17442d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return ((RequestEvent) evt).getRequest().toString();
17452d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        } else if (evt instanceof ResponseEvent) {
17462d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return ((ResponseEvent) evt).getResponse().toString();
17472d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        } else {
17482d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return evt.toString();
17492d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
17502d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
17512d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
17522d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private class RegisterCommand extends EventObject {
17532d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private int mDuration;
17542d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
17552d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public RegisterCommand(int duration) {
17562d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            super(SipSessionGroup.this);
17572d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            mDuration = duration;
17582d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
17592d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
17602d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public int getDuration() {
17612d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return mDuration;
17622d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
17632d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
17642d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
17652d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private class MakeCallCommand extends EventObject {
176695b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh        private String mSessionDescription;
17679352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan        private int mTimeout; // in seconds
17682d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
17692d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public MakeCallCommand(SipProfile peerProfile,
177095b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh                String sessionDescription) {
17719352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan            this(peerProfile, sessionDescription, -1);
17729352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan        }
17739352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan
17749352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan        public MakeCallCommand(SipProfile peerProfile,
17759352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                String sessionDescription, int timeout) {
17762d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            super(peerProfile);
17772d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            mSessionDescription = sessionDescription;
17789352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan            mTimeout = timeout;
17792d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
17802d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
17812d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public SipProfile getPeerProfile() {
17822d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return (SipProfile) getSource();
17832d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
17842d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
178595b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh        public String getSessionDescription() {
17862d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return mSessionDescription;
17872d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
17882d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
17899352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan        public int getTimeout() {
17909352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan            return mTimeout;
17919352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan        }
17929352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan    }
17934a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan
17944a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan    /** Class to help safely run KeepAliveProcessCallback in a different thread. */
17954a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan    static class KeepAliveProcessCallbackProxy implements KeepAliveProcessCallback {
17964a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan        private KeepAliveProcessCallback mCallback;
17974a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan
17984a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan        KeepAliveProcessCallbackProxy(KeepAliveProcessCallback callback) {
17994a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan            mCallback = callback;
18004a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan        }
18014a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan
18024a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan        private void proxy(Runnable runnable) {
18034a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan            // One thread for each calling back.
18044a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan            // Note: Guarantee ordering if the issue becomes important. Currently,
18054a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan            // the chance of handling two callback events at a time is none.
18064a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan            new Thread(runnable, "SIP-KeepAliveProcessCallbackThread").start();
18074a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan        }
18084a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan
18094a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan        public void onResponse(final boolean portChanged) {
18104a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan            if (mCallback == null) return;
18114a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan            proxy(new Runnable() {
18124a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                public void run() {
18134a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                    try {
18144a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                        mCallback.onResponse(portChanged);
18154a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                    } catch (Throwable t) {
18164a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                        Log.w(TAG, "onResponse", t);
18174a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                    }
18184a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                }
18194a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan            });
18204a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan        }
18214a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan
18224a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan        public void onError(final int errorCode, final String description) {
18234a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan            if (mCallback == null) return;
18244a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan            proxy(new Runnable() {
18254a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                public void run() {
18264a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                    try {
18274a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                        mCallback.onError(errorCode, description);
18284a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                    } catch (Throwable t) {
18294a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                        Log.w(TAG, "onError", t);
18304a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                    }
18314a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan                }
18324a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan            });
18334a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan        }
18344a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan    }
18352d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang}
1836