SipSessionGroup.java revision 7d137e40cd36290c6bfb5beaf66f4018ae92c97f
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;
282d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport android.net.sip.SessionDescription;
29903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyanimport android.net.sip.SipErrorCode;
302d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport android.net.sip.SipProfile;
312d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport android.net.sip.SipSessionAdapter;
322d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport android.net.sip.SipSessionState;
332d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport android.text.TextUtils;
342d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport android.util.Log;
352d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
362d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport java.io.IOException;
3795b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yehimport java.io.UnsupportedEncodingException;
382d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport java.net.DatagramSocket;
39903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyanimport java.net.UnknownHostException;
402d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport java.text.ParseException;
412d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport java.util.Collection;
422d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport java.util.EventObject;
432d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport java.util.HashMap;
442d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport java.util.Map;
452d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport java.util.Properties;
462d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport java.util.TooManyListenersException;
472d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
482d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.ClientTransaction;
492d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.Dialog;
502d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.DialogTerminatedEvent;
512d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.IOExceptionEvent;
522d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.InvalidArgumentException;
532d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.ListeningPoint;
542d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.RequestEvent;
552d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.ResponseEvent;
562d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.ServerTransaction;
572d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.SipException;
582d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.SipFactory;
592d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.SipListener;
602d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.SipProvider;
612d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.SipStack;
622d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.TimeoutEvent;
632d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.Transaction;
642d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.TransactionState;
652d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.TransactionTerminatedEvent;
66903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyanimport javax.sip.TransactionUnavailableException;
672d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.address.Address;
682d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.address.SipURI;
692d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.header.CSeqHeader;
702d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.header.ExpiresHeader;
712d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.header.FromHeader;
722d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.header.MinExpiresHeader;
732d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.header.ViaHeader;
742d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.message.Message;
752d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.message.Request;
762d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.message.Response;
772d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
782d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang/**
792d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang * Manages {@link ISipSession}'s for a SIP account.
802d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang */
812d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangclass SipSessionGroup implements SipListener {
822d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private static final String TAG = "SipSession";
832d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private static final String ANONYMOUS = "anonymous";
84903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan    private static final String SERVER_ERROR_PREFIX = "Response: ";
852d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private static final int EXPIRY_TIME = 3600;
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
1212d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    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);
221d231aa880ab006d51ffe03454c1fc082f1c97bb8Hung-ying Tyan        Log.d(TAG, "sesssion key from event: " + key);
222d231aa880ab006d51ffe03454c1fc082f1c97bb8Hung-ying Tyan        Log.d(TAG, "active sessions:");
2232d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        for (String k : mSessionMap.keySet()) {
224d231aa880ab006d51ffe03454c1fc082f1c97bb8Hung-ying Tyan            Log.d(TAG, " ..." + k + ": " + mSessionMap.get(k));
2252d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
2262d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        SipSessionImpl session = mSessionMap.get(key);
2272d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        return ((session != null) ? session : mCallReceiverSession);
2282d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
2292d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
2302d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private synchronized void addSipSession(SipSessionImpl newSession) {
2312d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        removeSipSession(newSession);
2322d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        String key = newSession.getCallId();
233d231aa880ab006d51ffe03454c1fc082f1c97bb8Hung-ying Tyan        Log.d(TAG, "+++  add a session with key:  '" + key + "'");
2342d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        mSessionMap.put(key, newSession);
2352d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        for (String k : mSessionMap.keySet()) {
2362d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            Log.d(TAG, "   .....  " + k + ": " + mSessionMap.get(k));
2372d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
2382d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
2392d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
2402d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private synchronized void removeSipSession(SipSessionImpl session) {
2412d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        if (session == mCallReceiverSession) return;
2422d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        String key = session.getCallId();
2432d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        SipSessionImpl s = mSessionMap.remove(key);
2442d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        // sanity check
2452d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        if ((s != null) && (s != session)) {
2462d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            Log.w(TAG, "session " + session + " is not associated with key '"
2472d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    + key + "'");
2482d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            mSessionMap.put(key, s);
2492d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            for (Map.Entry<String, SipSessionImpl> entry
2502d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    : mSessionMap.entrySet()) {
2512d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                if (entry.getValue() == s) {
2522d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    key = entry.getKey();
2532d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    mSessionMap.remove(key);
2542d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                }
2552d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
2562d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
2572d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        Log.d(TAG, "   remove session " + session + " with key '" + key + "'");
2582d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
2592d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        for (String k : mSessionMap.keySet()) {
2602d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            Log.d(TAG, "   .....  " + k + ": " + mSessionMap.get(k));
2612d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
2622d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
2632d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
2642d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    public void processRequest(RequestEvent event) {
2652d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        process(event);
2662d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
2672d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
2682d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    public void processResponse(ResponseEvent event) {
2692d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        process(event);
2702d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
2712d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
2722d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    public void processIOException(IOExceptionEvent event) {
2732d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        process(event);
2742d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
2752d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
2762d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    public void processTimeout(TimeoutEvent event) {
2772d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        process(event);
2782d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
2792d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
2802d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    public void processTransactionTerminated(TransactionTerminatedEvent event) {
2812d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        process(event);
2822d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
2832d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
2842d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    public void processDialogTerminated(DialogTerminatedEvent event) {
2852d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        process(event);
2862d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
2872d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
2882d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private synchronized void process(EventObject event) {
2892d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        SipSessionImpl session = getSipSession(event);
2902d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        try {
2912d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            if ((session != null) && session.process(event)) {
2922d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                Log.d(TAG, " ~~~~~   new state: " + session.mState);
2932d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            } else {
2942d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                Log.d(TAG, "event not processed: " + event);
2952d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
2962d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        } catch (Throwable e) {
297903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan            Log.w(TAG, "event process error: " + event, e);
2982d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            session.onError(e);
2992d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
3002d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
3012d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
30295b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh    private String extractContent(Message message) {
30395b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh        // Currently we do not support secure MIME bodies.
30495b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh        byte[] bytes = message.getRawContent();
30595b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh        if (bytes != null) {
30695b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh            try {
30795b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh                if (message instanceof SIPMessage) {
30895b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh                    return ((SIPMessage) message).getMessageContent();
30995b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh                } else {
31095b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh                    return new String(bytes, "UTF-8");
31195b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh                }
31295b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh            } catch (UnsupportedEncodingException e) {
31395b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh            }
31495b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh        }
31595b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh        return null;
31695b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh    }
31795b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh
3182d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private class SipSessionCallReceiverImpl extends SipSessionImpl {
3192d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public SipSessionCallReceiverImpl(ISipSessionListener listener) {
3202d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            super(listener);
3212d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
3222d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
3232d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public boolean process(EventObject evt) throws SipException {
3242d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            Log.d(TAG, " ~~~~~   " + this + ": " + mState + ": processing "
3252d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    + log(evt));
3262d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            if (isRequestEvent(Request.INVITE, evt)) {
3272d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                RequestEvent event = (RequestEvent) evt;
3282d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                SipSessionImpl newSession = new SipSessionImpl(mProxy);
3292d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                newSession.mServerTransaction = mSipHelper.sendRinging(event,
3302d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        generateTag());
3312d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                newSession.mDialog = newSession.mServerTransaction.getDialog();
3322d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                newSession.mInviteReceived = event;
3332d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                newSession.mPeerProfile = createPeerProfile(event.getRequest());
3342d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                newSession.mState = SipSessionState.INCOMING_CALL;
3352d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                newSession.mPeerSessionDescription =
33695b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh                        extractContent(event.getRequest());
3372d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                addSipSession(newSession);
3382d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mProxy.onRinging(newSession, newSession.mPeerProfile,
3392d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        newSession.mPeerSessionDescription);
3402d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return true;
3412d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            } else {
3422d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return false;
3432d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
3442d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
3452d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
3462d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
3472d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    class SipSessionImpl extends ISipSession.Stub {
3482d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        SipProfile mPeerProfile;
3492d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        SipSessionListenerProxy mProxy = new SipSessionListenerProxy();
3502d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        SipSessionState mState = SipSessionState.READY_TO_CALL;
3512d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        RequestEvent mInviteReceived;
3522d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        Dialog mDialog;
3532d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        ServerTransaction mServerTransaction;
3542d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        ClientTransaction mClientTransaction;
35595b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh        String mPeerSessionDescription;
3562d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        boolean mInCall;
3572d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        boolean mReRegisterFlag = false;
3582d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
3592d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public SipSessionImpl(ISipSessionListener listener) {
3602d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            setListener(listener);
3612d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
3622d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
3632d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        SipSessionImpl duplicate() {
3642d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return new SipSessionImpl(mProxy.getListener());
3652d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
3662d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
3672d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private void reset() {
3682d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            mInCall = false;
3692d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            removeSipSession(this);
3702d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            mPeerProfile = null;
3712d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            mState = SipSessionState.READY_TO_CALL;
3722d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            mInviteReceived = null;
3732d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            mDialog = null;
3742d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            mServerTransaction = null;
3752d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            mClientTransaction = null;
3762d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            mPeerSessionDescription = null;
3772d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
3782d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
3792d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public boolean isInCall() {
3802d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return mInCall;
3812d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
3822d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
3832d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public String getLocalIp() {
3842d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return mLocalIp;
3852d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
3862d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
3872d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public SipProfile getLocalProfile() {
3882d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return mLocalProfile;
3892d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
3902d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
3912d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public SipProfile getPeerProfile() {
3922d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return mPeerProfile;
3932d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
3942d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
3952d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public String getCallId() {
3962d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return SipHelper.getCallId(getTransaction());
3972d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
3982d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
3992d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private Transaction getTransaction() {
4002d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            if (mClientTransaction != null) return mClientTransaction;
4012d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            if (mServerTransaction != null) return mServerTransaction;
4022d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return null;
4032d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
4042d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
4052d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public String getState() {
4062d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return mState.toString();
4072d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
4082d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
4092d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public void setListener(ISipSessionListener listener) {
4102d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            mProxy.setListener((listener instanceof SipSessionListenerProxy)
4112d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    ? ((SipSessionListenerProxy) listener).getListener()
4122d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    : listener);
4132d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
4142d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
415dba514c6d8e8263d4b8f31cb2fdebfc1d4f84c35Hung-ying Tyan        // process the command in a new thread
416dba514c6d8e8263d4b8f31cb2fdebfc1d4f84c35Hung-ying Tyan        private void doCommandAsync(final EventObject command) {
417dba514c6d8e8263d4b8f31cb2fdebfc1d4f84c35Hung-ying Tyan            new Thread(new Runnable() {
418dba514c6d8e8263d4b8f31cb2fdebfc1d4f84c35Hung-ying Tyan                    public void run() {
419dba514c6d8e8263d4b8f31cb2fdebfc1d4f84c35Hung-ying Tyan                        try {
420dba514c6d8e8263d4b8f31cb2fdebfc1d4f84c35Hung-ying Tyan                            processCommand(command);
421dba514c6d8e8263d4b8f31cb2fdebfc1d4f84c35Hung-ying Tyan                        } catch (SipException e) {
422903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                            Log.w(TAG, "command error: " + command, e);
4233d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                            onError(e);
424dba514c6d8e8263d4b8f31cb2fdebfc1d4f84c35Hung-ying Tyan                        }
425dba514c6d8e8263d4b8f31cb2fdebfc1d4f84c35Hung-ying Tyan                    }
426dba514c6d8e8263d4b8f31cb2fdebfc1d4f84c35Hung-ying Tyan            }).start();
427dba514c6d8e8263d4b8f31cb2fdebfc1d4f84c35Hung-ying Tyan        }
428dba514c6d8e8263d4b8f31cb2fdebfc1d4f84c35Hung-ying Tyan
4292d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public void makeCall(SipProfile peerProfile,
43095b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh                String sessionDescription) {
431dba514c6d8e8263d4b8f31cb2fdebfc1d4f84c35Hung-ying Tyan            doCommandAsync(
432dba514c6d8e8263d4b8f31cb2fdebfc1d4f84c35Hung-ying Tyan                    new MakeCallCommand(peerProfile, sessionDescription));
4332d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
4342d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
43595b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh        public void answerCall(String sessionDescription) {
4362d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            try {
4372d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                processCommand(
4382d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        new MakeCallCommand(mPeerProfile, sessionDescription));
4392d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            } catch (SipException e) {
4402d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                onError(e);
4412d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
4422d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
4432d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
4442d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public void endCall() {
445dba514c6d8e8263d4b8f31cb2fdebfc1d4f84c35Hung-ying Tyan            doCommandAsync(END_CALL);
4462d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
4472d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
44895b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh        public void changeCall(String sessionDescription) {
449dba514c6d8e8263d4b8f31cb2fdebfc1d4f84c35Hung-ying Tyan            doCommandAsync(
450dba514c6d8e8263d4b8f31cb2fdebfc1d4f84c35Hung-ying Tyan                    new MakeCallCommand(mPeerProfile, sessionDescription));
4512d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
4522d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
4532d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public void register(int duration) {
454dba514c6d8e8263d4b8f31cb2fdebfc1d4f84c35Hung-ying Tyan            doCommandAsync(new RegisterCommand(duration));
4552d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
4562d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
4572d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public void unregister() {
458dba514c6d8e8263d4b8f31cb2fdebfc1d4f84c35Hung-ying Tyan            doCommandAsync(DEREGISTER);
4592d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
4602d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
4612d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public boolean isReRegisterRequired() {
4622d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return mReRegisterFlag;
4632d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
4642d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
4652d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public void clearReRegisterRequired() {
4662d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            mReRegisterFlag = false;
4672d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
4682d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
4692d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public void sendKeepAlive() {
4702d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            mState = SipSessionState.PINGING;
4712d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            try {
4722d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                processCommand(new OptionsCommand());
4732d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                while (SipSessionState.PINGING.equals(mState)) {
4742d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    Thread.sleep(1000);
4752d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                }
4762d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            } catch (SipException e) {
4772d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                Log.e(TAG, "sendKeepAlive failed", e);
4782d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            } catch (InterruptedException e) {
4792d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                Log.e(TAG, "sendKeepAlive interrupted", e);
4802d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
4812d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
4822d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
4832d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private void processCommand(EventObject command) throws SipException {
4842d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            if (!process(command)) {
4853d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                onError(SipErrorCode.IN_PROGRESS,
4863d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                        "cannot initiate a new transaction to execute: "
4873d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                        + command);
4882d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
4892d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
4902d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
4912d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        protected String generateTag() {
4922d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            // 32-bit randomness
4932d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return String.valueOf((long) (Math.random() * 0x100000000L));
4942d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
4952d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
4962d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public String toString() {
4972d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            try {
4982d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                String s = super.toString();
4992d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return s.substring(s.indexOf("@")) + ":" + mState;
5002d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            } catch (Throwable e) {
5012d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return super.toString();
5022d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
5032d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
5042d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
5052d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public boolean process(EventObject evt) throws SipException {
5062d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            Log.d(TAG, " ~~~~~   " + this + ": " + mState + ": processing "
5072d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    + log(evt));
5082d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            synchronized (SipSessionGroup.this) {
5092d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                if (isClosed()) return false;
5102d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
5112d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                Dialog dialog = null;
5122d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                if (evt instanceof RequestEvent) {
5132d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    dialog = ((RequestEvent) evt).getDialog();
5142d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                } else if (evt instanceof ResponseEvent) {
5152d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    dialog = ((ResponseEvent) evt).getDialog();
5162d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                }
5172d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                if (dialog != null) mDialog = dialog;
5182d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
5192d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                boolean processed;
5202d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
5212d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                switch (mState) {
5222d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                case REGISTERING:
5232d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                case DEREGISTERING:
5242d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    processed = registeringToReady(evt);
5252d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    break;
5262d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                case PINGING:
5272d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    processed = keepAliveProcess(evt);
5282d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    break;
5292d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                case READY_TO_CALL:
5302d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    processed = readyForCall(evt);
5312d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    break;
5322d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                case INCOMING_CALL:
5332d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    processed = incomingCall(evt);
5342d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    break;
5352d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                case INCOMING_CALL_ANSWERING:
5362d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    processed = incomingCallToInCall(evt);
5372d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    break;
5382d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                case OUTGOING_CALL:
5392d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                case OUTGOING_CALL_RING_BACK:
5402d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    processed = outgoingCall(evt);
5412d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    break;
5422d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                case OUTGOING_CALL_CANCELING:
5432d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    processed = outgoingCallToReady(evt);
5442d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    break;
5452d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                case IN_CALL:
5462d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    processed = inCall(evt);
5472d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    break;
5482d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                default:
5492d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    processed = false;
5502d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                }
5512d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return (processed || processExceptions(evt));
5522d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
5532d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
5542d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
5552d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private boolean processExceptions(EventObject evt) throws SipException {
5562d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            if (isRequestEvent(Request.BYE, evt)) {
5572d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                // terminate the call whenever a BYE is received
5582d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mSipHelper.sendResponse((RequestEvent) evt, Response.OK);
5592d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                endCallNormally();
5602d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return true;
5612d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            } else if (isRequestEvent(Request.CANCEL, evt)) {
5622d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mSipHelper.sendResponse((RequestEvent) evt,
5632d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        Response.CALL_OR_TRANSACTION_DOES_NOT_EXIST);
5642d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return true;
5652d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            } else if (evt instanceof TransactionTerminatedEvent) {
5662d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                if (evt instanceof TimeoutEvent) {
5672d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    processTimeout((TimeoutEvent) evt);
5682d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                } else {
5693d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                    processTransactionTerminated(
5703d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                            (TransactionTerminatedEvent) evt);
5712d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                }
5722d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return true;
5732d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            } else if (evt instanceof DialogTerminatedEvent) {
5742d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                processDialogTerminated((DialogTerminatedEvent) evt);
5752d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return true;
5762d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
5772d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return false;
5782d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
5792d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
5802d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private void processDialogTerminated(DialogTerminatedEvent event) {
5812d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            if (mDialog == event.getDialog()) {
5822d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                onError(new SipException("dialog terminated"));
5832d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            } else {
5842d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                Log.d(TAG, "not the current dialog; current=" + mDialog
5852d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        + ", terminated=" + event.getDialog());
5862d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
5872d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
5882d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
5893d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan        private void processTransactionTerminated(
5903d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                TransactionTerminatedEvent event) {
5913d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan            switch (mState) {
5923d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                case IN_CALL:
5933d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                case READY_TO_CALL:
5943d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                    Log.d(TAG, "Transaction terminated; do nothing");
5953d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                    break;
5963d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                default:
5973d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                    Log.d(TAG, "Transaction terminated early: " + this);
5983d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                    onError(SipErrorCode.TRANSACTION_TERMINTED,
5993d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                            "transaction terminated");
6003d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan            }
6013d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan        }
6023d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan
6032d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private void processTimeout(TimeoutEvent event) {
6042d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            Log.d(TAG, "processing Timeout..." + event);
6052d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            Transaction current = event.isServerTransaction()
6062d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    ? mServerTransaction
6072d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    : mClientTransaction;
6082d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            Transaction target = event.isServerTransaction()
6092d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    ? event.getServerTransaction()
6102d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    : event.getClientTransaction();
6112d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
6122d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            if ((current != target) && (mState != SipSessionState.PINGING)) {
6132d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                Log.d(TAG, "not the current transaction; current=" + current
6142d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        + ", timed out=" + target);
6152d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return;
6162d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
6172d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            switch (mState) {
6183d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                case REGISTERING:
6193d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                case DEREGISTERING:
6203d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                    reset();
6213d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                    mProxy.onRegistrationTimeout(this);
6223d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                    break;
6233d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                case INCOMING_CALL:
6243d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                case INCOMING_CALL_ANSWERING:
6253d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                case OUTGOING_CALL:
6263d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                case OUTGOING_CALL_CANCELING:
6273d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                    onError(SipErrorCode.TIME_OUT, event.toString());
6283d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                    break;
6293d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                case PINGING:
6303d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                    reset();
6313d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                    mReRegisterFlag = true;
6323d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                    mState = SipSessionState.READY_TO_CALL;
6333d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                    break;
6342d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
6353d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                default:
6363d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                    Log.d(TAG, "   do nothing");
6373d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                    break;
6382d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
6392d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
6402d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
6412d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private int getExpiryTime(Response response) {
6422d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            int expires = EXPIRY_TIME;
6432d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            ExpiresHeader expiresHeader = (ExpiresHeader)
6442d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    response.getHeader(ExpiresHeader.NAME);
6452d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            if (expiresHeader != null) expires = expiresHeader.getExpires();
6462d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            expiresHeader = (ExpiresHeader)
6472d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    response.getHeader(MinExpiresHeader.NAME);
6482d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            if (expiresHeader != null) {
6492d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                expires = Math.max(expires, expiresHeader.getExpires());
6502d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
6512d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return expires;
6522d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
6532d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
6542d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private boolean keepAliveProcess(EventObject evt) throws SipException {
6552d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            if (evt instanceof OptionsCommand) {
6562d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mClientTransaction = mSipHelper.sendKeepAlive(mLocalProfile,
6572d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        generateTag());
6582d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mDialog = mClientTransaction.getDialog();
6592d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                addSipSession(this);
6602d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return true;
6612d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            } else if (evt instanceof ResponseEvent) {
6622d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return parseOptionsResult(evt);
6632d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
6642d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return false;
6652d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
6662d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
6672d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private boolean parseOptionsResult(EventObject evt) {
6682d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            if (expectResponse(Request.OPTIONS, evt)) {
6692d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                ResponseEvent event = (ResponseEvent) evt;
6702d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                int rPort = getRPortFromResponse(event.getResponse());
6712d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                if (rPort != -1) {
6722d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    if (mRPort == 0) mRPort = rPort;
6732d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    if (mRPort != rPort) {
6742d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        mReRegisterFlag = true;
6752d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        Log.w(TAG, String.format("rport is changed: %d <> %d",
6762d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                                mRPort, rPort));
6772d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        mRPort = rPort;
6782d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    } else {
6792d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        Log.w(TAG, "rport is the same: " + rPort);
6802d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    }
6812d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                } else {
6822d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    Log.w(TAG, "peer did not respect our rport request");
6832d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                }
6843d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                reset();
6852d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return true;
6862d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
6872d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return false;
6882d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
6892d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
6902d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private int getRPortFromResponse(Response response) {
6912d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            ViaHeader viaHeader = (ViaHeader)(response.getHeader(
6922d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    SIPHeaderNames.VIA));
6932d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return (viaHeader == null) ? -1 : viaHeader.getRPort();
6942d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
6952d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
6962d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private boolean registeringToReady(EventObject evt)
6972d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                throws SipException {
6982d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            if (expectResponse(Request.REGISTER, evt)) {
6992d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                ResponseEvent event = (ResponseEvent) evt;
7002d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                Response response = event.getResponse();
7012d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
7022d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                int statusCode = response.getStatusCode();
7032d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                switch (statusCode) {
7042d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                case Response.OK:
7052d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    SipSessionState state = mState;
7062d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    onRegistrationDone((state == SipSessionState.REGISTERING)
7072d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                            ? getExpiryTime(((ResponseEvent) evt).getResponse())
7082d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                            : -1);
7092d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    mLastNonce = null;
7102d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    mRPort = 0;
7112d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    return true;
7122d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                case Response.UNAUTHORIZED:
7132d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                case Response.PROXY_AUTHENTICATION_REQUIRED:
714903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                    if (!handleAuthentication(event)) {
7152d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        Log.v(TAG, "Incorrect username/password");
716903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                        onRegistrationFailed(SipErrorCode.INVALID_CREDENTIALS,
717903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                                "incorrect username or password");
7182d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    }
7192d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    return true;
7202d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                default:
7212d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    if (statusCode >= 500) {
7223d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                        onRegistrationFailed(response);
7232d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        return true;
7242d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    }
7252d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                }
7262d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
7272d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return false;
7282d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
7292d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
730903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan        private boolean handleAuthentication(ResponseEvent event)
731903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                throws SipException {
732903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan            Response response = event.getResponse();
733903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan            String nonce = getNonceFromResponse(response);
734903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan            if (((nonce != null) && nonce.equals(mLastNonce)) ||
7357d137e40cd36290c6bfb5beaf66f4018ae92c97fChung-yih Wang                    (nonce == null)) {
7367d137e40cd36290c6bfb5beaf66f4018ae92c97fChung-yih Wang                mLastNonce = nonce;
737903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                return false;
738903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan            } else {
739903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                mClientTransaction = mSipHelper.handleChallenge(
740903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                        event, getAccountManager());
741903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                mDialog = mClientTransaction.getDialog();
742903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                mLastNonce = nonce;
743903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                return true;
744903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan            }
745903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan        }
746903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan
7472d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private AccountManager getAccountManager() {
7482d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return new AccountManager() {
7492d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                public UserCredentials getCredentials(ClientTransaction
7502d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        challengedTransaction, String realm) {
7512d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    return new UserCredentials() {
7522d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        public String getUserName() {
7532d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                            return mLocalProfile.getUserName();
7542d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        }
7552d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
7562d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        public String getPassword() {
7572d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                            return mPassword;
7582d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        }
7592d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
7602d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        public String getSipDomain() {
7612d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                            return mLocalProfile.getSipDomain();
7622d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        }
7632d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    };
7642d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                }
7652d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            };
7662d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
7672d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
7682d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private String getNonceFromResponse(Response response) {
7697d137e40cd36290c6bfb5beaf66f4018ae92c97fChung-yih Wang            WWWAuthenticate wwwAuth = (WWWAuthenticate)response.getHeader(
7707d137e40cd36290c6bfb5beaf66f4018ae92c97fChung-yih Wang                    SIPHeaderNames.WWW_AUTHENTICATE);
7717d137e40cd36290c6bfb5beaf66f4018ae92c97fChung-yih Wang            if (wwwAuth != null) return wwwAuth.getNonce();
7727d137e40cd36290c6bfb5beaf66f4018ae92c97fChung-yih Wang            ProxyAuthenticate proxyAuth = (ProxyAuthenticate)response.getHeader(
7737d137e40cd36290c6bfb5beaf66f4018ae92c97fChung-yih Wang                    SIPHeaderNames.PROXY_AUTHENTICATE);
7747d137e40cd36290c6bfb5beaf66f4018ae92c97fChung-yih Wang            return (proxyAuth == null) ? null : proxyAuth.getNonce();
7752d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
7762d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
7772d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private boolean readyForCall(EventObject evt) throws SipException {
7782d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            // expect MakeCallCommand, RegisterCommand, DEREGISTER
7792d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            if (evt instanceof MakeCallCommand) {
7802d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                MakeCallCommand cmd = (MakeCallCommand) evt;
7812d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mPeerProfile = cmd.getPeerProfile();
7822d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mClientTransaction = mSipHelper.sendInvite(mLocalProfile,
78395b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh                        mPeerProfile, cmd.getSessionDescription(),
78495b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh                        generateTag());
7852d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mDialog = mClientTransaction.getDialog();
7862d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                addSipSession(this);
7872d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mState = SipSessionState.OUTGOING_CALL;
7882d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mProxy.onCalling(this);
7892d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return true;
7902d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            } else if (evt instanceof RegisterCommand) {
7912d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                int duration = ((RegisterCommand) evt).getDuration();
7922d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mClientTransaction = mSipHelper.sendRegister(mLocalProfile,
7932d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        generateTag(), duration);
7942d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mDialog = mClientTransaction.getDialog();
7952d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                addSipSession(this);
7962d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mState = SipSessionState.REGISTERING;
7972d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mProxy.onRegistering(this);
7982d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return true;
7992d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            } else if (DEREGISTER == evt) {
8002d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mClientTransaction = mSipHelper.sendRegister(mLocalProfile,
8012d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        generateTag(), 0);
8022d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mDialog = mClientTransaction.getDialog();
8032d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                addSipSession(this);
8042d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mState = SipSessionState.DEREGISTERING;
8052d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mProxy.onRegistering(this);
8062d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return true;
8072d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
8082d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return false;
8092d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
8102d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
8112d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private boolean incomingCall(EventObject evt) throws SipException {
8122d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            // expect MakeCallCommand(answering) , END_CALL cmd , Cancel
8132d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            if (evt instanceof MakeCallCommand) {
8142d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                // answer call
8152d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mServerTransaction = mSipHelper.sendInviteOk(mInviteReceived,
8162d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        mLocalProfile,
8172d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        ((MakeCallCommand) evt).getSessionDescription(),
8182d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        mServerTransaction);
8192d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mState = SipSessionState.INCOMING_CALL_ANSWERING;
8202d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return true;
8212d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            } else if (END_CALL == evt) {
8222d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mSipHelper.sendInviteBusyHere(mInviteReceived,
8232d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        mServerTransaction);
8242d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                endCallNormally();
8252d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return true;
8262d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            } else if (isRequestEvent(Request.CANCEL, evt)) {
8272d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                RequestEvent event = (RequestEvent) evt;
8282d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mSipHelper.sendResponse(event, Response.OK);
8292d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mSipHelper.sendInviteRequestTerminated(
8302d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        mInviteReceived.getRequest(), mServerTransaction);
8312d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                endCallNormally();
8322d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return true;
8332d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
8342d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return false;
8352d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
8362d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
8372d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private boolean incomingCallToInCall(EventObject evt)
8382d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                throws SipException {
8392d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            // expect ACK, CANCEL request
8402d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            if (isRequestEvent(Request.ACK, evt)) {
8412d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                establishCall();
8422d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return true;
8432d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            } else if (isRequestEvent(Request.CANCEL, evt)) {
8442d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                // http://tools.ietf.org/html/rfc3261#section-9.2
8452d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                // Final response has been sent; do nothing here.
8462d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return true;
8472d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
8482d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return false;
8492d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
8502d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
8512d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private boolean outgoingCall(EventObject evt) throws SipException {
8522d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            if (expectResponse(Request.INVITE, evt)) {
8532d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                ResponseEvent event = (ResponseEvent) evt;
8542d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                Response response = event.getResponse();
8552d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
8562d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                int statusCode = response.getStatusCode();
8572d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                switch (statusCode) {
8582d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                case Response.RINGING:
8592d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    if (mState == SipSessionState.OUTGOING_CALL) {
8602d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        mState = SipSessionState.OUTGOING_CALL_RING_BACK;
8612d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        mProxy.onRingingBack(this);
8622d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    }
8632d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    return true;
8642d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                case Response.OK:
8652d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    mSipHelper.sendInviteAck(event, mDialog);
86695b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh                    mPeerSessionDescription = extractContent(response);
8672d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    establishCall();
8682d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    return true;
8692d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                case Response.PROXY_AUTHENTICATION_REQUIRED:
870903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                    if (handleAuthentication(event)) {
871903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                        addSipSession(this);
872903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                    } else {
873903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                        endCallOnError(SipErrorCode.INVALID_CREDENTIALS,
874903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                                "incorrect username or password");
875903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                    }
8762d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    return true;
8772d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                case Response.REQUEST_PENDING:
8782d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    // TODO:
8792d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    // rfc3261#section-14.1; re-schedule invite
8802d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    return true;
8812d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                default:
8822d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    if (statusCode >= 400) {
8832d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        // error: an ack is sent automatically by the stack
884903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                        onError(response);
8852d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        return true;
8862d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    } else if (statusCode >= 300) {
8872d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        // TODO: handle 3xx (redirect)
8882d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    } else {
8892d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        return true;
8902d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    }
8912d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                }
8922d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return false;
8932d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            } else if (END_CALL == evt) {
8942d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                // RFC says that UA should not send out cancel when no
8952d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                // response comes back yet. We are cheating for not checking
8962d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                // response.
8972d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mSipHelper.sendCancel(mClientTransaction);
8982d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mState = SipSessionState.OUTGOING_CALL_CANCELING;
8992d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return true;
9002d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
9012d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return false;
9022d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
9032d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
9042d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private boolean outgoingCallToReady(EventObject evt)
9052d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                throws SipException {
9062d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            if (evt instanceof ResponseEvent) {
9072d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                ResponseEvent event = (ResponseEvent) evt;
9082d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                Response response = event.getResponse();
9092d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                int statusCode = response.getStatusCode();
9102d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                if (expectResponse(Request.CANCEL, evt)) {
9112d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    if (statusCode == Response.OK) {
9122d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        // do nothing; wait for REQUEST_TERMINATED
9132d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        return true;
9142d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    }
9152d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                } else if (expectResponse(Request.INVITE, evt)) {
9162d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    if (statusCode == Response.OK) {
9172d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        outgoingCall(evt); // abort Cancel
9182d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        return true;
9192d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    }
9202d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                } else {
9212d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    return false;
9222d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                }
9232d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
9242d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                if (statusCode >= 400) {
9253d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                    onError(response);
9262d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    return true;
9272d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                }
9282d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            } else if (evt instanceof TransactionTerminatedEvent) {
9292d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                // rfc3261#section-14.1:
9302d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                // if re-invite gets timed out, terminate the dialog; but
9312d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                // re-invite is not reliable, just let it go and pretend
9322d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                // nothing happened.
9332d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                onError(new SipException("timed out"));
9342d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
9352d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return false;
9362d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
9372d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
9382d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private boolean inCall(EventObject evt) throws SipException {
9392d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            // expect END_CALL cmd, BYE request, hold call (MakeCallCommand)
9402d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            // OK retransmission is handled in SipStack
9412d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            if (END_CALL == evt) {
9422d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                // rfc3261#section-15.1.1
9432d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mSipHelper.sendBye(mDialog);
9442d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                endCallNormally();
9452d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return true;
9462d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            } else if (isRequestEvent(Request.INVITE, evt)) {
9472d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                // got Re-INVITE
9482d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                RequestEvent event = mInviteReceived = (RequestEvent) evt;
9492d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mState = SipSessionState.INCOMING_CALL;
95095b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh                mPeerSessionDescription = extractContent(event.getRequest());
9512d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mServerTransaction = null;
9522d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mProxy.onRinging(this, mPeerProfile, mPeerSessionDescription);
9532d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return true;
9542d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            } else if (isRequestEvent(Request.BYE, evt)) {
9552d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mSipHelper.sendResponse((RequestEvent) evt, Response.OK);
9562d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                endCallNormally();
9572d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return true;
9582d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            } else if (evt instanceof MakeCallCommand) {
9592d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                // to change call
9602d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mClientTransaction = mSipHelper.sendReinvite(mDialog,
9612d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        ((MakeCallCommand) evt).getSessionDescription());
9622d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mState = SipSessionState.OUTGOING_CALL;
9632d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return true;
9642d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
9652d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return false;
9662d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
9672d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
968903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan        private String createErrorMessage(Response response) {
969903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan            return String.format(SERVER_ERROR_PREFIX + "%s (%d)",
970903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                    response.getReasonPhrase(), response.getStatusCode());
971903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan        }
972903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan
9732d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private void establishCall() {
9742d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            mState = SipSessionState.IN_CALL;
9752d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            mInCall = true;
9762d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            mProxy.onCallEstablished(this, mPeerSessionDescription);
9772d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
9782d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
9792d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private void fallbackToPreviousInCall(Throwable exception) {
980903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan            exception = getRootCause(exception);
9813d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan            fallbackToPreviousInCall(getErrorCode(exception),
982903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                    exception.toString());
9832d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
9842d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
9853d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan        private void fallbackToPreviousInCall(SipErrorCode errorCode,
9863d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                String message) {
9873d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan            mState = SipSessionState.IN_CALL;
9883d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan            mProxy.onCallChangeFailed(this, errorCode.toString(), message);
9893d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan        }
9903d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan
9912d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private void endCallNormally() {
9922d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            reset();
9932d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            mProxy.onCallEnded(this);
9942d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
9952d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
996903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan        private void endCallOnError(SipErrorCode errorCode, String message) {
9972d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            reset();
998903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan            mProxy.onError(this, errorCode.toString(), message);
999903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan        }
1000903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan
1001903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan        private void endCallOnBusy() {
1002903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan            reset();
1003903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan            mProxy.onCallBusy(this);
10042d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
10052d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
10063d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan        private void onError(SipErrorCode errorCode, String message) {
10073d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan            switch (mState) {
10083d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                case REGISTERING:
10093d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                case DEREGISTERING:
10103d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                    onRegistrationFailed(errorCode, message);
10113d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                    break;
10123d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                default:
1013d231aa880ab006d51ffe03454c1fc082f1c97bb8Hung-ying Tyan                    if ((errorCode != SipErrorCode.DATA_CONNECTION_LOST)
1014d231aa880ab006d51ffe03454c1fc082f1c97bb8Hung-ying Tyan                            && mInCall) {
10153d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                        fallbackToPreviousInCall(errorCode, message);
10163d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                    } else {
10173d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                        endCallOnError(errorCode, message);
10183d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                    }
10192d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
10202d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
10212d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
10223d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan
10233d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan        private void onError(Throwable exception) {
10243d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan            exception = getRootCause(exception);
10253d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan            onError(getErrorCode(exception), exception.toString());
10263d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan        }
10273d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan
1028903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan        private void onError(Response response) {
10293d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan            int statusCode = response.getStatusCode();
10303d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan            if (!mInCall && ((statusCode == Response.TEMPORARILY_UNAVAILABLE)
10313d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                    || (statusCode == Response.BUSY_HERE))) {
10323d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                endCallOnBusy();
1033903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan            } else {
10343d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                onError(getErrorCode(statusCode), createErrorMessage(response));
1035903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan            }
1036903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan        }
1037903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan
1038903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan        private SipErrorCode getErrorCode(int responseStatusCode) {
1039903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan            switch (responseStatusCode) {
1040903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                case Response.NOT_FOUND:
1041903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                case Response.ADDRESS_INCOMPLETE:
1042903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                    return SipErrorCode.INVALID_REMOTE_URI;
1043903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                case Response.REQUEST_TIMEOUT:
1044903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                    return SipErrorCode.TIME_OUT;
1045903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                default:
1046903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                    if (responseStatusCode < 500) {
1047903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                        return SipErrorCode.CLIENT_ERROR;
1048903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                    } else {
1049903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                        return SipErrorCode.SERVER_ERROR;
1050903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                    }
1051903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan            }
1052903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan        }
1053903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan
1054903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan        private Throwable getRootCause(Throwable exception) {
1055903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan            Throwable cause = exception.getCause();
1056903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan            while (cause != null) {
1057903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                exception = cause;
1058903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                cause = exception.getCause();
1059903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan            }
1060903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan            return exception;
1061903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan        }
1062903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan
1063903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan        private SipErrorCode getErrorCode(Throwable exception) {
1064903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan            String message = exception.getMessage();
1065903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan            if (exception instanceof UnknownHostException) {
1066903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                return SipErrorCode.INVALID_REMOTE_URI;
1067903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan            } else if (exception instanceof IOException) {
1068903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                return SipErrorCode.SOCKET_ERROR;
1069903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan            } else if (message.startsWith(SERVER_ERROR_PREFIX)) {
1070903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                return SipErrorCode.SERVER_ERROR;
1071903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan            } else {
1072903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                return SipErrorCode.CLIENT_ERROR;
1073903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan            }
1074903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan        }
1075903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan
10762d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private void onRegistrationDone(int duration) {
10773d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan            reset();
10782d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            mProxy.onRegistrationDone(this, duration);
10792d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
10802d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
1081903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan        private void onRegistrationFailed(SipErrorCode errorCode,
1082903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                String message) {
10833d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan            reset();
1084903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan            mProxy.onRegistrationFailed(this, errorCode.toString(), message);
1085903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan        }
1086903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan
10872d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private void onRegistrationFailed(Throwable exception) {
10883d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan            reset();
1089903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan            exception = getRootCause(exception);
1090903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan            onRegistrationFailed(getErrorCode(exception),
1091903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                    exception.toString());
10922d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
10933d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan
10943d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan        private void onRegistrationFailed(Response response) {
10953d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan            reset();
10963d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan            int statusCode = response.getStatusCode();
10973d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan            onRegistrationFailed(getErrorCode(statusCode),
10983d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                    createErrorMessage(response));
10993d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan        }
11002d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
11012d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
11022d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    /**
11032d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang     * @return true if the event is a request event matching the specified
11042d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang     *      method; false otherwise
11052d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang     */
11062d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private static boolean isRequestEvent(String method, EventObject event) {
11072d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        try {
11082d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            if (event instanceof RequestEvent) {
11092d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                RequestEvent requestEvent = (RequestEvent) event;
11102d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return method.equals(requestEvent.getRequest().getMethod());
11112d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
11122d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        } catch (Throwable e) {
11132d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
11142d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        return false;
11152d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
11162d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
11172d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private static String getCseqMethod(Message message) {
11182d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        return ((CSeqHeader) message.getHeader(CSeqHeader.NAME)).getMethod();
11192d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
11202d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
11212d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    /**
11222d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang     * @return true if the event is a response event and the CSeqHeader method
11232d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang     * match the given arguments; false otherwise
11242d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang     */
11252d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private static boolean expectResponse(
11262d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            String expectedMethod, EventObject evt) {
11272d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        if (evt instanceof ResponseEvent) {
11282d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            ResponseEvent event = (ResponseEvent) evt;
11292d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            Response response = event.getResponse();
11302d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return expectedMethod.equalsIgnoreCase(getCseqMethod(response));
11312d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
11322d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        return false;
11332d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
11342d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
11352d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    /**
11362d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang     * @return true if the event is a response event and the response code and
11372d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang     *      CSeqHeader method match the given arguments; false otherwise
11382d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang     */
11392d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private static boolean expectResponse(
11402d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            int responseCode, String expectedMethod, EventObject evt) {
11412d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        if (evt instanceof ResponseEvent) {
11422d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            ResponseEvent event = (ResponseEvent) evt;
11432d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            Response response = event.getResponse();
11442d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            if (response.getStatusCode() == responseCode) {
11452d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return expectedMethod.equalsIgnoreCase(getCseqMethod(response));
11462d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
11472d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
11482d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        return false;
11492d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
11502d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
11512d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private static SipProfile createPeerProfile(Request request)
11522d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            throws SipException {
11532d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        try {
11542d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            FromHeader fromHeader =
11552d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    (FromHeader) request.getHeader(FromHeader.NAME);
11562d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            Address address = fromHeader.getAddress();
11572d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            SipURI uri = (SipURI) address.getURI();
11582d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            String username = uri.getUser();
11592d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            if (username == null) username = ANONYMOUS;
11602d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return new SipProfile.Builder(username, uri.getHost())
11612d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    .setPort(uri.getPort())
11622d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    .setDisplayName(address.getDisplayName())
11632d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    .build();
11642d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        } catch (InvalidArgumentException e) {
11652d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            throw new SipException("createPeerProfile()", e);
11662d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        } catch (ParseException e) {
11672d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            throw new SipException("createPeerProfile()", e);
11682d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
11692d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
11702d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
11712d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private static String log(EventObject evt) {
11722d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        if (evt instanceof RequestEvent) {
11732d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return ((RequestEvent) evt).getRequest().toString();
11742d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        } else if (evt instanceof ResponseEvent) {
11752d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return ((ResponseEvent) evt).getResponse().toString();
11762d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        } else {
11772d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return evt.toString();
11782d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
11792d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
11802d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
11812d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private class OptionsCommand extends EventObject {
11822d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public OptionsCommand() {
11832d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            super(SipSessionGroup.this);
11842d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
11852d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
11862d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
11872d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private class RegisterCommand extends EventObject {
11882d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private int mDuration;
11892d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
11902d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public RegisterCommand(int duration) {
11912d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            super(SipSessionGroup.this);
11922d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            mDuration = duration;
11932d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
11942d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
11952d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public int getDuration() {
11962d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return mDuration;
11972d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
11982d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
11992d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
12002d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private class MakeCallCommand extends EventObject {
120195b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh        private String mSessionDescription;
12022d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
12032d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public MakeCallCommand(SipProfile peerProfile,
120495b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh                String sessionDescription) {
12052d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            super(peerProfile);
12062d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            mSessionDescription = sessionDescription;
12072d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
12082d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
12092d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public SipProfile getPeerProfile() {
12102d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return (SipProfile) getSource();
12112d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
12122d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
121395b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh        public String getSessionDescription() {
12142d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return mSessionDescription;
12152d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
12162d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
12172d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
12182d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang}
1219