SipSessionGroup.java revision d47a47a131a8e7f80b934aec4cc7a10fd9fe1094
19c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan/*
29c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan * Copyright (C) 2010 The Android Open Source Project
39c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan *
49c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan * Licensed under the Apache License, Version 2.0 (the "License");
59c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan * you may not use this file except in compliance with the License.
69c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan * You may obtain a copy of the License at
79c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan *
89c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan *      http://www.apache.org/licenses/LICENSE-2.0
99c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan *
109c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan * Unless required by applicable law or agreed to in writing, software
119c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan * distributed under the License is distributed on an "AS IS" BASIS,
129c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
139c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan * See the License for the specific language governing permissions and
149c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan * limitations under the License.
159c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan */
169c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
179c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanpackage com.android.server.sip;
189c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
199c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport gov.nist.javax.sip.clientauthutils.AccountManager;
209c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport gov.nist.javax.sip.clientauthutils.UserCredentials;
219c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport gov.nist.javax.sip.header.SIPHeaderNames;
229c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport gov.nist.javax.sip.header.ProxyAuthenticate;
239c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport gov.nist.javax.sip.header.WWWAuthenticate;
249c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport gov.nist.javax.sip.message.SIPMessage;
259c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
269c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport android.net.sip.ISipSession;
279c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport android.net.sip.ISipSessionListener;
289c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport android.net.sip.SipErrorCode;
299c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport android.net.sip.SipProfile;
309c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport android.net.sip.SipSession;
319c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport android.text.TextUtils;
329c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport android.util.Log;
339c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
349c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport java.io.IOException;
359c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport java.io.UnsupportedEncodingException;
369c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport java.net.DatagramSocket;
379c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport java.net.UnknownHostException;
389c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport java.text.ParseException;
399c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport java.util.Collection;
409c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport java.util.EventObject;
419c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport java.util.HashMap;
429c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport java.util.Map;
439c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport java.util.Properties;
449c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport java.util.TooManyListenersException;
459c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
469c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport javax.sip.ClientTransaction;
479c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport javax.sip.Dialog;
489c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport javax.sip.DialogTerminatedEvent;
499c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport javax.sip.IOExceptionEvent;
509c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport javax.sip.InvalidArgumentException;
519c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport javax.sip.ListeningPoint;
52e8782e2fdfd256296a0d1fd2a1c3f98d58103eaaHung-ying Tyanimport javax.sip.ObjectInUseException;
539c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport javax.sip.RequestEvent;
549c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport javax.sip.ResponseEvent;
559c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport javax.sip.ServerTransaction;
569c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport javax.sip.SipException;
579c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport javax.sip.SipFactory;
589c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport javax.sip.SipListener;
599c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport javax.sip.SipProvider;
609c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport javax.sip.SipStack;
619c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport javax.sip.TimeoutEvent;
629c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport javax.sip.Transaction;
639c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport javax.sip.TransactionState;
649c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport javax.sip.TransactionTerminatedEvent;
659c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport javax.sip.TransactionUnavailableException;
669c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport javax.sip.address.Address;
679c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport javax.sip.address.SipURI;
689c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport javax.sip.header.CSeqHeader;
699c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport javax.sip.header.ExpiresHeader;
709c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport javax.sip.header.FromHeader;
719c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport javax.sip.header.MinExpiresHeader;
729c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport javax.sip.header.ViaHeader;
739c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport javax.sip.message.Message;
749c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport javax.sip.message.Request;
759c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport javax.sip.message.Response;
769c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
779c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan/**
789c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan * Manages {@link ISipSession}'s for a SIP account.
799c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan */
809c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanclass SipSessionGroup implements SipListener {
819c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private static final String TAG = "SipSession";
829c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private static final boolean DEBUG = true;
839c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private static final boolean DEBUG_PING = DEBUG && false;
849c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private static final String ANONYMOUS = "anonymous";
85b891d6f795fd5ba90455f8071b03404ff0a5b1aaChung-yih Wang    // Limit the size of thread pool to 1 for the order issue when the phone is
86b891d6f795fd5ba90455f8071b03404ff0a5b1aaChung-yih Wang    // waken up from sleep and there are many packets to be processed in the SIP
87b891d6f795fd5ba90455f8071b03404ff0a5b1aaChung-yih Wang    // stack. Note: The default thread pool size in NIST SIP stack is -1 which is
88b891d6f795fd5ba90455f8071b03404ff0a5b1aaChung-yih Wang    // unlimited.
89b891d6f795fd5ba90455f8071b03404ff0a5b1aaChung-yih Wang    private static final String THREAD_POOL_SIZE = "1";
909c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private static final int EXPIRY_TIME = 3600; // in seconds
919c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private static final int CANCEL_CALL_TIMER = 3; // in seconds
92acedadc1967457ac2f8981c67f884fd8c0ee853cHung-ying Tyan    private static final long WAKE_LOCK_HOLDING_TIME = 500; // in milliseconds
939c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
949c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private static final EventObject DEREGISTER = new EventObject("Deregister");
959c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private static final EventObject END_CALL = new EventObject("End call");
969c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private static final EventObject HOLD_CALL = new EventObject("Hold call");
979c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private static final EventObject CONTINUE_CALL
989c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            = new EventObject("Continue call");
999c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
1009c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private final SipProfile mLocalProfile;
1019c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private final String mPassword;
1029c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
1039c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private SipStack mSipStack;
1049c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private SipHelper mSipHelper;
1059c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
1069c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    // session that processes INVITE requests
1079c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private SipSessionImpl mCallReceiverSession;
1089c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private String mLocalIp;
1099c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
110acedadc1967457ac2f8981c67f884fd8c0ee853cHung-ying Tyan    private SipWakeLock mWakeLock;
111acedadc1967457ac2f8981c67f884fd8c0ee853cHung-ying Tyan
1129c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    // call-id-to-SipSession map
1139c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private Map<String, SipSessionImpl> mSessionMap =
1149c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            new HashMap<String, SipSessionImpl>();
1159c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
1169c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    /**
1179c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan     * @param myself the local profile with password crossed out
1189c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan     * @param password the password of the profile
1199c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan     * @throws IOException if cannot assign requested address
1209c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan     */
121acedadc1967457ac2f8981c67f884fd8c0ee853cHung-ying Tyan    public SipSessionGroup(String localIp, SipProfile myself, String password,
122acedadc1967457ac2f8981c67f884fd8c0ee853cHung-ying Tyan            SipWakeLock wakeLock) throws SipException, IOException {
1239c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        mLocalProfile = myself;
1249c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        mPassword = password;
125acedadc1967457ac2f8981c67f884fd8c0ee853cHung-ying Tyan        mWakeLock = wakeLock;
1269c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        reset(localIp);
1279c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
1289c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
1299c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    synchronized void reset(String localIp) throws SipException, IOException {
1309c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        mLocalIp = localIp;
1319c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        if (localIp == null) return;
1329c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
1339c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        SipProfile myself = mLocalProfile;
1349c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        SipFactory sipFactory = SipFactory.getInstance();
1359c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        Properties properties = new Properties();
1369c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        properties.setProperty("javax.sip.STACK_NAME", getStackName());
1377f5530b2b6b0183208a3280600de5e74fc1c672cChung-yih Wang        properties.setProperty(
1387f5530b2b6b0183208a3280600de5e74fc1c672cChung-yih Wang                "gov.nist.javax.sip.THREAD_POOL_SIZE", THREAD_POOL_SIZE);
1399c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        String outboundProxy = myself.getProxyAddress();
1409c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        if (!TextUtils.isEmpty(outboundProxy)) {
1419c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            Log.v(TAG, "outboundProxy is " + outboundProxy);
1429c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            properties.setProperty("javax.sip.OUTBOUND_PROXY", outboundProxy
1439c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    + ":" + myself.getPort() + "/" + myself.getProtocol());
1449c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
1459c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        SipStack stack = mSipStack = sipFactory.createSipStack(properties);
1469c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
1479c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        try {
1489c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            SipProvider provider = stack.createSipProvider(
1499c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    stack.createListeningPoint(localIp, allocateLocalPort(),
1509c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            myself.getProtocol()));
1519c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            provider.addSipListener(this);
1529c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mSipHelper = new SipHelper(stack, provider);
1539c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        } catch (InvalidArgumentException e) {
1549c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            throw new IOException(e.getMessage());
1559c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        } catch (TooManyListenersException e) {
1569c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            // must never happen
1579c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            throw new SipException("SipSessionGroup constructor", e);
1589c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
1599c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        Log.d(TAG, " start stack for " + myself.getUriString());
1609c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        stack.start();
1619c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
1629c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        mCallReceiverSession = null;
1639c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        mSessionMap.clear();
1649c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
1659c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
1669c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    synchronized void onConnectivityChanged() {
16727820a94e73622f647daf8678180abc541d3a092Hung-ying Tyan        SipSessionImpl[] ss = mSessionMap.values().toArray(
16827820a94e73622f647daf8678180abc541d3a092Hung-ying Tyan                    new SipSessionImpl[mSessionMap.size()]);
16927820a94e73622f647daf8678180abc541d3a092Hung-ying Tyan        // Iterate on the copied array instead of directly on mSessionMap to
17027820a94e73622f647daf8678180abc541d3a092Hung-ying Tyan        // avoid ConcurrentModificationException being thrown when
17127820a94e73622f647daf8678180abc541d3a092Hung-ying Tyan        // SipSessionImpl removes itself from mSessionMap in onError() in the
17227820a94e73622f647daf8678180abc541d3a092Hung-ying Tyan        // following loop.
17327820a94e73622f647daf8678180abc541d3a092Hung-ying Tyan        for (SipSessionImpl s : ss) {
1749c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            s.onError(SipErrorCode.DATA_CONNECTION_LOST,
1759c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    "data connection lost");
1769c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
1779c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
1789c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
1799c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    public SipProfile getLocalProfile() {
1809c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        return mLocalProfile;
1819c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
1829c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
1839c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    public String getLocalProfileUri() {
1849c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        return mLocalProfile.getUriString();
1859c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
1869c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
1879c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private String getStackName() {
1889c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        return "stack" + System.currentTimeMillis();
1899c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
1909c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
1919c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    public synchronized void close() {
1929c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        Log.d(TAG, " close stack for " + mLocalProfile.getUriString());
1930d603e36cb0d81f55bb90fc37784da3221f13920Hung-ying Tyan        onConnectivityChanged();
1949c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        mSessionMap.clear();
1959c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        closeToNotReceiveCalls();
1969c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        if (mSipStack != null) {
1979c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mSipStack.stop();
1989c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mSipStack = null;
1999c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mSipHelper = null;
2009c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
2019c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
2029c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
2039c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    public synchronized boolean isClosed() {
2049c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        return (mSipStack == null);
2059c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
2069c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
2079c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    // For internal use, require listener not to block in callbacks.
2089c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    public synchronized void openToReceiveCalls(ISipSessionListener listener) {
2099c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        if (mCallReceiverSession == null) {
2109c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mCallReceiverSession = new SipSessionCallReceiverImpl(listener);
2119c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        } else {
2129c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mCallReceiverSession.setListener(listener);
2139c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
2149c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
2159c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
2169c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    public synchronized void closeToNotReceiveCalls() {
2179c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        mCallReceiverSession = null;
2189c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
2199c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
2209c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    public ISipSession createSession(ISipSessionListener listener) {
2219c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        return (isClosed() ? null : new SipSessionImpl(listener));
2229c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
2239c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
2249c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private static int allocateLocalPort() throws SipException {
2259c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        try {
2269c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            DatagramSocket s = new DatagramSocket();
2279c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            int localPort = s.getLocalPort();
2289c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            s.close();
2299c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return localPort;
2309c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        } catch (IOException e) {
2319c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            throw new SipException("allocateLocalPort()", e);
2329c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
2339c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
2349c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
235ed3c0fbbd5457f43ff72cec31b8ab0bb3f7a0047Hung-ying Tyan    synchronized boolean containsSession(String callId) {
236ed3c0fbbd5457f43ff72cec31b8ab0bb3f7a0047Hung-ying Tyan        return mSessionMap.containsKey(callId);
237ed3c0fbbd5457f43ff72cec31b8ab0bb3f7a0047Hung-ying Tyan    }
238ed3c0fbbd5457f43ff72cec31b8ab0bb3f7a0047Hung-ying Tyan
2399c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private synchronized SipSessionImpl getSipSession(EventObject event) {
2409c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        String key = SipHelper.getCallId(event);
2419c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        SipSessionImpl session = mSessionMap.get(key);
2429c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        if ((session != null) && isLoggable(session)) {
2439c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            Log.d(TAG, "session key from event: " + key);
2449c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            Log.d(TAG, "active sessions:");
2459c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            for (String k : mSessionMap.keySet()) {
2469c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                Log.d(TAG, " ..." + k + ": " + mSessionMap.get(k));
2479c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
2489c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
2499c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        return ((session != null) ? session : mCallReceiverSession);
2509c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
2519c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
2529c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private synchronized void addSipSession(SipSessionImpl newSession) {
2539c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        removeSipSession(newSession);
2549c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        String key = newSession.getCallId();
2559c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        mSessionMap.put(key, newSession);
2569c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        if (isLoggable(newSession)) {
2579c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            Log.d(TAG, "+++  add a session with key:  '" + key + "'");
2589c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            for (String k : mSessionMap.keySet()) {
2599c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                Log.d(TAG, "  " + k + ": " + mSessionMap.get(k));
2609c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
2619c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
2629c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
2639c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
2649c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private synchronized void removeSipSession(SipSessionImpl session) {
2659c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        if (session == mCallReceiverSession) return;
2669c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        String key = session.getCallId();
2679c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        SipSessionImpl s = mSessionMap.remove(key);
2689c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        // sanity check
2699c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        if ((s != null) && (s != session)) {
2709c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            Log.w(TAG, "session " + session + " is not associated with key '"
2719c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    + key + "'");
2729c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mSessionMap.put(key, s);
2739c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            for (Map.Entry<String, SipSessionImpl> entry
2749c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    : mSessionMap.entrySet()) {
2759c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                if (entry.getValue() == s) {
2769c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    key = entry.getKey();
2779c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    mSessionMap.remove(key);
2789c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                }
2799c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
2809c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
2819c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
2829c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        if ((s != null) && isLoggable(s)) {
2839c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            Log.d(TAG, "remove session " + session + " @key '" + key + "'");
2849c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            for (String k : mSessionMap.keySet()) {
2859c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                Log.d(TAG, "  " + k + ": " + mSessionMap.get(k));
2869c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
2879c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
2889c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
2899c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
290acedadc1967457ac2f8981c67f884fd8c0ee853cHung-ying Tyan    public void processRequest(final RequestEvent event) {
291acedadc1967457ac2f8981c67f884fd8c0ee853cHung-ying Tyan        if (isRequestEvent(Request.INVITE, event)) {
292acedadc1967457ac2f8981c67f884fd8c0ee853cHung-ying Tyan            if (DEBUG) Log.d(TAG, "<<<<< got INVITE, thread:"
293acedadc1967457ac2f8981c67f884fd8c0ee853cHung-ying Tyan                    + Thread.currentThread());
294acedadc1967457ac2f8981c67f884fd8c0ee853cHung-ying Tyan            // Acquire a wake lock and keep it for WAKE_LOCK_HOLDING_TIME;
295acedadc1967457ac2f8981c67f884fd8c0ee853cHung-ying Tyan            // should be large enough to bring up the app.
296acedadc1967457ac2f8981c67f884fd8c0ee853cHung-ying Tyan            mWakeLock.acquire(WAKE_LOCK_HOLDING_TIME);
297acedadc1967457ac2f8981c67f884fd8c0ee853cHung-ying Tyan        }
2989c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        process(event);
2999c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
3009c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
3019c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    public void processResponse(ResponseEvent event) {
3029c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        process(event);
3039c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
3049c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
3059c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    public void processIOException(IOExceptionEvent event) {
3069c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        process(event);
3079c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
3089c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
3099c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    public void processTimeout(TimeoutEvent event) {
3109c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        process(event);
3119c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
3129c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
3139c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    public void processTransactionTerminated(TransactionTerminatedEvent event) {
3149c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        process(event);
3159c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
3169c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
3179c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    public void processDialogTerminated(DialogTerminatedEvent event) {
3189c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        process(event);
3199c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
3209c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
3219c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private synchronized void process(EventObject event) {
3229c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        SipSessionImpl session = getSipSession(event);
3239c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        try {
3249c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            boolean isLoggable = isLoggable(session, event);
3259c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            boolean processed = (session != null) && session.process(event);
3269c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (isLoggable && processed) {
3279c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                Log.d(TAG, "new state after: "
3289c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        + SipSession.State.toString(session.mState));
3299c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
3309c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        } catch (Throwable e) {
3319c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            Log.w(TAG, "event process error: " + event, e);
3329c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            session.onError(e);
3339c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
3349c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
3359c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
3369c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private String extractContent(Message message) {
3379c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        // Currently we do not support secure MIME bodies.
3389c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        byte[] bytes = message.getRawContent();
3399c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        if (bytes != null) {
3409c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            try {
3419c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                if (message instanceof SIPMessage) {
3429c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    return ((SIPMessage) message).getMessageContent();
3439c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                } else {
3449c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    return new String(bytes, "UTF-8");
3459c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                }
3469c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            } catch (UnsupportedEncodingException e) {
3479c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
3489c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
3499c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        return null;
3509c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
3519c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
3529c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private class SipSessionCallReceiverImpl extends SipSessionImpl {
3539c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public SipSessionCallReceiverImpl(ISipSessionListener listener) {
3549c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            super(listener);
3559c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
3569c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
3579c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public boolean process(EventObject evt) throws SipException {
3589c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (isLoggable(this, evt)) Log.d(TAG, " ~~~~~   " + this + ": "
3599c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    + SipSession.State.toString(mState) + ": processing "
3609c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    + log(evt));
3619c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (isRequestEvent(Request.INVITE, evt)) {
3629c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                RequestEvent event = (RequestEvent) evt;
3639c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                SipSessionImpl newSession = new SipSessionImpl(mProxy);
364d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                newSession.mState = SipSession.State.INCOMING_CALL;
3659c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                newSession.mServerTransaction = mSipHelper.sendRinging(event,
3669c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        generateTag());
3679c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                newSession.mDialog = newSession.mServerTransaction.getDialog();
3689c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                newSession.mInviteReceived = event;
3699c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                newSession.mPeerProfile = createPeerProfile(event.getRequest());
3709c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                newSession.mPeerSessionDescription =
3719c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        extractContent(event.getRequest());
3729c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                addSipSession(newSession);
3739c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mProxy.onRinging(newSession, newSession.mPeerProfile,
3749c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        newSession.mPeerSessionDescription);
3759c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                return true;
3769c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            } else if (isRequestEvent(Request.OPTIONS, evt)) {
3779c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mSipHelper.sendResponse((RequestEvent) evt, Response.OK);
3789c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                return true;
3799c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            } else {
3809c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                return false;
3819c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
3829c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
3839c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
3849c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
3859c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    class SipSessionImpl extends ISipSession.Stub {
3869c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        SipProfile mPeerProfile;
3879c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        SipSessionListenerProxy mProxy = new SipSessionListenerProxy();
3889c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        int mState = SipSession.State.READY_TO_CALL;
3899c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        RequestEvent mInviteReceived;
3909c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        Dialog mDialog;
3919c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        ServerTransaction mServerTransaction;
3929c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        ClientTransaction mClientTransaction;
3939c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        String mPeerSessionDescription;
3949c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        boolean mInCall;
3959c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        SessionTimer mTimer;
396fcc474636f42243cfce960158373b9c49636a556Hung-ying Tyan        int mAuthenticationRetryCount;
397fcc474636f42243cfce960158373b9c49636a556Hung-ying Tyan
398fcc474636f42243cfce960158373b9c49636a556Hung-ying Tyan        // for registration
399fcc474636f42243cfce960158373b9c49636a556Hung-ying Tyan        boolean mReRegisterFlag = false;
400fcc474636f42243cfce960158373b9c49636a556Hung-ying Tyan        int mRPort;
4019c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
4029c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        // lightweight timer
4039c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        class SessionTimer {
4049c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            private boolean mRunning = true;
4059c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
4069c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            void start(final int timeout) {
4079c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                new Thread(new Runnable() {
4089c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    public void run() {
4099c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        sleep(timeout);
4109c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        if (mRunning) timeout();
4119c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    }
4129c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                }, "SipSessionTimerThread").start();
4139c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
4149c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
4159c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            synchronized void cancel() {
4169c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mRunning = false;
4179c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                this.notify();
4189c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
4199c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
4209c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            private void timeout() {
4219c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                synchronized (SipSessionGroup.this) {
4229c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    onError(SipErrorCode.TIME_OUT, "Session timed out!");
4239c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                }
4249c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
4259c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
4269c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            private synchronized void sleep(int timeout) {
4279c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                try {
4289c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    this.wait(timeout * 1000);
4299c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                } catch (InterruptedException e) {
4309c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    Log.e(TAG, "session timer interrupted!");
4319c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                }
4329c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
4339c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
4349c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
4359c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public SipSessionImpl(ISipSessionListener listener) {
4369c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            setListener(listener);
4379c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
4389c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
4399c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        SipSessionImpl duplicate() {
4409c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return new SipSessionImpl(mProxy.getListener());
4419c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
4429c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
4439c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private void reset() {
4449c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mInCall = false;
4459c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            removeSipSession(this);
4469c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mPeerProfile = null;
4479c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mState = SipSession.State.READY_TO_CALL;
4489c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mInviteReceived = null;
449e8782e2fdfd256296a0d1fd2a1c3f98d58103eaaHung-ying Tyan            mPeerSessionDescription = null;
450fcc474636f42243cfce960158373b9c49636a556Hung-ying Tyan            mRPort = 0;
451fcc474636f42243cfce960158373b9c49636a556Hung-ying Tyan            mAuthenticationRetryCount = 0;
452e8782e2fdfd256296a0d1fd2a1c3f98d58103eaaHung-ying Tyan
453e8782e2fdfd256296a0d1fd2a1c3f98d58103eaaHung-ying Tyan            if (mDialog != null) mDialog.delete();
4549c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mDialog = null;
455e8782e2fdfd256296a0d1fd2a1c3f98d58103eaaHung-ying Tyan
456e8782e2fdfd256296a0d1fd2a1c3f98d58103eaaHung-ying Tyan            try {
457e8782e2fdfd256296a0d1fd2a1c3f98d58103eaaHung-ying Tyan                if (mServerTransaction != null) mServerTransaction.terminate();
458e8782e2fdfd256296a0d1fd2a1c3f98d58103eaaHung-ying Tyan            } catch (ObjectInUseException e) {
459e8782e2fdfd256296a0d1fd2a1c3f98d58103eaaHung-ying Tyan                // ignored
460e8782e2fdfd256296a0d1fd2a1c3f98d58103eaaHung-ying Tyan            }
4619c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mServerTransaction = null;
462e8782e2fdfd256296a0d1fd2a1c3f98d58103eaaHung-ying Tyan
463e8782e2fdfd256296a0d1fd2a1c3f98d58103eaaHung-ying Tyan            try {
464e8782e2fdfd256296a0d1fd2a1c3f98d58103eaaHung-ying Tyan                if (mClientTransaction != null) mClientTransaction.terminate();
465e8782e2fdfd256296a0d1fd2a1c3f98d58103eaaHung-ying Tyan            } catch (ObjectInUseException e) {
466e8782e2fdfd256296a0d1fd2a1c3f98d58103eaaHung-ying Tyan                // ignored
467e8782e2fdfd256296a0d1fd2a1c3f98d58103eaaHung-ying Tyan            }
4689c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mClientTransaction = null;
4699c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
4709c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            cancelSessionTimer();
4719c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
4729c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
4739c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public boolean isInCall() {
4749c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return mInCall;
4759c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
4769c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
4779c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public String getLocalIp() {
4789c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return mLocalIp;
4799c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
4809c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
4819c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public SipProfile getLocalProfile() {
4829c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return mLocalProfile;
4839c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
4849c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
4859c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public SipProfile getPeerProfile() {
4869c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return mPeerProfile;
4879c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
4889c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
4899c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public String getCallId() {
4909c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return SipHelper.getCallId(getTransaction());
4919c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
4929c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
4939c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private Transaction getTransaction() {
4949c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (mClientTransaction != null) return mClientTransaction;
4959c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (mServerTransaction != null) return mServerTransaction;
4969c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return null;
4979c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
4989c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
4999c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public int getState() {
5009c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return mState;
5019c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
5029c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
5039c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public void setListener(ISipSessionListener listener) {
5049c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mProxy.setListener((listener instanceof SipSessionListenerProxy)
5059c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    ? ((SipSessionListenerProxy) listener).getListener()
5069c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    : listener);
5079c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
5089c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
5099c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        // process the command in a new thread
5109c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private void doCommandAsync(final EventObject command) {
5119c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            new Thread(new Runnable() {
5129c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    public void run() {
5139c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        try {
5149c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            processCommand(command);
515e26eb3274a65c41a6a30bdace1818c5629cca1c8Hung-ying Tyan                        } catch (Throwable e) {
5169c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            Log.w(TAG, "command error: " + command, e);
5179c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            onError(e);
5189c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        }
5199c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    }
5209c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }, "SipSessionAsyncCmdThread").start();
5219c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
5229c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
5239c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public void makeCall(SipProfile peerProfile, String sessionDescription,
5249c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                int timeout) {
5259c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            doCommandAsync(new MakeCallCommand(peerProfile, sessionDescription,
5269c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    timeout));
5279c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
5289c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
5299c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public void answerCall(String sessionDescription, int timeout) {
530d47a47a131a8e7f80b934aec4cc7a10fd9fe1094Hung-ying Tyan            synchronized (SipSessionGroup.this) {
531d47a47a131a8e7f80b934aec4cc7a10fd9fe1094Hung-ying Tyan                if (mPeerProfile == null) return;
532d47a47a131a8e7f80b934aec4cc7a10fd9fe1094Hung-ying Tyan                try {
533d47a47a131a8e7f80b934aec4cc7a10fd9fe1094Hung-ying Tyan                    processCommand(new MakeCallCommand(mPeerProfile,
534d47a47a131a8e7f80b934aec4cc7a10fd9fe1094Hung-ying Tyan                            sessionDescription, timeout));
535d47a47a131a8e7f80b934aec4cc7a10fd9fe1094Hung-ying Tyan                } catch (SipException e) {
536d47a47a131a8e7f80b934aec4cc7a10fd9fe1094Hung-ying Tyan                    onError(e);
537d47a47a131a8e7f80b934aec4cc7a10fd9fe1094Hung-ying Tyan                }
5389c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
5399c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
5409c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
5419c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public void endCall() {
5429c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            doCommandAsync(END_CALL);
5439c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
5449c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
5459c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public void changeCall(String sessionDescription, int timeout) {
546d47a47a131a8e7f80b934aec4cc7a10fd9fe1094Hung-ying Tyan            synchronized (SipSessionGroup.this) {
547d47a47a131a8e7f80b934aec4cc7a10fd9fe1094Hung-ying Tyan                if (mPeerProfile == null) return;
548d47a47a131a8e7f80b934aec4cc7a10fd9fe1094Hung-ying Tyan                doCommandAsync(new MakeCallCommand(mPeerProfile,
549d47a47a131a8e7f80b934aec4cc7a10fd9fe1094Hung-ying Tyan                        sessionDescription, timeout));
550d47a47a131a8e7f80b934aec4cc7a10fd9fe1094Hung-ying Tyan            }
5519c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
5529c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
5539c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public void register(int duration) {
5549c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            doCommandAsync(new RegisterCommand(duration));
5559c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
5569c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
5579c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public void unregister() {
5589c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            doCommandAsync(DEREGISTER);
5599c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
5609c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
5619c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public boolean isReRegisterRequired() {
5629c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return mReRegisterFlag;
5639c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
5649c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
5659c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public void clearReRegisterRequired() {
5669c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mReRegisterFlag = false;
5679c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
5689c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
5699c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public void sendKeepAlive() {
5709c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mState = SipSession.State.PINGING;
5719c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            try {
5729c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                processCommand(new OptionsCommand());
573257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                for (int i = 0; i < 15; i++) {
574257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                    if (SipSession.State.PINGING != mState) break;
575257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                    Thread.sleep(200);
576257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                }
577257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                if (SipSession.State.PINGING == mState) {
578257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                    // FIXME: what to do if server doesn't respond
579257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                    reset();
580257c7e2b4193bff3793fcedd8b34b7fec2b1019bHung-ying Tyan                    if (DEBUG) Log.w(TAG, "no response from ping");
5819c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                }
5829c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            } catch (SipException e) {
5839c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                Log.e(TAG, "sendKeepAlive failed", e);
5849c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            } catch (InterruptedException e) {
5859c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                Log.e(TAG, "sendKeepAlive interrupted", e);
5869c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
5879c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
5889c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
5899c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private void processCommand(EventObject command) throws SipException {
590ed3c0fbbd5457f43ff72cec31b8ab0bb3f7a0047Hung-ying Tyan            if (isLoggable(command)) Log.d(TAG, "process cmd: " + command);
5919c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (!process(command)) {
5929c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                onError(SipErrorCode.IN_PROGRESS,
5939c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        "cannot initiate a new transaction to execute: "
5949c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        + command);
5959c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
5969c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
5979c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
5989c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        protected String generateTag() {
5999c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            // 32-bit randomness
6009c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return String.valueOf((long) (Math.random() * 0x100000000L));
6019c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
6029c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
6039c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public String toString() {
6049c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            try {
6059c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                String s = super.toString();
6069c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                return s.substring(s.indexOf("@")) + ":"
6079c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        + SipSession.State.toString(mState);
6089c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            } catch (Throwable e) {
6099c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                return super.toString();
6109c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
6119c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
6129c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
6139c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public boolean process(EventObject evt) throws SipException {
6149c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (isLoggable(this, evt)) Log.d(TAG, " ~~~~~   " + this + ": "
6159c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    + SipSession.State.toString(mState) + ": processing "
6169c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    + log(evt));
6179c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            synchronized (SipSessionGroup.this) {
6189c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                if (isClosed()) return false;
6199c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
6209c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                Dialog dialog = null;
6219c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                if (evt instanceof RequestEvent) {
6229c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    dialog = ((RequestEvent) evt).getDialog();
6239c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                } else if (evt instanceof ResponseEvent) {
6249c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    dialog = ((ResponseEvent) evt).getDialog();
6259c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                }
6269c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                if (dialog != null) mDialog = dialog;
6279c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
6289c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                boolean processed;
6299c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
6309c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                switch (mState) {
6319c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                case SipSession.State.REGISTERING:
6329c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                case SipSession.State.DEREGISTERING:
6339c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    processed = registeringToReady(evt);
6349c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    break;
6359c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                case SipSession.State.PINGING:
6369c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    processed = keepAliveProcess(evt);
6379c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    break;
6389c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                case SipSession.State.READY_TO_CALL:
6399c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    processed = readyForCall(evt);
6409c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    break;
6419c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                case SipSession.State.INCOMING_CALL:
6429c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    processed = incomingCall(evt);
6439c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    break;
6449c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                case SipSession.State.INCOMING_CALL_ANSWERING:
6459c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    processed = incomingCallToInCall(evt);
6469c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    break;
6479c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                case SipSession.State.OUTGOING_CALL:
6489c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                case SipSession.State.OUTGOING_CALL_RING_BACK:
6499c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    processed = outgoingCall(evt);
6509c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    break;
6519c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                case SipSession.State.OUTGOING_CALL_CANCELING:
6529c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    processed = outgoingCallToReady(evt);
6539c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    break;
6549c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                case SipSession.State.IN_CALL:
6559c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    processed = inCall(evt);
6569c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    break;
6579c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                default:
6589c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    processed = false;
6599c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                }
6609c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                return (processed || processExceptions(evt));
6619c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
6629c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
6639c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
6649c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private boolean processExceptions(EventObject evt) throws SipException {
6659c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (isRequestEvent(Request.BYE, evt)) {
6669c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                // terminate the call whenever a BYE is received
6679c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mSipHelper.sendResponse((RequestEvent) evt, Response.OK);
6689c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                endCallNormally();
6699c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                return true;
6709c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            } else if (isRequestEvent(Request.CANCEL, evt)) {
6719c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mSipHelper.sendResponse((RequestEvent) evt,
6729c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        Response.CALL_OR_TRANSACTION_DOES_NOT_EXIST);
6739c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                return true;
6749c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            } else if (evt instanceof TransactionTerminatedEvent) {
6759c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                if (isCurrentTransaction((TransactionTerminatedEvent) evt)) {
6769c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    if (evt instanceof TimeoutEvent) {
6779c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        processTimeout((TimeoutEvent) evt);
6789c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    } else {
6799c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        processTransactionTerminated(
6809c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                                (TransactionTerminatedEvent) evt);
6819c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    }
6829c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    return true;
6839c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                }
6849c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            } else if (isRequestEvent(Request.OPTIONS, evt)) {
6859c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mSipHelper.sendResponse((RequestEvent) evt, Response.OK);
6869c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                return true;
6879c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            } else if (evt instanceof DialogTerminatedEvent) {
6889c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                processDialogTerminated((DialogTerminatedEvent) evt);
6899c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                return true;
6909c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
6919c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return false;
6929c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
6939c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
6949c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private void processDialogTerminated(DialogTerminatedEvent event) {
6959c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (mDialog == event.getDialog()) {
6969c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                onError(new SipException("dialog terminated"));
6979c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            } else {
6989c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                Log.d(TAG, "not the current dialog; current=" + mDialog
6999c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        + ", terminated=" + event.getDialog());
7009c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
7019c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
7029c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
7039c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private boolean isCurrentTransaction(TransactionTerminatedEvent event) {
7049c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            Transaction current = event.isServerTransaction()
7059c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    ? mServerTransaction
7069c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    : mClientTransaction;
7079c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            Transaction target = event.isServerTransaction()
7089c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    ? event.getServerTransaction()
7099c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    : event.getClientTransaction();
7109c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
7119c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if ((current != target) && (mState != SipSession.State.PINGING)) {
7129c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                Log.d(TAG, "not the current transaction; current="
7139c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        + toString(current) + ", target=" + toString(target));
7149c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                return false;
7159c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            } else if (current != null) {
7169c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                Log.d(TAG, "transaction terminated: " + toString(current));
7179c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                return true;
7189c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            } else {
7199c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                // no transaction; shouldn't be here; ignored
7209c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                return true;
7219c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
7229c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
7239c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
7249c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private String toString(Transaction transaction) {
7259c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (transaction == null) return "null";
7269c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            Request request = transaction.getRequest();
7279c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            Dialog dialog = transaction.getDialog();
7289c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            CSeqHeader cseq = (CSeqHeader) request.getHeader(CSeqHeader.NAME);
7299c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return String.format("req=%s,%s,s=%s,ds=%s,", request.getMethod(),
7309c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    cseq.getSeqNumber(), transaction.getState(),
7319c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    ((dialog == null) ? "-" : dialog.getState()));
7329c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
7339c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
7349c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private void processTransactionTerminated(
7359c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                TransactionTerminatedEvent event) {
7369c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            switch (mState) {
7379c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                case SipSession.State.IN_CALL:
7389c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                case SipSession.State.READY_TO_CALL:
7399c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    Log.d(TAG, "Transaction terminated; do nothing");
7409c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    break;
7419c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                default:
7429c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    Log.d(TAG, "Transaction terminated early: " + this);
7439c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    onError(SipErrorCode.TRANSACTION_TERMINTED,
7449c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            "transaction terminated");
7459c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
7469c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
7479c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
7489c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private void processTimeout(TimeoutEvent event) {
7499c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            Log.d(TAG, "processing Timeout...");
7509c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            switch (mState) {
7519c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                case SipSession.State.REGISTERING:
7529c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                case SipSession.State.DEREGISTERING:
7539c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    reset();
7549c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    mProxy.onRegistrationTimeout(this);
7559c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    break;
7569c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                case SipSession.State.INCOMING_CALL:
7579c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                case SipSession.State.INCOMING_CALL_ANSWERING:
7589c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                case SipSession.State.OUTGOING_CALL:
7599c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                case SipSession.State.OUTGOING_CALL_CANCELING:
7609c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    onError(SipErrorCode.TIME_OUT, event.toString());
7619c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    break;
7629c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                case SipSession.State.PINGING:
7639c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    reset();
7649c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    mReRegisterFlag = true;
7659c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    break;
7669c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
7679c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                default:
7689c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    Log.d(TAG, "   do nothing");
7699c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    break;
7709c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
7719c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
7729c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
7739c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private int getExpiryTime(Response response) {
7749c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            int expires = EXPIRY_TIME;
7759c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            ExpiresHeader expiresHeader = (ExpiresHeader)
7769c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    response.getHeader(ExpiresHeader.NAME);
7779c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (expiresHeader != null) expires = expiresHeader.getExpires();
7789c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            expiresHeader = (ExpiresHeader)
7799c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    response.getHeader(MinExpiresHeader.NAME);
7809c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (expiresHeader != null) {
7819c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                expires = Math.max(expires, expiresHeader.getExpires());
7829c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
7839c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return expires;
7849c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
7859c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
7869c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private boolean keepAliveProcess(EventObject evt) throws SipException {
7879c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (evt instanceof OptionsCommand) {
7889c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mClientTransaction = mSipHelper.sendKeepAlive(mLocalProfile,
7899c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        generateTag());
7909c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mDialog = mClientTransaction.getDialog();
7919c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                addSipSession(this);
7929c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                return true;
7939c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            } else if (evt instanceof ResponseEvent) {
7949c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                return parseOptionsResult(evt);
7959c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
7969c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return false;
7979c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
7989c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
7999c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private boolean parseOptionsResult(EventObject evt) {
8009c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (expectResponse(Request.OPTIONS, evt)) {
8019c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                ResponseEvent event = (ResponseEvent) evt;
8029c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                int rPort = getRPortFromResponse(event.getResponse());
8039c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                if (rPort != -1) {
8049c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    if (mRPort == 0) mRPort = rPort;
8059c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    if (mRPort != rPort) {
8069c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        mReRegisterFlag = true;
8079c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        if (DEBUG) Log.w(TAG, String.format(
8089c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                                "rport is changed: %d <> %d", mRPort, rPort));
8099c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        mRPort = rPort;
8109c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    } else {
8119c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        if (DEBUG_PING) Log.w(TAG, "rport is the same: " + rPort);
8129c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    }
8139c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                } else {
8149c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    if (DEBUG) Log.w(TAG, "peer did not respond rport");
8159c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                }
8169c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                reset();
8179c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                return true;
8189c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
8199c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return false;
8209c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
8219c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
8229c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private int getRPortFromResponse(Response response) {
8239c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            ViaHeader viaHeader = (ViaHeader)(response.getHeader(
8249c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    SIPHeaderNames.VIA));
8259c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return (viaHeader == null) ? -1 : viaHeader.getRPort();
8269c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
8279c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
8289c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private boolean registeringToReady(EventObject evt)
8299c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                throws SipException {
8309c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (expectResponse(Request.REGISTER, evt)) {
8319c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                ResponseEvent event = (ResponseEvent) evt;
8329c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                Response response = event.getResponse();
8339c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
8349c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                int statusCode = response.getStatusCode();
8359c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                switch (statusCode) {
8369c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                case Response.OK:
8379c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    int state = mState;
8389c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    onRegistrationDone((state == SipSession.State.REGISTERING)
8399c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            ? getExpiryTime(((ResponseEvent) evt).getResponse())
8409c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            : -1);
8419c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    return true;
8429c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                case Response.UNAUTHORIZED:
8439c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                case Response.PROXY_AUTHENTICATION_REQUIRED:
844fcc474636f42243cfce960158373b9c49636a556Hung-ying Tyan                    handleAuthentication(event);
8459c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    return true;
8469c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                default:
8479c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    if (statusCode >= 500) {
8489c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        onRegistrationFailed(response);
8499c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        return true;
8509c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    }
8519c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                }
8529c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
8539c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return false;
8549c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
8559c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
8569c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private boolean handleAuthentication(ResponseEvent event)
8579c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                throws SipException {
8589c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            Response response = event.getResponse();
8599c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            String nonce = getNonceFromResponse(response);
860fcc474636f42243cfce960158373b9c49636a556Hung-ying Tyan            if (nonce == null) {
861fcc474636f42243cfce960158373b9c49636a556Hung-ying Tyan                onError(SipErrorCode.SERVER_ERROR,
862fcc474636f42243cfce960158373b9c49636a556Hung-ying Tyan                        "server does not provide challenge");
8639c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                return false;
864fcc474636f42243cfce960158373b9c49636a556Hung-ying Tyan            } else if (mAuthenticationRetryCount < 2) {
8659c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mClientTransaction = mSipHelper.handleChallenge(
8669c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        event, getAccountManager());
8679c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mDialog = mClientTransaction.getDialog();
868fcc474636f42243cfce960158373b9c49636a556Hung-ying Tyan                mAuthenticationRetryCount++;
869fcc474636f42243cfce960158373b9c49636a556Hung-ying Tyan                if (isLoggable(this, event)) {
870fcc474636f42243cfce960158373b9c49636a556Hung-ying Tyan                    Log.d(TAG, "   authentication retry count="
871fcc474636f42243cfce960158373b9c49636a556Hung-ying Tyan                            + mAuthenticationRetryCount);
872fcc474636f42243cfce960158373b9c49636a556Hung-ying Tyan                }
8739c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                return true;
874fcc474636f42243cfce960158373b9c49636a556Hung-ying Tyan            } else {
875fcc474636f42243cfce960158373b9c49636a556Hung-ying Tyan                onError(SipErrorCode.INVALID_CREDENTIALS,
876fcc474636f42243cfce960158373b9c49636a556Hung-ying Tyan                        "incorrect username or password");
877fcc474636f42243cfce960158373b9c49636a556Hung-ying Tyan                return false;
8789c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
8799c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
8809c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
8819c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private boolean crossDomainAuthenticationRequired(Response response) {
8829c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            String realm = getRealmFromResponse(response);
8839c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (realm == null) realm = "";
8849c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return !mLocalProfile.getSipDomain().trim().equals(realm.trim());
8859c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
8869c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
8879c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private AccountManager getAccountManager() {
8889c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return new AccountManager() {
8899c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                public UserCredentials getCredentials(ClientTransaction
8909c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        challengedTransaction, String realm) {
8919c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    return new UserCredentials() {
8929c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        public String getUserName() {
8939c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            return mLocalProfile.getUserName();
8949c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        }
8959c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
8969c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        public String getPassword() {
8979c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            return mPassword;
8989c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        }
8999c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
9009c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        public String getSipDomain() {
9019c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            return mLocalProfile.getSipDomain();
9029c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        }
9039c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    };
9049c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                }
9059c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            };
9069c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
9079c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
9089c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private String getRealmFromResponse(Response response) {
9099c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            WWWAuthenticate wwwAuth = (WWWAuthenticate)response.getHeader(
9109c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    SIPHeaderNames.WWW_AUTHENTICATE);
9119c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (wwwAuth != null) return wwwAuth.getRealm();
9129c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            ProxyAuthenticate proxyAuth = (ProxyAuthenticate)response.getHeader(
9139c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    SIPHeaderNames.PROXY_AUTHENTICATE);
9149c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return (proxyAuth == null) ? null : proxyAuth.getRealm();
9159c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
9169c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
9179c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private String getNonceFromResponse(Response response) {
9189c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            WWWAuthenticate wwwAuth = (WWWAuthenticate)response.getHeader(
9199c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    SIPHeaderNames.WWW_AUTHENTICATE);
9209c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (wwwAuth != null) return wwwAuth.getNonce();
9219c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            ProxyAuthenticate proxyAuth = (ProxyAuthenticate)response.getHeader(
9229c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    SIPHeaderNames.PROXY_AUTHENTICATE);
9239c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return (proxyAuth == null) ? null : proxyAuth.getNonce();
9249c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
9259c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
9269c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private boolean readyForCall(EventObject evt) throws SipException {
9279c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            // expect MakeCallCommand, RegisterCommand, DEREGISTER
9289c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (evt instanceof MakeCallCommand) {
929d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                mState = SipSession.State.OUTGOING_CALL;
9309c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                MakeCallCommand cmd = (MakeCallCommand) evt;
9319c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mPeerProfile = cmd.getPeerProfile();
9329c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mClientTransaction = mSipHelper.sendInvite(mLocalProfile,
9339c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        mPeerProfile, cmd.getSessionDescription(),
9349c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        generateTag());
9359c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mDialog = mClientTransaction.getDialog();
9369c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                addSipSession(this);
9379c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                startSessionTimer(cmd.getTimeout());
938e8782e2fdfd256296a0d1fd2a1c3f98d58103eaaHung-ying Tyan                mProxy.onCalling(this);
9399c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                return true;
9409c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            } else if (evt instanceof RegisterCommand) {
941d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                mState = SipSession.State.REGISTERING;
9429c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                int duration = ((RegisterCommand) evt).getDuration();
9439c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mClientTransaction = mSipHelper.sendRegister(mLocalProfile,
9449c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        generateTag(), duration);
9459c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mDialog = mClientTransaction.getDialog();
9469c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                addSipSession(this);
9479c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mProxy.onRegistering(this);
9489c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                return true;
9499c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            } else if (DEREGISTER == evt) {
950d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                mState = SipSession.State.DEREGISTERING;
9519c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mClientTransaction = mSipHelper.sendRegister(mLocalProfile,
9529c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        generateTag(), 0);
9539c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mDialog = mClientTransaction.getDialog();
9549c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                addSipSession(this);
9559c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mProxy.onRegistering(this);
9569c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                return true;
9579c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
9589c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return false;
9599c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
9609c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
9619c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private boolean incomingCall(EventObject evt) throws SipException {
9629c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            // expect MakeCallCommand(answering) , END_CALL cmd , Cancel
9639c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (evt instanceof MakeCallCommand) {
9649c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                // answer call
965d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                mState = SipSession.State.INCOMING_CALL_ANSWERING;
9669c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mServerTransaction = mSipHelper.sendInviteOk(mInviteReceived,
9679c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        mLocalProfile,
9689c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        ((MakeCallCommand) evt).getSessionDescription(),
9699c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        mServerTransaction);
9709c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                startSessionTimer(((MakeCallCommand) evt).getTimeout());
9719c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                return true;
9729c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            } else if (END_CALL == evt) {
9739c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mSipHelper.sendInviteBusyHere(mInviteReceived,
9749c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        mServerTransaction);
9759c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                endCallNormally();
9769c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                return true;
9779c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            } else if (isRequestEvent(Request.CANCEL, evt)) {
9789c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                RequestEvent event = (RequestEvent) evt;
9799c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mSipHelper.sendResponse(event, Response.OK);
9809c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mSipHelper.sendInviteRequestTerminated(
9819c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        mInviteReceived.getRequest(), mServerTransaction);
9829c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                endCallNormally();
9839c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                return true;
9849c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
9859c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return false;
9869c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
9879c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
9889c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private boolean incomingCallToInCall(EventObject evt)
9899c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                throws SipException {
9909c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            // expect ACK, CANCEL request
9919c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (isRequestEvent(Request.ACK, evt)) {
9929c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                establishCall();
9939c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                return true;
9949c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            } else if (isRequestEvent(Request.CANCEL, evt)) {
9959c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                // http://tools.ietf.org/html/rfc3261#section-9.2
9969c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                // Final response has been sent; do nothing here.
9979c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                return true;
9989c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
9999c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return false;
10009c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
10019c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
10029c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private boolean outgoingCall(EventObject evt) throws SipException {
10039c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (expectResponse(Request.INVITE, evt)) {
10049c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                ResponseEvent event = (ResponseEvent) evt;
10059c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                Response response = event.getResponse();
10069c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
10079c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                int statusCode = response.getStatusCode();
10089c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                switch (statusCode) {
10099c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                case Response.RINGING:
10106ca8ec4793540c6c0ad83574aa52e316a8ed17b4Hung-ying Tyan                case Response.CALL_IS_BEING_FORWARDED:
10116ca8ec4793540c6c0ad83574aa52e316a8ed17b4Hung-ying Tyan                case Response.QUEUED:
10126ca8ec4793540c6c0ad83574aa52e316a8ed17b4Hung-ying Tyan                case Response.SESSION_PROGRESS:
10136ca8ec4793540c6c0ad83574aa52e316a8ed17b4Hung-ying Tyan                    // feedback any provisional responses (except TRYING) as
10146ca8ec4793540c6c0ad83574aa52e316a8ed17b4Hung-ying Tyan                    // ring back for better UX
10159c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    if (mState == SipSession.State.OUTGOING_CALL) {
10169c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        mState = SipSession.State.OUTGOING_CALL_RING_BACK;
10179c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        cancelSessionTimer();
1018e8782e2fdfd256296a0d1fd2a1c3f98d58103eaaHung-ying Tyan                        mProxy.onRingingBack(this);
10199c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    }
10209c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    return true;
10219c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                case Response.OK:
10229c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    mSipHelper.sendInviteAck(event, mDialog);
10239c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    mPeerSessionDescription = extractContent(response);
10249c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    establishCall();
10259c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    return true;
10269c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                case Response.UNAUTHORIZED:
10279c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                case Response.PROXY_AUTHENTICATION_REQUIRED:
10289c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    if (crossDomainAuthenticationRequired(response)) {
10299c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        onError(SipErrorCode.CROSS_DOMAIN_AUTHENTICATION,
10309c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                                getRealmFromResponse(response));
10319c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    } else if (handleAuthentication(event)) {
10329c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        addSipSession(this);
10339c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    }
10349c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    return true;
10359c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                case Response.REQUEST_PENDING:
10369c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    // TODO:
10379c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    // rfc3261#section-14.1; re-schedule invite
10389c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    return true;
10399c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                default:
10409c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    if (statusCode >= 400) {
10419c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        // error: an ack is sent automatically by the stack
10429c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        onError(response);
10439c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        return true;
10449c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    } else if (statusCode >= 300) {
10459c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        // TODO: handle 3xx (redirect)
10469c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    } else {
10479c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        return true;
10489c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    }
10499c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                }
10509c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                return false;
10519c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            } else if (END_CALL == evt) {
10529c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                // RFC says that UA should not send out cancel when no
10539c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                // response comes back yet. We are cheating for not checking
10549c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                // response.
10559c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mState = SipSession.State.OUTGOING_CALL_CANCELING;
1056d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                mSipHelper.sendCancel(mClientTransaction);
10579c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                startSessionTimer(CANCEL_CALL_TIMER);
10589c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                return true;
1059ed3c0fbbd5457f43ff72cec31b8ab0bb3f7a0047Hung-ying Tyan            } else if (isRequestEvent(Request.INVITE, evt)) {
1060ed3c0fbbd5457f43ff72cec31b8ab0bb3f7a0047Hung-ying Tyan                // Call self? Send BUSY HERE so server may redirect the call to
1061ed3c0fbbd5457f43ff72cec31b8ab0bb3f7a0047Hung-ying Tyan                // voice mailbox.
1062ed3c0fbbd5457f43ff72cec31b8ab0bb3f7a0047Hung-ying Tyan                RequestEvent event = (RequestEvent) evt;
1063ed3c0fbbd5457f43ff72cec31b8ab0bb3f7a0047Hung-ying Tyan                mSipHelper.sendInviteBusyHere(event,
1064ed3c0fbbd5457f43ff72cec31b8ab0bb3f7a0047Hung-ying Tyan                        event.getServerTransaction());
1065ed3c0fbbd5457f43ff72cec31b8ab0bb3f7a0047Hung-ying Tyan                return true;
10669c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
10679c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return false;
10689c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
10699c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
10709c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private boolean outgoingCallToReady(EventObject evt)
10719c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                throws SipException {
10729c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (evt instanceof ResponseEvent) {
10739c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                ResponseEvent event = (ResponseEvent) evt;
10749c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                Response response = event.getResponse();
10759c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                int statusCode = response.getStatusCode();
10769c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                if (expectResponse(Request.CANCEL, evt)) {
10779c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    if (statusCode == Response.OK) {
10789c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        // do nothing; wait for REQUEST_TERMINATED
10799c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        return true;
10809c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    }
10819c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                } else if (expectResponse(Request.INVITE, evt)) {
10829c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    switch (statusCode) {
10839c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        case Response.OK:
10849c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            outgoingCall(evt); // abort Cancel
10859c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            return true;
10869c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        case Response.REQUEST_TERMINATED:
10879c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            endCallNormally();
10889c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            return true;
10899c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    }
10909c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                } else {
10919c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    return false;
10929c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                }
10939c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
10949c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                if (statusCode >= 400) {
10959c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    onError(response);
10969c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    return true;
10979c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                }
10989c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            } else if (evt instanceof TransactionTerminatedEvent) {
10999c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                // rfc3261#section-14.1:
11009c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                // if re-invite gets timed out, terminate the dialog; but
11019c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                // re-invite is not reliable, just let it go and pretend
11029c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                // nothing happened.
11039c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                onError(new SipException("timed out"));
11049c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
11059c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return false;
11069c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
11079c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
11089c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private boolean inCall(EventObject evt) throws SipException {
11099c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            // expect END_CALL cmd, BYE request, hold call (MakeCallCommand)
11109c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            // OK retransmission is handled in SipStack
11119c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (END_CALL == evt) {
11129c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                // rfc3261#section-15.1.1
11139c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mSipHelper.sendBye(mDialog);
11149c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                endCallNormally();
11159c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                return true;
11169c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            } else if (isRequestEvent(Request.INVITE, evt)) {
11179c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                // got Re-INVITE
11189c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mState = SipSession.State.INCOMING_CALL;
1119d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                RequestEvent event = mInviteReceived = (RequestEvent) evt;
11209c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mPeerSessionDescription = extractContent(event.getRequest());
11219c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mServerTransaction = null;
11229c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mProxy.onRinging(this, mPeerProfile, mPeerSessionDescription);
11239c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                return true;
11249c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            } else if (isRequestEvent(Request.BYE, evt)) {
11259c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mSipHelper.sendResponse((RequestEvent) evt, Response.OK);
11269c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                endCallNormally();
11279c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                return true;
11289c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            } else if (evt instanceof MakeCallCommand) {
11299c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                // to change call
1130d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                mState = SipSession.State.OUTGOING_CALL;
11319c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mClientTransaction = mSipHelper.sendReinvite(mDialog,
11329c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        ((MakeCallCommand) evt).getSessionDescription());
11339c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                startSessionTimer(((MakeCallCommand) evt).getTimeout());
11349c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                return true;
11359c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
11369c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return false;
11379c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
11389c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
11399c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        // timeout in seconds
11409c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private void startSessionTimer(int timeout) {
11419c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (timeout > 0) {
11429c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mTimer = new SessionTimer();
11439c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mTimer.start(timeout);
11449c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
11459c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
11469c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
11479c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private void cancelSessionTimer() {
11489c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (mTimer != null) {
11499c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mTimer.cancel();
11509c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mTimer = null;
11519c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
11529c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
11539c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
11549c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private String createErrorMessage(Response response) {
11553b9456929376a047ef38b5ddb27fed9b11664306Hung-ying Tyan            return String.format("%s (%d)", response.getReasonPhrase(),
11563b9456929376a047ef38b5ddb27fed9b11664306Hung-ying Tyan                    response.getStatusCode());
11579c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
11589c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
11599c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private void establishCall() {
11609c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mState = SipSession.State.IN_CALL;
11619c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mInCall = true;
11629c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            cancelSessionTimer();
11639c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mProxy.onCallEstablished(this, mPeerSessionDescription);
11649c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
11659c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
11669c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private void endCallNormally() {
11679c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            reset();
11689c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mProxy.onCallEnded(this);
11699c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
11709c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
11719c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private void endCallOnError(int errorCode, String message) {
11729c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            reset();
11739c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mProxy.onError(this, errorCode, message);
11749c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
11759c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
11769c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private void endCallOnBusy() {
11779c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            reset();
11789c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mProxy.onCallBusy(this);
11799c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
11809c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
11819c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private void onError(int errorCode, String message) {
11829c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            cancelSessionTimer();
11839c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            switch (mState) {
11849c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                case SipSession.State.REGISTERING:
11859c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                case SipSession.State.DEREGISTERING:
11869c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    onRegistrationFailed(errorCode, message);
11879c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    break;
11889c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                default:
1189bddf530f0bbd2917d98c3fd0f11920c2b2473154Hung-ying Tyan                    endCallOnError(errorCode, message);
11909c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
11919c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
11929c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
11939c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
11949c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private void onError(Throwable exception) {
11959c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            exception = getRootCause(exception);
11969c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            onError(getErrorCode(exception), exception.toString());
11979c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
11989c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
11999c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private void onError(Response response) {
12009c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            int statusCode = response.getStatusCode();
12019c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (!mInCall && (statusCode == Response.BUSY_HERE)) {
12029c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                endCallOnBusy();
12039c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            } else {
12049c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                onError(getErrorCode(statusCode), createErrorMessage(response));
12059c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
12069c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
12079c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
12089c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private int getErrorCode(int responseStatusCode) {
12099c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            switch (responseStatusCode) {
12109c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                case Response.TEMPORARILY_UNAVAILABLE:
12119c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                case Response.FORBIDDEN:
12129c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                case Response.GONE:
12139c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                case Response.NOT_FOUND:
12149c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                case Response.NOT_ACCEPTABLE:
12159c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                case Response.NOT_ACCEPTABLE_HERE:
12169c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    return SipErrorCode.PEER_NOT_REACHABLE;
12179c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
12189c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                case Response.REQUEST_URI_TOO_LONG:
12199c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                case Response.ADDRESS_INCOMPLETE:
12209c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                case Response.AMBIGUOUS:
12219c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    return SipErrorCode.INVALID_REMOTE_URI;
12229c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
12239c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                case Response.REQUEST_TIMEOUT:
12249c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    return SipErrorCode.TIME_OUT;
12259c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
12269c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                default:
12279c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    if (responseStatusCode < 500) {
12289c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        return SipErrorCode.CLIENT_ERROR;
12299c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    } else {
12309c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        return SipErrorCode.SERVER_ERROR;
12319c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    }
12329c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
12339c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
12349c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
12359c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private Throwable getRootCause(Throwable exception) {
12369c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            Throwable cause = exception.getCause();
12379c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            while (cause != null) {
12389c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                exception = cause;
12399c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                cause = exception.getCause();
12409c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
12419c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return exception;
12429c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
12439c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
12449c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private int getErrorCode(Throwable exception) {
12459c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            String message = exception.getMessage();
12469c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (exception instanceof UnknownHostException) {
1247e26eb3274a65c41a6a30bdace1818c5629cca1c8Hung-ying Tyan                return SipErrorCode.SERVER_UNREACHABLE;
12489c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            } else if (exception instanceof IOException) {
12499c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                return SipErrorCode.SOCKET_ERROR;
12509c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            } else {
12519c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                return SipErrorCode.CLIENT_ERROR;
12529c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
12539c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
12549c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
12559c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private void onRegistrationDone(int duration) {
12569c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            reset();
12579c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mProxy.onRegistrationDone(this, duration);
12589c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
12599c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
12609c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private void onRegistrationFailed(int errorCode, String message) {
12619c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            reset();
12629c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mProxy.onRegistrationFailed(this, errorCode, message);
12639c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
12649c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
12659c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private void onRegistrationFailed(Throwable exception) {
12669c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            exception = getRootCause(exception);
12679c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            onRegistrationFailed(getErrorCode(exception),
12689c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    exception.toString());
12699c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
12709c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
12719c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private void onRegistrationFailed(Response response) {
12729c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            int statusCode = response.getStatusCode();
12739c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            onRegistrationFailed(getErrorCode(statusCode),
12749c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    createErrorMessage(response));
12759c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
12769c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
12779c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
12789c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    /**
12799c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan     * @return true if the event is a request event matching the specified
12809c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan     *      method; false otherwise
12819c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan     */
12829c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private static boolean isRequestEvent(String method, EventObject event) {
12839c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        try {
12849c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (event instanceof RequestEvent) {
12859c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                RequestEvent requestEvent = (RequestEvent) event;
12869c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                return method.equals(requestEvent.getRequest().getMethod());
12879c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
12889c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        } catch (Throwable e) {
12899c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
12909c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        return false;
12919c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
12929c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
12939c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private static String getCseqMethod(Message message) {
12949c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        return ((CSeqHeader) message.getHeader(CSeqHeader.NAME)).getMethod();
12959c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
12969c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
12979c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    /**
12989c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan     * @return true if the event is a response event and the CSeqHeader method
12999c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan     * match the given arguments; false otherwise
13009c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan     */
13019c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private static boolean expectResponse(
13029c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            String expectedMethod, EventObject evt) {
13039c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        if (evt instanceof ResponseEvent) {
13049c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            ResponseEvent event = (ResponseEvent) evt;
13059c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            Response response = event.getResponse();
13069c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return expectedMethod.equalsIgnoreCase(getCseqMethod(response));
13079c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
13089c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        return false;
13099c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
13109c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
13119c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    /**
13129c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan     * @return true if the event is a response event and the response code and
13139c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan     *      CSeqHeader method match the given arguments; false otherwise
13149c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan     */
13159c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private static boolean expectResponse(
13169c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            int responseCode, String expectedMethod, EventObject evt) {
13179c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        if (evt instanceof ResponseEvent) {
13189c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            ResponseEvent event = (ResponseEvent) evt;
13199c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            Response response = event.getResponse();
13209c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (response.getStatusCode() == responseCode) {
13219c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                return expectedMethod.equalsIgnoreCase(getCseqMethod(response));
13229c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
13239c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
13249c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        return false;
13259c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
13269c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
13279c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private static SipProfile createPeerProfile(Request request)
13289c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            throws SipException {
13299c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        try {
13309c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            FromHeader fromHeader =
13319c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    (FromHeader) request.getHeader(FromHeader.NAME);
13329c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            Address address = fromHeader.getAddress();
13339c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            SipURI uri = (SipURI) address.getURI();
13349c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            String username = uri.getUser();
13359c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (username == null) username = ANONYMOUS;
13369c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return new SipProfile.Builder(username, uri.getHost())
13379c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    .setPort(uri.getPort())
13389c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    .setDisplayName(address.getDisplayName())
13399c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    .build();
13409c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        } catch (IllegalArgumentException e) {
13419c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            throw new SipException("createPeerProfile()", e);
13429c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        } catch (ParseException e) {
13439c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            throw new SipException("createPeerProfile()", e);
13449c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
13459c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
13469c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
13479c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private static boolean isLoggable(SipSessionImpl s) {
13489c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        if (s != null) {
13499c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            switch (s.mState) {
13509c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                case SipSession.State.PINGING:
13519c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    return DEBUG_PING;
13529c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
13539c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
13549c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        return DEBUG;
13559c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
13569c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
1357ed3c0fbbd5457f43ff72cec31b8ab0bb3f7a0047Hung-ying Tyan    private static boolean isLoggable(EventObject evt) {
1358ed3c0fbbd5457f43ff72cec31b8ab0bb3f7a0047Hung-ying Tyan        return isLoggable(null, evt);
1359ed3c0fbbd5457f43ff72cec31b8ab0bb3f7a0047Hung-ying Tyan    }
1360ed3c0fbbd5457f43ff72cec31b8ab0bb3f7a0047Hung-ying Tyan
13619c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private static boolean isLoggable(SipSessionImpl s, EventObject evt) {
13629c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        if (!isLoggable(s)) return false;
13639c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        if (evt == null) return false;
13649c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
13659c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        if (evt instanceof OptionsCommand) {
13669c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return DEBUG_PING;
13679c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        } else if (evt instanceof ResponseEvent) {
13689c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            Response response = ((ResponseEvent) evt).getResponse();
13699c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (Request.OPTIONS.equals(response.getHeader(CSeqHeader.NAME))) {
13709c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                return DEBUG_PING;
13719c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
13729c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return DEBUG;
13739c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        } else if (evt instanceof RequestEvent) {
13749c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return DEBUG;
13759c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
13769c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        return false;
13779c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
13789c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
13799c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private static String log(EventObject evt) {
13809c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        if (evt instanceof RequestEvent) {
13819c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return ((RequestEvent) evt).getRequest().toString();
13829c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        } else if (evt instanceof ResponseEvent) {
13839c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return ((ResponseEvent) evt).getResponse().toString();
13849c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        } else {
13859c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return evt.toString();
13869c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
13879c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
13889c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
13899c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private class OptionsCommand extends EventObject {
13909c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public OptionsCommand() {
13919c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            super(SipSessionGroup.this);
13929c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
13939c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
13949c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
13959c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private class RegisterCommand extends EventObject {
13969c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private int mDuration;
13979c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
13989c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public RegisterCommand(int duration) {
13999c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            super(SipSessionGroup.this);
14009c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mDuration = duration;
14019c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
14029c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
14039c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public int getDuration() {
14049c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return mDuration;
14059c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
14069c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
14079c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
14089c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private class MakeCallCommand extends EventObject {
14099c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private String mSessionDescription;
14109c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private int mTimeout; // in seconds
14119c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
14129c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public MakeCallCommand(SipProfile peerProfile,
14139c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                String sessionDescription) {
14149c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            this(peerProfile, sessionDescription, -1);
14159c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
14169c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
14179c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public MakeCallCommand(SipProfile peerProfile,
14189c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                String sessionDescription, int timeout) {
14199c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            super(peerProfile);
14209c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mSessionDescription = sessionDescription;
14219c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mTimeout = timeout;
14229c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
14239c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
14249c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public SipProfile getPeerProfile() {
14259c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return (SipProfile) getSource();
14269c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
14279c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
14289c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public String getSessionDescription() {
14299c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return mSessionDescription;
14309c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
14319c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
14329c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public int getTimeout() {
14339c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return mTimeout;
14349c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
14359c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
14369c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan}
1437