SipSessionGroup.java revision 6057cd00d95c756b78f22c67279cb982bc0674ef
12d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang/*
22d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang * Copyright (C) 2010 The Android Open Source Project
32d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang *
42d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang * Licensed under the Apache License, Version 2.0 (the "License");
52d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang * you may not use this file except in compliance with the License.
62d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang * You may obtain a copy of the License at
72d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang *
82d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang *      http://www.apache.org/licenses/LICENSE-2.0
92d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang *
102d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang * Unless required by applicable law or agreed to in writing, software
112d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang * distributed under the License is distributed on an "AS IS" BASIS,
122d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
132d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang * See the License for the specific language governing permissions and
142d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang * limitations under the License.
152d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang */
162d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
172d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangpackage com.android.server.sip;
182d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
192d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport gov.nist.javax.sip.clientauthutils.AccountManager;
202d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport gov.nist.javax.sip.clientauthutils.UserCredentials;
212d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport gov.nist.javax.sip.header.SIPHeaderNames;
227d137e40cd36290c6bfb5beaf66f4018ae92c97fChung-yih Wangimport gov.nist.javax.sip.header.ProxyAuthenticate;
232d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport gov.nist.javax.sip.header.WWWAuthenticate;
2495b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yehimport gov.nist.javax.sip.message.SIPMessage;
252d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
262d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport android.net.sip.ISipSession;
272d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport android.net.sip.ISipSessionListener;
28903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyanimport android.net.sip.SipErrorCode;
292d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport android.net.sip.SipProfile;
3084a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyanimport android.net.sip.SipSession;
312d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport android.text.TextUtils;
322d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport android.util.Log;
332d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
342d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport java.io.IOException;
3595b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yehimport java.io.UnsupportedEncodingException;
362d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport java.net.DatagramSocket;
37903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyanimport java.net.UnknownHostException;
382d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport java.text.ParseException;
392d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport java.util.Collection;
402d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport java.util.EventObject;
412d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport java.util.HashMap;
422d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport java.util.Map;
432d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport java.util.Properties;
442d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport java.util.TooManyListenersException;
452d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
462d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.ClientTransaction;
472d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.Dialog;
482d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.DialogTerminatedEvent;
492d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.IOExceptionEvent;
502d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.InvalidArgumentException;
512d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.ListeningPoint;
522d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.RequestEvent;
532d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.ResponseEvent;
542d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.ServerTransaction;
552d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.SipException;
562d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.SipFactory;
572d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.SipListener;
582d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.SipProvider;
592d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.SipStack;
602d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.TimeoutEvent;
612d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.Transaction;
622d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.TransactionState;
632d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.TransactionTerminatedEvent;
64903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyanimport javax.sip.TransactionUnavailableException;
652d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.address.Address;
662d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.address.SipURI;
672d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.header.CSeqHeader;
682d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.header.ExpiresHeader;
692d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.header.FromHeader;
702d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.header.MinExpiresHeader;
712d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.header.ViaHeader;
722d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.message.Message;
732d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.message.Request;
742d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.message.Response;
752d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
762d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang/**
772d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang * Manages {@link ISipSession}'s for a SIP account.
782d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang */
792d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangclass SipSessionGroup implements SipListener {
802d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private static final String TAG = "SipSession";
81c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan    private static final boolean DEBUG = true;
82c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan    private static final boolean DEBUG_PING = DEBUG && false;
832d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private static final String ANONYMOUS = "anonymous";
849352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan    private static final int EXPIRY_TIME = 3600; // in seconds
85194bbcce9ba15634500f542b9ea017b2cf154b45Hung-ying Tyan    private static final int CANCEL_CALL_TIMER = 3; // in seconds
862d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
872d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private static final EventObject DEREGISTER = new EventObject("Deregister");
882d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private static final EventObject END_CALL = new EventObject("End call");
892d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private static final EventObject HOLD_CALL = new EventObject("Hold call");
902d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private static final EventObject CONTINUE_CALL
912d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            = new EventObject("Continue call");
922d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
932d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private final SipProfile mLocalProfile;
942d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private final String mPassword;
952d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
962d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private SipStack mSipStack;
972d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private SipHelper mSipHelper;
982d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private String mLastNonce;
992d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private int mRPort;
1002d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
1012d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    // session that processes INVITE requests
1022d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private SipSessionImpl mCallReceiverSession;
1032d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private String mLocalIp;
1042d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
1052d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    // call-id-to-SipSession map
1062d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private Map<String, SipSessionImpl> mSessionMap =
1072d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            new HashMap<String, SipSessionImpl>();
1082d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
1092d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    /**
1102d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang     * @param myself the local profile with password crossed out
1112d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang     * @param password the password of the profile
1122d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang     * @throws IOException if cannot assign requested address
1132d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang     */
1142d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    public SipSessionGroup(String localIp, SipProfile myself, String password)
1152d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            throws SipException, IOException {
1162d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        mLocalProfile = myself;
1172d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        mPassword = password;
1182d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        reset(localIp);
1192d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
1202d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
12184a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan    synchronized void reset(String localIp) throws SipException, IOException {
1222d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        mLocalIp = localIp;
1232d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        if (localIp == null) return;
1242d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
1252d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        SipProfile myself = mLocalProfile;
1262d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        SipFactory sipFactory = SipFactory.getInstance();
1272d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        Properties properties = new Properties();
1282d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        properties.setProperty("javax.sip.STACK_NAME", getStackName());
1292d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        String outboundProxy = myself.getProxyAddress();
1302d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        if (!TextUtils.isEmpty(outboundProxy)) {
1315de1d36dd0415c4cf9afdf093a4915951ef6c770Chung-yih Wang            Log.v(TAG, "outboundProxy is " + outboundProxy);
1322d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            properties.setProperty("javax.sip.OUTBOUND_PROXY", outboundProxy
1332d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    + ":" + myself.getPort() + "/" + myself.getProtocol());
1342d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
1352d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        SipStack stack = mSipStack = sipFactory.createSipStack(properties);
1362d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
1372d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        try {
1382d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            SipProvider provider = stack.createSipProvider(
1392d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    stack.createListeningPoint(localIp, allocateLocalPort(),
1402d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                            myself.getProtocol()));
1412d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            provider.addSipListener(this);
1422d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            mSipHelper = new SipHelper(stack, provider);
1432d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        } catch (InvalidArgumentException e) {
1442d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            throw new IOException(e.getMessage());
1452d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        } catch (TooManyListenersException e) {
1462d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            // must never happen
1472d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            throw new SipException("SipSessionGroup constructor", e);
1482d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
1492d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        Log.d(TAG, " start stack for " + myself.getUriString());
1502d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        stack.start();
1512d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
1522d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        mLastNonce = null;
1532d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        mCallReceiverSession = null;
1542d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        mSessionMap.clear();
1552d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
1562d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
157d231aa880ab006d51ffe03454c1fc082f1c97bb8Hung-ying Tyan    synchronized void onConnectivityChanged() {
158d231aa880ab006d51ffe03454c1fc082f1c97bb8Hung-ying Tyan        for (SipSessionImpl s : mSessionMap.values()) {
159d231aa880ab006d51ffe03454c1fc082f1c97bb8Hung-ying Tyan            s.onError(SipErrorCode.DATA_CONNECTION_LOST,
160d231aa880ab006d51ffe03454c1fc082f1c97bb8Hung-ying Tyan                    "data connection lost");
161d231aa880ab006d51ffe03454c1fc082f1c97bb8Hung-ying Tyan        }
162d231aa880ab006d51ffe03454c1fc082f1c97bb8Hung-ying Tyan    }
163d231aa880ab006d51ffe03454c1fc082f1c97bb8Hung-ying Tyan
1642d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    public SipProfile getLocalProfile() {
1652d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        return mLocalProfile;
1662d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
1672d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
1682d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    public String getLocalProfileUri() {
1692d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        return mLocalProfile.getUriString();
1702d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
1712d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
1722d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private String getStackName() {
1732d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        return "stack" + System.currentTimeMillis();
1742d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
1752d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
1762d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    public synchronized void close() {
1772d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        Log.d(TAG, " close stack for " + mLocalProfile.getUriString());
1782d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        mSessionMap.clear();
1792d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        closeToNotReceiveCalls();
1802d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        if (mSipStack != null) {
1812d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            mSipStack.stop();
1822d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            mSipStack = null;
1832d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            mSipHelper = null;
1842d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
1852d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
1862d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
1872d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    public synchronized boolean isClosed() {
1882d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        return (mSipStack == null);
1892d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
1902d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
1912d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    // For internal use, require listener not to block in callbacks.
1922d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    public synchronized void openToReceiveCalls(ISipSessionListener listener) {
1932d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        if (mCallReceiverSession == null) {
1942d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            mCallReceiverSession = new SipSessionCallReceiverImpl(listener);
1952d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        } else {
1962d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            mCallReceiverSession.setListener(listener);
1972d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
1982d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
1992d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
2002d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    public synchronized void closeToNotReceiveCalls() {
2012d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        mCallReceiverSession = null;
2022d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
2032d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
2042d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    public ISipSession createSession(ISipSessionListener listener) {
2052d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        return (isClosed() ? null : new SipSessionImpl(listener));
2062d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
2072d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
2082d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private static int allocateLocalPort() throws SipException {
2092d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        try {
2102d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            DatagramSocket s = new DatagramSocket();
2112d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            int localPort = s.getLocalPort();
2122d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            s.close();
2132d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return localPort;
2142d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        } catch (IOException e) {
2152d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            throw new SipException("allocateLocalPort()", e);
2162d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
2172d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
2182d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
2192d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private synchronized SipSessionImpl getSipSession(EventObject event) {
2202d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        String key = SipHelper.getCallId(event);
2212d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        SipSessionImpl session = mSessionMap.get(key);
222c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan        if ((session != null) && isLoggable(session)) {
223c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan            Log.d(TAG, "session key from event: " + key);
224c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan            Log.d(TAG, "active sessions:");
225c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan            for (String k : mSessionMap.keySet()) {
226c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan                Log.d(TAG, " ..." + k + ": " + mSessionMap.get(k));
227c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan            }
228c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan        }
2292d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        return ((session != null) ? session : mCallReceiverSession);
2302d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
2312d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
2322d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private synchronized void addSipSession(SipSessionImpl newSession) {
2332d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        removeSipSession(newSession);
2342d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        String key = newSession.getCallId();
2352d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        mSessionMap.put(key, newSession);
236c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan        if (isLoggable(newSession)) {
237c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan            Log.d(TAG, "+++  add a session with key:  '" + key + "'");
238c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan            for (String k : mSessionMap.keySet()) {
239c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan                Log.d(TAG, "  " + k + ": " + mSessionMap.get(k));
240c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan            }
2412d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
2422d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
2432d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
2442d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private synchronized void removeSipSession(SipSessionImpl session) {
2452d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        if (session == mCallReceiverSession) return;
2462d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        String key = session.getCallId();
2472d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        SipSessionImpl s = mSessionMap.remove(key);
2482d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        // sanity check
2492d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        if ((s != null) && (s != session)) {
2502d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            Log.w(TAG, "session " + session + " is not associated with key '"
2512d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    + key + "'");
2522d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            mSessionMap.put(key, s);
2532d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            for (Map.Entry<String, SipSessionImpl> entry
2542d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    : mSessionMap.entrySet()) {
2552d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                if (entry.getValue() == s) {
2562d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    key = entry.getKey();
2572d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    mSessionMap.remove(key);
2582d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                }
2592d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
2602d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
2612d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
262c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan        if ((s != null) && isLoggable(s)) {
263c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan            Log.d(TAG, "remove session " + session + " @key '" + key + "'");
264c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan            for (String k : mSessionMap.keySet()) {
265c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan                Log.d(TAG, "  " + k + ": " + mSessionMap.get(k));
266c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan            }
2672d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
2682d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
2692d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
2702d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    public void processRequest(RequestEvent event) {
2712d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        process(event);
2722d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
2732d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
2742d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    public void processResponse(ResponseEvent event) {
2752d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        process(event);
2762d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
2772d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
2782d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    public void processIOException(IOExceptionEvent event) {
2792d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        process(event);
2802d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
2812d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
2822d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    public void processTimeout(TimeoutEvent event) {
2832d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        process(event);
2842d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
2852d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
2862d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    public void processTransactionTerminated(TransactionTerminatedEvent event) {
2872d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        process(event);
2882d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
2892d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
2902d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    public void processDialogTerminated(DialogTerminatedEvent event) {
2912d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        process(event);
2922d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
2932d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
2942d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private synchronized void process(EventObject event) {
2952d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        SipSessionImpl session = getSipSession(event);
2962d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        try {
297c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan            boolean isLoggable = isLoggable(session, event);
298c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan            boolean processed = (session != null) && session.process(event);
299c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan            if (isLoggable && processed) {
30097963794af1e18674dd111e3ad344d90b16c922cHung-ying Tyan                Log.d(TAG, "new state after: "
30184a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                        + SipSession.State.toString(session.mState));
3022d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
3032d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        } catch (Throwable e) {
304903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan            Log.w(TAG, "event process error: " + event, e);
3052d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            session.onError(e);
3062d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
3072d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
3082d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
30995b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh    private String extractContent(Message message) {
31095b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh        // Currently we do not support secure MIME bodies.
31195b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh        byte[] bytes = message.getRawContent();
31295b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh        if (bytes != null) {
31395b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh            try {
31495b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh                if (message instanceof SIPMessage) {
31595b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh                    return ((SIPMessage) message).getMessageContent();
31695b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh                } else {
31795b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh                    return new String(bytes, "UTF-8");
31895b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh                }
31995b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh            } catch (UnsupportedEncodingException e) {
32095b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh            }
32195b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh        }
32295b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh        return null;
32395b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh    }
32495b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh
3252d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private class SipSessionCallReceiverImpl extends SipSessionImpl {
3262d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public SipSessionCallReceiverImpl(ISipSessionListener listener) {
3272d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            super(listener);
3282d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
3292d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
3302d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public boolean process(EventObject evt) throws SipException {
331c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan            if (isLoggable(this, evt)) Log.d(TAG, " ~~~~~   " + this + ": "
33284a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                    + SipSession.State.toString(mState) + ": processing "
33397963794af1e18674dd111e3ad344d90b16c922cHung-ying Tyan                    + log(evt));
3342d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            if (isRequestEvent(Request.INVITE, evt)) {
3352d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                RequestEvent event = (RequestEvent) evt;
3362d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                SipSessionImpl newSession = new SipSessionImpl(mProxy);
3372d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                newSession.mServerTransaction = mSipHelper.sendRinging(event,
3382d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        generateTag());
3392d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                newSession.mDialog = newSession.mServerTransaction.getDialog();
3402d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                newSession.mInviteReceived = event;
3412d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                newSession.mPeerProfile = createPeerProfile(event.getRequest());
34284a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                newSession.mState = SipSession.State.INCOMING_CALL;
3432d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                newSession.mPeerSessionDescription =
34495b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh                        extractContent(event.getRequest());
3452d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                addSipSession(newSession);
3462d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mProxy.onRinging(newSession, newSession.mPeerProfile,
3472d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        newSession.mPeerSessionDescription);
3482d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return true;
3490b4d2fb11405e2e785ec30cabe7bb311c654c0d2Chung-yih Wang            } else if (isRequestEvent(Request.OPTIONS, evt)) {
3500b4d2fb11405e2e785ec30cabe7bb311c654c0d2Chung-yih Wang                mSipHelper.sendResponse((RequestEvent) evt, Response.OK);
3510b4d2fb11405e2e785ec30cabe7bb311c654c0d2Chung-yih Wang                return true;
3522d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            } else {
3532d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return false;
3542d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
3552d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
3562d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
3572d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
3582d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    class SipSessionImpl extends ISipSession.Stub {
3592d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        SipProfile mPeerProfile;
3602d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        SipSessionListenerProxy mProxy = new SipSessionListenerProxy();
36184a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan        int mState = SipSession.State.READY_TO_CALL;
3622d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        RequestEvent mInviteReceived;
3632d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        Dialog mDialog;
3642d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        ServerTransaction mServerTransaction;
3652d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        ClientTransaction mClientTransaction;
36695b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh        String mPeerSessionDescription;
3672d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        boolean mInCall;
3682d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        boolean mReRegisterFlag = false;
3699352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan        SessionTimer mTimer;
3709352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan
3719352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan        // lightweight timer
3729352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan        class SessionTimer {
3739352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan            private boolean mRunning = true;
3749352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan
3759352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan            void start(final int timeout) {
3769352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                new Thread(new Runnable() {
3779352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                    public void run() {
3789352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                        sleep(timeout);
3799352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                        if (mRunning) timeout();
3809352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                    }
38184a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                }, "SipSessionTimerThread").start();
3829352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan            }
3839352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan
3849352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan            synchronized void cancel() {
3859352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                mRunning = false;
3869352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                this.notify();
3879352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan            }
3889352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan
3899352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan            private void timeout() {
3909352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                synchronized (SipSessionGroup.this) {
3919352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                    onError(SipErrorCode.TIME_OUT, "Session timed out!");
3929352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                }
3939352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan            }
3949352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan
3959352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan            private synchronized void sleep(int timeout) {
3969352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                try {
3979352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                    this.wait(timeout * 1000);
3989352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                } catch (InterruptedException e) {
3999352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                    Log.e(TAG, "session timer interrupted!");
4009352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                }
4019352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan            }
4029352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan        }
4032d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
4042d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public SipSessionImpl(ISipSessionListener listener) {
4052d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            setListener(listener);
4062d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
4072d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
4082d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        SipSessionImpl duplicate() {
4092d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return new SipSessionImpl(mProxy.getListener());
4102d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
4112d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
4122d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private void reset() {
4132d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            mInCall = false;
4142d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            removeSipSession(this);
4152d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            mPeerProfile = null;
41684a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan            mState = SipSession.State.READY_TO_CALL;
4172d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            mInviteReceived = null;
4182d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            mDialog = null;
4192d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            mServerTransaction = null;
4202d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            mClientTransaction = null;
4212d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            mPeerSessionDescription = null;
4229352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan
4239352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan            cancelSessionTimer();
4242d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
4252d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
4262d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public boolean isInCall() {
4272d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return mInCall;
4282d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
4292d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
4302d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public String getLocalIp() {
4312d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return mLocalIp;
4322d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
4332d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
4342d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public SipProfile getLocalProfile() {
4352d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return mLocalProfile;
4362d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
4372d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
4382d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public SipProfile getPeerProfile() {
4392d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return mPeerProfile;
4402d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
4412d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
4422d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public String getCallId() {
4432d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return SipHelper.getCallId(getTransaction());
4442d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
4452d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
4462d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private Transaction getTransaction() {
4472d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            if (mClientTransaction != null) return mClientTransaction;
4482d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            if (mServerTransaction != null) return mServerTransaction;
4492d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return null;
4502d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
4512d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
45297963794af1e18674dd111e3ad344d90b16c922cHung-ying Tyan        public int getState() {
45397963794af1e18674dd111e3ad344d90b16c922cHung-ying Tyan            return mState;
4542d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
4552d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
4562d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public void setListener(ISipSessionListener listener) {
4572d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            mProxy.setListener((listener instanceof SipSessionListenerProxy)
4582d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    ? ((SipSessionListenerProxy) listener).getListener()
4592d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    : listener);
4602d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
4612d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
462dba514c6d8e8263d4b8f31cb2fdebfc1d4f84c35Hung-ying Tyan        // process the command in a new thread
463dba514c6d8e8263d4b8f31cb2fdebfc1d4f84c35Hung-ying Tyan        private void doCommandAsync(final EventObject command) {
464dba514c6d8e8263d4b8f31cb2fdebfc1d4f84c35Hung-ying Tyan            new Thread(new Runnable() {
465dba514c6d8e8263d4b8f31cb2fdebfc1d4f84c35Hung-ying Tyan                    public void run() {
466dba514c6d8e8263d4b8f31cb2fdebfc1d4f84c35Hung-ying Tyan                        try {
467dba514c6d8e8263d4b8f31cb2fdebfc1d4f84c35Hung-ying Tyan                            processCommand(command);
468dba514c6d8e8263d4b8f31cb2fdebfc1d4f84c35Hung-ying Tyan                        } catch (SipException e) {
469903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                            Log.w(TAG, "command error: " + command, e);
4703d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                            onError(e);
471dba514c6d8e8263d4b8f31cb2fdebfc1d4f84c35Hung-ying Tyan                        }
472dba514c6d8e8263d4b8f31cb2fdebfc1d4f84c35Hung-ying Tyan                    }
47384a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan            }, "SipSessionAsyncCmdThread").start();
474dba514c6d8e8263d4b8f31cb2fdebfc1d4f84c35Hung-ying Tyan        }
475dba514c6d8e8263d4b8f31cb2fdebfc1d4f84c35Hung-ying Tyan
4769352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan        public void makeCall(SipProfile peerProfile, String sessionDescription,
4779352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                int timeout) {
4789352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan            doCommandAsync(new MakeCallCommand(peerProfile, sessionDescription,
4799352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                    timeout));
4802d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
4812d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
4829352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan        public void answerCall(String sessionDescription, int timeout) {
4832d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            try {
4849352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                processCommand(new MakeCallCommand(mPeerProfile,
4859352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                        sessionDescription, timeout));
4862d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            } catch (SipException e) {
4872d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                onError(e);
4882d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
4892d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
4902d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
4912d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public void endCall() {
492dba514c6d8e8263d4b8f31cb2fdebfc1d4f84c35Hung-ying Tyan            doCommandAsync(END_CALL);
4932d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
4942d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
4959352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan        public void changeCall(String sessionDescription, int timeout) {
4969352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan            doCommandAsync(new MakeCallCommand(mPeerProfile, sessionDescription,
4979352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                    timeout));
4989352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan        }
4999352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan
5009352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan        public void changeCallWithTimeout(
5019352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                String sessionDescription, int timeout) {
5029352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan            doCommandAsync(new MakeCallCommand(mPeerProfile, sessionDescription,
5039352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                    timeout));
5042d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
5052d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
5062d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public void register(int duration) {
507dba514c6d8e8263d4b8f31cb2fdebfc1d4f84c35Hung-ying Tyan            doCommandAsync(new RegisterCommand(duration));
5082d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
5092d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
5102d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public void unregister() {
511dba514c6d8e8263d4b8f31cb2fdebfc1d4f84c35Hung-ying Tyan            doCommandAsync(DEREGISTER);
5122d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
5132d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
5142d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public boolean isReRegisterRequired() {
5152d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return mReRegisterFlag;
5162d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
5172d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
5182d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public void clearReRegisterRequired() {
5192d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            mReRegisterFlag = false;
5202d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
5212d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
5222d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public void sendKeepAlive() {
52384a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan            mState = SipSession.State.PINGING;
5242d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            try {
5252d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                processCommand(new OptionsCommand());
52684a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                while (SipSession.State.PINGING == mState) {
5272d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    Thread.sleep(1000);
5282d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                }
5292d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            } catch (SipException e) {
5302d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                Log.e(TAG, "sendKeepAlive failed", e);
5312d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            } catch (InterruptedException e) {
5322d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                Log.e(TAG, "sendKeepAlive interrupted", e);
5332d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
5342d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
5352d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
5362d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private void processCommand(EventObject command) throws SipException {
5372d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            if (!process(command)) {
5383d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                onError(SipErrorCode.IN_PROGRESS,
5393d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                        "cannot initiate a new transaction to execute: "
5403d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                        + command);
5412d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
5422d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
5432d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
5442d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        protected String generateTag() {
5452d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            // 32-bit randomness
5462d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return String.valueOf((long) (Math.random() * 0x100000000L));
5472d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
5482d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
5492d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public String toString() {
5502d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            try {
5512d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                String s = super.toString();
55297963794af1e18674dd111e3ad344d90b16c922cHung-ying Tyan                return s.substring(s.indexOf("@")) + ":"
55384a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                        + SipSession.State.toString(mState);
5542d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            } catch (Throwable e) {
5552d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return super.toString();
5562d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
5572d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
5582d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
5592d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public boolean process(EventObject evt) throws SipException {
560c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan            if (isLoggable(this, evt)) Log.d(TAG, " ~~~~~   " + this + ": "
56184a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                    + SipSession.State.toString(mState) + ": processing "
56297963794af1e18674dd111e3ad344d90b16c922cHung-ying Tyan                    + log(evt));
5632d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            synchronized (SipSessionGroup.this) {
5642d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                if (isClosed()) return false;
5652d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
5662d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                Dialog dialog = null;
5672d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                if (evt instanceof RequestEvent) {
5682d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    dialog = ((RequestEvent) evt).getDialog();
5692d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                } else if (evt instanceof ResponseEvent) {
5702d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    dialog = ((ResponseEvent) evt).getDialog();
5712d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                }
5722d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                if (dialog != null) mDialog = dialog;
5732d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
5742d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                boolean processed;
5752d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
5762d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                switch (mState) {
57784a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                case SipSession.State.REGISTERING:
57884a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                case SipSession.State.DEREGISTERING:
5792d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    processed = registeringToReady(evt);
5802d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    break;
58184a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                case SipSession.State.PINGING:
5822d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    processed = keepAliveProcess(evt);
5832d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    break;
58484a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                case SipSession.State.READY_TO_CALL:
5852d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    processed = readyForCall(evt);
5862d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    break;
58784a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                case SipSession.State.INCOMING_CALL:
5882d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    processed = incomingCall(evt);
5892d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    break;
59084a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                case SipSession.State.INCOMING_CALL_ANSWERING:
5912d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    processed = incomingCallToInCall(evt);
5922d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    break;
59384a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                case SipSession.State.OUTGOING_CALL:
59484a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                case SipSession.State.OUTGOING_CALL_RING_BACK:
5952d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    processed = outgoingCall(evt);
5962d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    break;
59784a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                case SipSession.State.OUTGOING_CALL_CANCELING:
5982d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    processed = outgoingCallToReady(evt);
5992d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    break;
60084a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                case SipSession.State.IN_CALL:
6012d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    processed = inCall(evt);
6022d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    break;
6032d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                default:
6042d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    processed = false;
6052d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                }
6062d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return (processed || processExceptions(evt));
6072d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
6082d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
6092d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
6102d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private boolean processExceptions(EventObject evt) throws SipException {
6112d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            if (isRequestEvent(Request.BYE, evt)) {
6122d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                // terminate the call whenever a BYE is received
6132d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mSipHelper.sendResponse((RequestEvent) evt, Response.OK);
6142d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                endCallNormally();
6152d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return true;
6162d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            } else if (isRequestEvent(Request.CANCEL, evt)) {
6172d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mSipHelper.sendResponse((RequestEvent) evt,
6182d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        Response.CALL_OR_TRANSACTION_DOES_NOT_EXIST);
6192d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return true;
6202d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            } else if (evt instanceof TransactionTerminatedEvent) {
621025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan                if (isCurrentTransaction((TransactionTerminatedEvent) evt)) {
622025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan                    if (evt instanceof TimeoutEvent) {
623025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan                        processTimeout((TimeoutEvent) evt);
624025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan                    } else {
625025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan                        processTransactionTerminated(
626025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan                                (TransactionTerminatedEvent) evt);
627025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan                    }
628025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan                    return true;
6292d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                }
6300b4d2fb11405e2e785ec30cabe7bb311c654c0d2Chung-yih Wang            } else if (isRequestEvent(Request.OPTIONS, evt)) {
6310b4d2fb11405e2e785ec30cabe7bb311c654c0d2Chung-yih Wang                mSipHelper.sendResponse((RequestEvent) evt, Response.OK);
6320b4d2fb11405e2e785ec30cabe7bb311c654c0d2Chung-yih Wang                return true;
6332d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            } else if (evt instanceof DialogTerminatedEvent) {
6342d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                processDialogTerminated((DialogTerminatedEvent) evt);
6352d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return true;
6362d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
6372d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return false;
6382d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
6392d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
6402d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private void processDialogTerminated(DialogTerminatedEvent event) {
6412d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            if (mDialog == event.getDialog()) {
6422d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                onError(new SipException("dialog terminated"));
6432d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            } else {
6442d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                Log.d(TAG, "not the current dialog; current=" + mDialog
6452d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        + ", terminated=" + event.getDialog());
6462d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
6472d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
6482d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
649025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan        private boolean isCurrentTransaction(TransactionTerminatedEvent event) {
650025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan            Transaction current = event.isServerTransaction()
651025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan                    ? mServerTransaction
652025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan                    : mClientTransaction;
653025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan            Transaction target = event.isServerTransaction()
654025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan                    ? event.getServerTransaction()
655025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan                    : event.getClientTransaction();
656025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan
657025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan            if ((current != target) && (mState != SipSession.State.PINGING)) {
658025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan                Log.d(TAG, "not the current transaction; current="
659025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan                        + toString(current) + ", target=" + toString(target));
660025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan                return false;
661025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan            } else if (current != null) {
662025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan                Log.d(TAG, "transaction terminated: " + toString(current));
663025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan                return true;
664fccd5bc78f94b7dcfbcf78ddca83719c9cd1a74fHung-ying Tyan            } else {
665fccd5bc78f94b7dcfbcf78ddca83719c9cd1a74fHung-ying Tyan                // no transaction; shouldn't be here; ignored
666fccd5bc78f94b7dcfbcf78ddca83719c9cd1a74fHung-ying Tyan                return true;
667025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan            }
668025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan        }
669025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan
670025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan        private String toString(Transaction transaction) {
671025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan            if (transaction == null) return "null";
672025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan            Request request = transaction.getRequest();
673025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan            Dialog dialog = transaction.getDialog();
674025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan            CSeqHeader cseq = (CSeqHeader) request.getHeader(CSeqHeader.NAME);
675025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan            return String.format("req=%s,%s,s=%s,ds=%s,", request.getMethod(),
676025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan                    cseq.getSeqNumber(), transaction.getState(),
677025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan                    ((dialog == null) ? "-" : dialog.getState()));
678025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan        }
679025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan
6803d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan        private void processTransactionTerminated(
6813d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                TransactionTerminatedEvent event) {
6823d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan            switch (mState) {
68384a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                case SipSession.State.IN_CALL:
68484a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                case SipSession.State.READY_TO_CALL:
6853d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                    Log.d(TAG, "Transaction terminated; do nothing");
6863d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                    break;
6873d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                default:
6883d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                    Log.d(TAG, "Transaction terminated early: " + this);
6893d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                    onError(SipErrorCode.TRANSACTION_TERMINTED,
6903d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                            "transaction terminated");
6913d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan            }
6923d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan        }
6933d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan
6942d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private void processTimeout(TimeoutEvent event) {
695025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan            Log.d(TAG, "processing Timeout...");
6962d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            switch (mState) {
69784a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                case SipSession.State.REGISTERING:
69884a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                case SipSession.State.DEREGISTERING:
6993d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                    reset();
7003d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                    mProxy.onRegistrationTimeout(this);
7013d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                    break;
70284a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                case SipSession.State.INCOMING_CALL:
70384a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                case SipSession.State.INCOMING_CALL_ANSWERING:
70484a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                case SipSession.State.OUTGOING_CALL:
70584a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                case SipSession.State.OUTGOING_CALL_CANCELING:
7063d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                    onError(SipErrorCode.TIME_OUT, event.toString());
7073d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                    break;
70884a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                case SipSession.State.PINGING:
7093d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                    reset();
7103d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                    mReRegisterFlag = true;
71184a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                    mState = SipSession.State.READY_TO_CALL;
7123d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                    break;
7132d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
7143d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                default:
7153d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                    Log.d(TAG, "   do nothing");
7163d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                    break;
7172d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
7182d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
7192d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
7202d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private int getExpiryTime(Response response) {
7212d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            int expires = EXPIRY_TIME;
7222d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            ExpiresHeader expiresHeader = (ExpiresHeader)
7232d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    response.getHeader(ExpiresHeader.NAME);
7242d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            if (expiresHeader != null) expires = expiresHeader.getExpires();
7252d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            expiresHeader = (ExpiresHeader)
7262d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    response.getHeader(MinExpiresHeader.NAME);
7272d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            if (expiresHeader != null) {
7282d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                expires = Math.max(expires, expiresHeader.getExpires());
7292d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
7302d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return expires;
7312d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
7322d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
7332d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private boolean keepAliveProcess(EventObject evt) throws SipException {
7342d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            if (evt instanceof OptionsCommand) {
7352d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mClientTransaction = mSipHelper.sendKeepAlive(mLocalProfile,
7362d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        generateTag());
7372d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mDialog = mClientTransaction.getDialog();
7382d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                addSipSession(this);
7392d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return true;
7402d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            } else if (evt instanceof ResponseEvent) {
7412d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return parseOptionsResult(evt);
7422d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
7432d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return false;
7442d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
7452d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
7462d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private boolean parseOptionsResult(EventObject evt) {
7472d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            if (expectResponse(Request.OPTIONS, evt)) {
7482d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                ResponseEvent event = (ResponseEvent) evt;
7492d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                int rPort = getRPortFromResponse(event.getResponse());
7502d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                if (rPort != -1) {
7512d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    if (mRPort == 0) mRPort = rPort;
7522d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    if (mRPort != rPort) {
7532d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        mReRegisterFlag = true;
754c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan                        if (DEBUG) Log.w(TAG, String.format(
755c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan                                "rport is changed: %d <> %d", mRPort, rPort));
7562d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        mRPort = rPort;
7572d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    } else {
758c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan                        if (DEBUG_PING) Log.w(TAG, "rport is the same: " + rPort);
7592d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    }
7602d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                } else {
761c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan                    if (DEBUG) Log.w(TAG, "peer did not respond rport");
7622d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                }
7633d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                reset();
7642d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return true;
7652d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
7662d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return false;
7672d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
7682d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
7692d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private int getRPortFromResponse(Response response) {
7702d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            ViaHeader viaHeader = (ViaHeader)(response.getHeader(
7712d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    SIPHeaderNames.VIA));
7722d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return (viaHeader == null) ? -1 : viaHeader.getRPort();
7732d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
7742d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
7752d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private boolean registeringToReady(EventObject evt)
7762d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                throws SipException {
7772d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            if (expectResponse(Request.REGISTER, evt)) {
7782d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                ResponseEvent event = (ResponseEvent) evt;
7792d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                Response response = event.getResponse();
7802d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
7812d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                int statusCode = response.getStatusCode();
7822d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                switch (statusCode) {
7832d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                case Response.OK:
78497963794af1e18674dd111e3ad344d90b16c922cHung-ying Tyan                    int state = mState;
78584a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                    onRegistrationDone((state == SipSession.State.REGISTERING)
7862d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                            ? getExpiryTime(((ResponseEvent) evt).getResponse())
7872d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                            : -1);
7882d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    mLastNonce = null;
7892d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    mRPort = 0;
7902d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    return true;
7912d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                case Response.UNAUTHORIZED:
7922d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                case Response.PROXY_AUTHENTICATION_REQUIRED:
793903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                    if (!handleAuthentication(event)) {
79488203cb111cd9605edcdedb402f8f2a96f8f01e1Hung-ying Tyan                        if (mLastNonce == null) {
79588203cb111cd9605edcdedb402f8f2a96f8f01e1Hung-ying Tyan                            onRegistrationFailed(SipErrorCode.SERVER_ERROR,
79688203cb111cd9605edcdedb402f8f2a96f8f01e1Hung-ying Tyan                                    "server does not provide challenge");
79788203cb111cd9605edcdedb402f8f2a96f8f01e1Hung-ying Tyan                        } else {
79888203cb111cd9605edcdedb402f8f2a96f8f01e1Hung-ying Tyan                            Log.v(TAG, "Incorrect username/password");
79988203cb111cd9605edcdedb402f8f2a96f8f01e1Hung-ying Tyan                            onRegistrationFailed(
80088203cb111cd9605edcdedb402f8f2a96f8f01e1Hung-ying Tyan                                    SipErrorCode.INVALID_CREDENTIALS,
80188203cb111cd9605edcdedb402f8f2a96f8f01e1Hung-ying Tyan                                    "incorrect username or password");
80288203cb111cd9605edcdedb402f8f2a96f8f01e1Hung-ying Tyan                        }
8032d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    }
8042d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    return true;
8052d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                default:
8062d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    if (statusCode >= 500) {
8073d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                        onRegistrationFailed(response);
8082d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        return true;
8092d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    }
8102d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                }
8112d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
8122d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return false;
8132d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
8142d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
815903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan        private boolean handleAuthentication(ResponseEvent event)
816903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                throws SipException {
817903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan            Response response = event.getResponse();
818903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan            String nonce = getNonceFromResponse(response);
819903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan            if (((nonce != null) && nonce.equals(mLastNonce)) ||
8207d137e40cd36290c6bfb5beaf66f4018ae92c97fChung-yih Wang                    (nonce == null)) {
8217d137e40cd36290c6bfb5beaf66f4018ae92c97fChung-yih Wang                mLastNonce = nonce;
822903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                return false;
823903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan            } else {
824903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                mClientTransaction = mSipHelper.handleChallenge(
825903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                        event, getAccountManager());
826903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                mDialog = mClientTransaction.getDialog();
827903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                mLastNonce = nonce;
828903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                return true;
829903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan            }
830903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan        }
831903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan
83200a22064efef4f574e439079aae2deae1a087a31Hung-ying Tyan        private boolean crossDomainAuthenticationRequired(Response response) {
83300a22064efef4f574e439079aae2deae1a087a31Hung-ying Tyan            String realm = getRealmFromResponse(response);
83400a22064efef4f574e439079aae2deae1a087a31Hung-ying Tyan            if (realm == null) realm = "";
83500a22064efef4f574e439079aae2deae1a087a31Hung-ying Tyan            return !mLocalProfile.getSipDomain().trim().equals(realm.trim());
83600a22064efef4f574e439079aae2deae1a087a31Hung-ying Tyan        }
83700a22064efef4f574e439079aae2deae1a087a31Hung-ying Tyan
8382d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private AccountManager getAccountManager() {
8392d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return new AccountManager() {
8402d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                public UserCredentials getCredentials(ClientTransaction
8412d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        challengedTransaction, String realm) {
8422d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    return new UserCredentials() {
8432d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        public String getUserName() {
8442d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                            return mLocalProfile.getUserName();
8452d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        }
8462d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
8472d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        public String getPassword() {
8482d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                            return mPassword;
8492d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        }
8502d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
8512d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        public String getSipDomain() {
8522d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                            return mLocalProfile.getSipDomain();
8532d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        }
8542d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    };
8552d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                }
8562d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            };
8572d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
8582d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
85900a22064efef4f574e439079aae2deae1a087a31Hung-ying Tyan        private String getRealmFromResponse(Response response) {
86000a22064efef4f574e439079aae2deae1a087a31Hung-ying Tyan            WWWAuthenticate wwwAuth = (WWWAuthenticate)response.getHeader(
86100a22064efef4f574e439079aae2deae1a087a31Hung-ying Tyan                    SIPHeaderNames.WWW_AUTHENTICATE);
86200a22064efef4f574e439079aae2deae1a087a31Hung-ying Tyan            if (wwwAuth != null) return wwwAuth.getRealm();
86300a22064efef4f574e439079aae2deae1a087a31Hung-ying Tyan            ProxyAuthenticate proxyAuth = (ProxyAuthenticate)response.getHeader(
86400a22064efef4f574e439079aae2deae1a087a31Hung-ying Tyan                    SIPHeaderNames.PROXY_AUTHENTICATE);
86500a22064efef4f574e439079aae2deae1a087a31Hung-ying Tyan            return (proxyAuth == null) ? null : proxyAuth.getRealm();
86600a22064efef4f574e439079aae2deae1a087a31Hung-ying Tyan        }
86700a22064efef4f574e439079aae2deae1a087a31Hung-ying Tyan
8682d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private String getNonceFromResponse(Response response) {
8697d137e40cd36290c6bfb5beaf66f4018ae92c97fChung-yih Wang            WWWAuthenticate wwwAuth = (WWWAuthenticate)response.getHeader(
8707d137e40cd36290c6bfb5beaf66f4018ae92c97fChung-yih Wang                    SIPHeaderNames.WWW_AUTHENTICATE);
8717d137e40cd36290c6bfb5beaf66f4018ae92c97fChung-yih Wang            if (wwwAuth != null) return wwwAuth.getNonce();
8727d137e40cd36290c6bfb5beaf66f4018ae92c97fChung-yih Wang            ProxyAuthenticate proxyAuth = (ProxyAuthenticate)response.getHeader(
8737d137e40cd36290c6bfb5beaf66f4018ae92c97fChung-yih Wang                    SIPHeaderNames.PROXY_AUTHENTICATE);
8747d137e40cd36290c6bfb5beaf66f4018ae92c97fChung-yih Wang            return (proxyAuth == null) ? null : proxyAuth.getNonce();
8752d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
8762d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
8772d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private boolean readyForCall(EventObject evt) throws SipException {
8782d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            // expect MakeCallCommand, RegisterCommand, DEREGISTER
8792d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            if (evt instanceof MakeCallCommand) {
8802d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                MakeCallCommand cmd = (MakeCallCommand) evt;
8812d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mPeerProfile = cmd.getPeerProfile();
8822d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mClientTransaction = mSipHelper.sendInvite(mLocalProfile,
88395b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh                        mPeerProfile, cmd.getSessionDescription(),
88495b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh                        generateTag());
8852d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mDialog = mClientTransaction.getDialog();
8862d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                addSipSession(this);
88784a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                mState = SipSession.State.OUTGOING_CALL;
8882d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mProxy.onCalling(this);
8899352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                startSessionTimer(cmd.getTimeout());
8902d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return true;
8912d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            } else if (evt instanceof RegisterCommand) {
8922d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                int duration = ((RegisterCommand) evt).getDuration();
8932d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mClientTransaction = mSipHelper.sendRegister(mLocalProfile,
8942d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        generateTag(), duration);
8952d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mDialog = mClientTransaction.getDialog();
8962d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                addSipSession(this);
89784a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                mState = SipSession.State.REGISTERING;
8982d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mProxy.onRegistering(this);
8992d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return true;
9002d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            } else if (DEREGISTER == evt) {
9012d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mClientTransaction = mSipHelper.sendRegister(mLocalProfile,
9022d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        generateTag(), 0);
9032d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mDialog = mClientTransaction.getDialog();
9042d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                addSipSession(this);
90584a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                mState = SipSession.State.DEREGISTERING;
9062d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mProxy.onRegistering(this);
9072d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return true;
9082d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
9092d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return false;
9102d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
9112d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
9122d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private boolean incomingCall(EventObject evt) throws SipException {
9132d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            // expect MakeCallCommand(answering) , END_CALL cmd , Cancel
9142d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            if (evt instanceof MakeCallCommand) {
9152d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                // answer call
9162d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mServerTransaction = mSipHelper.sendInviteOk(mInviteReceived,
9172d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        mLocalProfile,
9182d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        ((MakeCallCommand) evt).getSessionDescription(),
9192d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        mServerTransaction);
92084a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                mState = SipSession.State.INCOMING_CALL_ANSWERING;
9219352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                startSessionTimer(((MakeCallCommand) evt).getTimeout());
9222d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return true;
9232d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            } else if (END_CALL == evt) {
9242d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mSipHelper.sendInviteBusyHere(mInviteReceived,
9252d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        mServerTransaction);
9262d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                endCallNormally();
9272d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return true;
9282d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            } else if (isRequestEvent(Request.CANCEL, evt)) {
9292d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                RequestEvent event = (RequestEvent) evt;
9302d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mSipHelper.sendResponse(event, Response.OK);
9312d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mSipHelper.sendInviteRequestTerminated(
9322d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        mInviteReceived.getRequest(), mServerTransaction);
9332d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                endCallNormally();
9342d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return true;
9352d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
9362d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return false;
9372d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
9382d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
9392d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private boolean incomingCallToInCall(EventObject evt)
9402d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                throws SipException {
9412d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            // expect ACK, CANCEL request
9422d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            if (isRequestEvent(Request.ACK, evt)) {
9432d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                establishCall();
9442d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return true;
9452d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            } else if (isRequestEvent(Request.CANCEL, evt)) {
9462d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                // http://tools.ietf.org/html/rfc3261#section-9.2
9472d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                // Final response has been sent; do nothing here.
9482d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return true;
9492d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
9502d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return false;
9512d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
9522d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
9532d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private boolean outgoingCall(EventObject evt) throws SipException {
9542d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            if (expectResponse(Request.INVITE, evt)) {
9552d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                ResponseEvent event = (ResponseEvent) evt;
9562d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                Response response = event.getResponse();
9572d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
9582d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                int statusCode = response.getStatusCode();
9592d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                switch (statusCode) {
9602d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                case Response.RINGING:
9616057cd00d95c756b78f22c67279cb982bc0674efHung-ying Tyan                case Response.CALL_IS_BEING_FORWARDED:
9626057cd00d95c756b78f22c67279cb982bc0674efHung-ying Tyan                case Response.QUEUED:
9636057cd00d95c756b78f22c67279cb982bc0674efHung-ying Tyan                case Response.SESSION_PROGRESS:
9646057cd00d95c756b78f22c67279cb982bc0674efHung-ying Tyan                    // feedback any provisional responses (except TRYING) as
9656057cd00d95c756b78f22c67279cb982bc0674efHung-ying Tyan                    // ring back for better UX
96684a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                    if (mState == SipSession.State.OUTGOING_CALL) {
96784a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                        mState = SipSession.State.OUTGOING_CALL_RING_BACK;
9682d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        mProxy.onRingingBack(this);
9699352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                        cancelSessionTimer();
9702d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    }
9712d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    return true;
9722d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                case Response.OK:
9732d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    mSipHelper.sendInviteAck(event, mDialog);
97495b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh                    mPeerSessionDescription = extractContent(response);
9752d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    establishCall();
9762d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    return true;
9770e0633828928481658c0e09e5893f6214b57ba38Chung-yih Wang                case Response.UNAUTHORIZED:
9782d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                case Response.PROXY_AUTHENTICATION_REQUIRED:
97900a22064efef4f574e439079aae2deae1a087a31Hung-ying Tyan                    if (crossDomainAuthenticationRequired(response)) {
98000a22064efef4f574e439079aae2deae1a087a31Hung-ying Tyan                        onError(SipErrorCode.CROSS_DOMAIN_AUTHENTICATION,
98100a22064efef4f574e439079aae2deae1a087a31Hung-ying Tyan                                getRealmFromResponse(response));
98200a22064efef4f574e439079aae2deae1a087a31Hung-ying Tyan                    } else if (handleAuthentication(event)) {
983903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                        addSipSession(this);
98488203cb111cd9605edcdedb402f8f2a96f8f01e1Hung-ying Tyan                    } else if (mLastNonce == null) {
9859352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                        onError(SipErrorCode.SERVER_ERROR,
98688203cb111cd9605edcdedb402f8f2a96f8f01e1Hung-ying Tyan                                "server does not provide challenge");
987903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                    } else {
9889352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                        onError(SipErrorCode.INVALID_CREDENTIALS,
989903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                                "incorrect username or password");
990903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                    }
9912d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    return true;
9922d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                case Response.REQUEST_PENDING:
9932d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    // TODO:
9942d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    // rfc3261#section-14.1; re-schedule invite
9952d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    return true;
9962d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                default:
9972d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    if (statusCode >= 400) {
9982d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        // error: an ack is sent automatically by the stack
999903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                        onError(response);
10002d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        return true;
10012d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    } else if (statusCode >= 300) {
10022d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        // TODO: handle 3xx (redirect)
10032d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    } else {
10042d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        return true;
10052d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    }
10062d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                }
10072d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return false;
10082d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            } else if (END_CALL == evt) {
10092d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                // RFC says that UA should not send out cancel when no
10102d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                // response comes back yet. We are cheating for not checking
10112d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                // response.
10122d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mSipHelper.sendCancel(mClientTransaction);
101384a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                mState = SipSession.State.OUTGOING_CALL_CANCELING;
10149352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                startSessionTimer(CANCEL_CALL_TIMER);
10152d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return true;
10162d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
10172d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return false;
10182d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
10192d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
10202d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private boolean outgoingCallToReady(EventObject evt)
10212d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                throws SipException {
10222d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            if (evt instanceof ResponseEvent) {
10232d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                ResponseEvent event = (ResponseEvent) evt;
10242d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                Response response = event.getResponse();
10252d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                int statusCode = response.getStatusCode();
10262d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                if (expectResponse(Request.CANCEL, evt)) {
1027025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan                    if (statusCode == Response.OK) {
1028025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan                        // do nothing; wait for REQUEST_TERMINATED
1029025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan                        return true;
1030025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan                    }
1031025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan                } else if (expectResponse(Request.INVITE, evt)) {
10329352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                    switch (statusCode) {
10339352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                        case Response.OK:
1034025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan                            outgoingCall(evt); // abort Cancel
10359352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                            return true;
10369352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                        case Response.REQUEST_TERMINATED:
10379352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                            endCallNormally();
10389352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                            return true;
10392d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    }
10402d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                } else {
10412d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    return false;
10422d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                }
10432d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
10442d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                if (statusCode >= 400) {
10453d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                    onError(response);
10462d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    return true;
10472d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                }
10482d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            } else if (evt instanceof TransactionTerminatedEvent) {
10492d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                // rfc3261#section-14.1:
10502d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                // if re-invite gets timed out, terminate the dialog; but
10512d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                // re-invite is not reliable, just let it go and pretend
10522d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                // nothing happened.
10532d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                onError(new SipException("timed out"));
10542d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
10552d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return false;
10562d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
10572d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
10582d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private boolean inCall(EventObject evt) throws SipException {
10592d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            // expect END_CALL cmd, BYE request, hold call (MakeCallCommand)
10602d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            // OK retransmission is handled in SipStack
10612d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            if (END_CALL == evt) {
10622d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                // rfc3261#section-15.1.1
10632d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mSipHelper.sendBye(mDialog);
10642d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                endCallNormally();
10652d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return true;
10662d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            } else if (isRequestEvent(Request.INVITE, evt)) {
10672d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                // got Re-INVITE
10682d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                RequestEvent event = mInviteReceived = (RequestEvent) evt;
106984a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                mState = SipSession.State.INCOMING_CALL;
107095b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh                mPeerSessionDescription = extractContent(event.getRequest());
10712d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mServerTransaction = null;
10722d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mProxy.onRinging(this, mPeerProfile, mPeerSessionDescription);
10732d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return true;
10742d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            } else if (isRequestEvent(Request.BYE, evt)) {
10752d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mSipHelper.sendResponse((RequestEvent) evt, Response.OK);
10762d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                endCallNormally();
10772d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return true;
10782d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            } else if (evt instanceof MakeCallCommand) {
10792d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                // to change call
10802d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mClientTransaction = mSipHelper.sendReinvite(mDialog,
10812d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        ((MakeCallCommand) evt).getSessionDescription());
108284a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                mState = SipSession.State.OUTGOING_CALL;
10839352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                startSessionTimer(((MakeCallCommand) evt).getTimeout());
10842d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return true;
10852d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
10862d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return false;
10872d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
10882d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
10899352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan        // timeout in seconds
10909352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan        private void startSessionTimer(int timeout) {
10919352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan            if (timeout > 0) {
10929352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                mTimer = new SessionTimer();
10939352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                mTimer.start(timeout);
10949352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan            }
10959352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan        }
10969352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan
10979352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan        private void cancelSessionTimer() {
10989352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan            if (mTimer != null) {
10999352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                mTimer.cancel();
11009352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                mTimer = null;
11019352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan            }
11029352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan        }
11039352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan
1104903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan        private String createErrorMessage(Response response) {
1105624d5b4e8c20516516d0bff74479b9f5abdfe61cHung-ying Tyan            return String.format("%s (%d)", response.getReasonPhrase(),
1106624d5b4e8c20516516d0bff74479b9f5abdfe61cHung-ying Tyan                    response.getStatusCode());
1107903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan        }
1108903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan
11092d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private void establishCall() {
111084a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan            mState = SipSession.State.IN_CALL;
11112d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            mInCall = true;
11129352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan            cancelSessionTimer();
11132d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            mProxy.onCallEstablished(this, mPeerSessionDescription);
11142d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
11152d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
111697963794af1e18674dd111e3ad344d90b16c922cHung-ying Tyan        private void fallbackToPreviousInCall(int errorCode, String message) {
111784a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan            mState = SipSession.State.IN_CALL;
111897963794af1e18674dd111e3ad344d90b16c922cHung-ying Tyan            mProxy.onCallChangeFailed(this, errorCode, message);
11193d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan        }
11203d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan
11212d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private void endCallNormally() {
11222d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            reset();
11232d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            mProxy.onCallEnded(this);
11242d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
11252d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
112697963794af1e18674dd111e3ad344d90b16c922cHung-ying Tyan        private void endCallOnError(int errorCode, String message) {
11272d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            reset();
112897963794af1e18674dd111e3ad344d90b16c922cHung-ying Tyan            mProxy.onError(this, errorCode, message);
1129903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan        }
1130903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan
1131903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan        private void endCallOnBusy() {
1132903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan            reset();
1133903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan            mProxy.onCallBusy(this);
11342d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
11352d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
113697963794af1e18674dd111e3ad344d90b16c922cHung-ying Tyan        private void onError(int errorCode, String message) {
11379352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan            cancelSessionTimer();
11383d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan            switch (mState) {
113984a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                case SipSession.State.REGISTERING:
114084a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                case SipSession.State.DEREGISTERING:
11413d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                    onRegistrationFailed(errorCode, message);
11423d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                    break;
11433d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                default:
1144d231aa880ab006d51ffe03454c1fc082f1c97bb8Hung-ying Tyan                    if ((errorCode != SipErrorCode.DATA_CONNECTION_LOST)
1145d231aa880ab006d51ffe03454c1fc082f1c97bb8Hung-ying Tyan                            && mInCall) {
11463d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                        fallbackToPreviousInCall(errorCode, message);
11473d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                    } else {
11483d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                        endCallOnError(errorCode, message);
11493d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                    }
11502d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
11512d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
11522d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
11533d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan
11543d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan        private void onError(Throwable exception) {
11553d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan            exception = getRootCause(exception);
11563d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan            onError(getErrorCode(exception), exception.toString());
11573d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan        }
11583d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan
1159903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan        private void onError(Response response) {
11603d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan            int statusCode = response.getStatusCode();
1161ae076d3981fda732d54b6c6e37e5659b2e7ba130Hung-ying Tyan            if (!mInCall && (statusCode == Response.BUSY_HERE)) {
11623d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                endCallOnBusy();
1163903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan            } else {
11643d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                onError(getErrorCode(statusCode), createErrorMessage(response));
1165903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan            }
1166903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan        }
1167903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan
116897963794af1e18674dd111e3ad344d90b16c922cHung-ying Tyan        private int getErrorCode(int responseStatusCode) {
1169903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan            switch (responseStatusCode) {
1170ae076d3981fda732d54b6c6e37e5659b2e7ba130Hung-ying Tyan                case Response.TEMPORARILY_UNAVAILABLE:
1171ae076d3981fda732d54b6c6e37e5659b2e7ba130Hung-ying Tyan                case Response.FORBIDDEN:
1172ae076d3981fda732d54b6c6e37e5659b2e7ba130Hung-ying Tyan                case Response.GONE:
1173903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                case Response.NOT_FOUND:
1174ae076d3981fda732d54b6c6e37e5659b2e7ba130Hung-ying Tyan                case Response.NOT_ACCEPTABLE:
1175ae076d3981fda732d54b6c6e37e5659b2e7ba130Hung-ying Tyan                case Response.NOT_ACCEPTABLE_HERE:
1176ae076d3981fda732d54b6c6e37e5659b2e7ba130Hung-ying Tyan                    return SipErrorCode.PEER_NOT_REACHABLE;
1177ae076d3981fda732d54b6c6e37e5659b2e7ba130Hung-ying Tyan
1178ae076d3981fda732d54b6c6e37e5659b2e7ba130Hung-ying Tyan                case Response.REQUEST_URI_TOO_LONG:
1179903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                case Response.ADDRESS_INCOMPLETE:
1180ae076d3981fda732d54b6c6e37e5659b2e7ba130Hung-ying Tyan                case Response.AMBIGUOUS:
1181903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                    return SipErrorCode.INVALID_REMOTE_URI;
1182ae076d3981fda732d54b6c6e37e5659b2e7ba130Hung-ying Tyan
1183903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                case Response.REQUEST_TIMEOUT:
1184903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                    return SipErrorCode.TIME_OUT;
1185ae076d3981fda732d54b6c6e37e5659b2e7ba130Hung-ying Tyan
1186903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                default:
1187903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                    if (responseStatusCode < 500) {
1188903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                        return SipErrorCode.CLIENT_ERROR;
1189903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                    } else {
1190903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                        return SipErrorCode.SERVER_ERROR;
1191903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                    }
1192903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan            }
1193903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan        }
1194903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan
1195903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan        private Throwable getRootCause(Throwable exception) {
1196903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan            Throwable cause = exception.getCause();
1197903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan            while (cause != null) {
1198903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                exception = cause;
1199903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                cause = exception.getCause();
1200903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan            }
1201903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan            return exception;
1202903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan        }
1203903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan
120497963794af1e18674dd111e3ad344d90b16c922cHung-ying Tyan        private int getErrorCode(Throwable exception) {
1205903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan            String message = exception.getMessage();
1206903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan            if (exception instanceof UnknownHostException) {
1207903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                return SipErrorCode.INVALID_REMOTE_URI;
1208903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan            } else if (exception instanceof IOException) {
1209903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                return SipErrorCode.SOCKET_ERROR;
1210903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan            } else {
1211903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                return SipErrorCode.CLIENT_ERROR;
1212903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan            }
1213903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan        }
1214903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan
12152d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private void onRegistrationDone(int duration) {
12163d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan            reset();
12172d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            mProxy.onRegistrationDone(this, duration);
12182d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
12192d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
122097963794af1e18674dd111e3ad344d90b16c922cHung-ying Tyan        private void onRegistrationFailed(int errorCode, String message) {
12213d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan            reset();
122297963794af1e18674dd111e3ad344d90b16c922cHung-ying Tyan            mProxy.onRegistrationFailed(this, errorCode, message);
1223903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan        }
1224903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan
12252d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private void onRegistrationFailed(Throwable exception) {
12263d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan            reset();
1227903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan            exception = getRootCause(exception);
1228903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan            onRegistrationFailed(getErrorCode(exception),
1229903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                    exception.toString());
12302d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
12313d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan
12323d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan        private void onRegistrationFailed(Response response) {
12333d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan            reset();
12343d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan            int statusCode = response.getStatusCode();
12353d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan            onRegistrationFailed(getErrorCode(statusCode),
12363d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                    createErrorMessage(response));
12373d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan        }
12382d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
12392d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
12402d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    /**
12412d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang     * @return true if the event is a request event matching the specified
12422d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang     *      method; false otherwise
12432d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang     */
12442d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private static boolean isRequestEvent(String method, EventObject event) {
12452d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        try {
12462d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            if (event instanceof RequestEvent) {
12472d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                RequestEvent requestEvent = (RequestEvent) event;
12482d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return method.equals(requestEvent.getRequest().getMethod());
12492d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
12502d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        } catch (Throwable e) {
12512d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
12522d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        return false;
12532d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
12542d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
12552d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private static String getCseqMethod(Message message) {
12562d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        return ((CSeqHeader) message.getHeader(CSeqHeader.NAME)).getMethod();
12572d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
12582d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
12592d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    /**
12602d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang     * @return true if the event is a response event and the CSeqHeader method
12612d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang     * match the given arguments; false otherwise
12622d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang     */
12632d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private static boolean expectResponse(
12642d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            String expectedMethod, EventObject evt) {
12652d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        if (evt instanceof ResponseEvent) {
12662d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            ResponseEvent event = (ResponseEvent) evt;
12672d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            Response response = event.getResponse();
12682d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return expectedMethod.equalsIgnoreCase(getCseqMethod(response));
12692d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
12702d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        return false;
12712d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
12722d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
12732d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    /**
12742d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang     * @return true if the event is a response event and the response code and
12752d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang     *      CSeqHeader method match the given arguments; false otherwise
12762d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang     */
12772d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private static boolean expectResponse(
12782d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            int responseCode, String expectedMethod, EventObject evt) {
12792d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        if (evt instanceof ResponseEvent) {
12802d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            ResponseEvent event = (ResponseEvent) evt;
12812d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            Response response = event.getResponse();
12822d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            if (response.getStatusCode() == responseCode) {
12832d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return expectedMethod.equalsIgnoreCase(getCseqMethod(response));
12842d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
12852d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
12862d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        return false;
12872d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
12882d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
12892d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private static SipProfile createPeerProfile(Request request)
12902d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            throws SipException {
12912d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        try {
12922d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            FromHeader fromHeader =
12932d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    (FromHeader) request.getHeader(FromHeader.NAME);
12942d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            Address address = fromHeader.getAddress();
12952d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            SipURI uri = (SipURI) address.getURI();
12962d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            String username = uri.getUser();
12972d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            if (username == null) username = ANONYMOUS;
12982d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return new SipProfile.Builder(username, uri.getHost())
12992d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    .setPort(uri.getPort())
13002d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    .setDisplayName(address.getDisplayName())
13012d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    .build();
130299bf4e45c4566172189735b34b368b76660ca57aHung-ying Tyan        } catch (IllegalArgumentException e) {
13032d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            throw new SipException("createPeerProfile()", e);
13042d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        } catch (ParseException e) {
13052d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            throw new SipException("createPeerProfile()", e);
13062d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
13072d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
13082d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
1309c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan    private static boolean isLoggable(SipSessionImpl s) {
1310c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan        if (s != null) {
1311c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan            switch (s.mState) {
131284a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                case SipSession.State.PINGING:
1313c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan                    return DEBUG_PING;
1314c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan            }
1315c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan        }
1316c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan        return DEBUG;
1317c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan    }
1318c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan
1319c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan    private static boolean isLoggable(SipSessionImpl s, EventObject evt) {
1320c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan        if (!isLoggable(s)) return false;
1321c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan        if (evt == null) return false;
1322c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan
1323c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan        if (evt instanceof OptionsCommand) {
1324c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan            return DEBUG_PING;
1325c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan        } else if (evt instanceof ResponseEvent) {
1326c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan            Response response = ((ResponseEvent) evt).getResponse();
1327c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan            if (Request.OPTIONS.equals(response.getHeader(CSeqHeader.NAME))) {
1328c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan                return DEBUG_PING;
1329c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan            }
1330c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan            return DEBUG;
1331c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan        } else if (evt instanceof RequestEvent) {
1332c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan            return DEBUG;
1333c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan        }
1334c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan        return false;
1335c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan    }
1336c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan
13372d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private static String log(EventObject evt) {
13382d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        if (evt instanceof RequestEvent) {
13392d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return ((RequestEvent) evt).getRequest().toString();
13402d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        } else if (evt instanceof ResponseEvent) {
13412d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return ((ResponseEvent) evt).getResponse().toString();
13422d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        } else {
13432d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return evt.toString();
13442d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
13452d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
13462d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
13472d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private class OptionsCommand extends EventObject {
13482d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public OptionsCommand() {
13492d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            super(SipSessionGroup.this);
13502d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
13512d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
13522d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
13532d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private class RegisterCommand extends EventObject {
13542d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private int mDuration;
13552d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
13562d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public RegisterCommand(int duration) {
13572d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            super(SipSessionGroup.this);
13582d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            mDuration = duration;
13592d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
13602d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
13612d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public int getDuration() {
13622d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return mDuration;
13632d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
13642d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
13652d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
13662d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private class MakeCallCommand extends EventObject {
136795b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh        private String mSessionDescription;
13689352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan        private int mTimeout; // in seconds
13692d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
13702d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public MakeCallCommand(SipProfile peerProfile,
137195b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh                String sessionDescription) {
13729352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan            this(peerProfile, sessionDescription, -1);
13739352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan        }
13749352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan
13759352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan        public MakeCallCommand(SipProfile peerProfile,
13769352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                String sessionDescription, int timeout) {
13772d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            super(peerProfile);
13782d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            mSessionDescription = sessionDescription;
13799352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan            mTimeout = timeout;
13802d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
13812d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
13822d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public SipProfile getPeerProfile() {
13832d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return (SipProfile) getSource();
13842d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
13852d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
138695b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh        public String getSessionDescription() {
13872d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return mSessionDescription;
13882d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
13892d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
13909352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan        public int getTimeout() {
13919352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan            return mTimeout;
13929352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan        }
13939352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan    }
13942d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang}
1395