SipSessionGroup.java revision a936b256eb1611b5d8b88d0cd61f21225152cc82
12d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang/*
22d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang * Copyright (C) 2010 The Android Open Source Project
32d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang *
42d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang * Licensed under the Apache License, Version 2.0 (the "License");
52d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang * you may not use this file except in compliance with the License.
62d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang * You may obtain a copy of the License at
72d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang *
82d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang *      http://www.apache.org/licenses/LICENSE-2.0
92d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang *
102d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang * Unless required by applicable law or agreed to in writing, software
112d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang * distributed under the License is distributed on an "AS IS" BASIS,
122d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
132d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang * See the License for the specific language governing permissions and
142d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang * limitations under the License.
152d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang */
162d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
172d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangpackage com.android.server.sip;
182d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
192d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport gov.nist.javax.sip.clientauthutils.AccountManager;
202d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport gov.nist.javax.sip.clientauthutils.UserCredentials;
212d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport gov.nist.javax.sip.header.SIPHeaderNames;
227d137e40cd36290c6bfb5beaf66f4018ae92c97fChung-yih Wangimport gov.nist.javax.sip.header.ProxyAuthenticate;
232d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport gov.nist.javax.sip.header.WWWAuthenticate;
2495b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yehimport gov.nist.javax.sip.message.SIPMessage;
252d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
262d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport android.net.sip.ISipSession;
272d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport android.net.sip.ISipSessionListener;
28903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyanimport android.net.sip.SipErrorCode;
292d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport android.net.sip.SipProfile;
3084a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyanimport android.net.sip.SipSession;
312d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport android.text.TextUtils;
322d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport android.util.Log;
332d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
342d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport java.io.IOException;
3595b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yehimport java.io.UnsupportedEncodingException;
362d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport java.net.DatagramSocket;
37903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyanimport java.net.UnknownHostException;
382d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport java.text.ParseException;
392d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport java.util.Collection;
402d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport java.util.EventObject;
412d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport java.util.HashMap;
422d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport java.util.Map;
432d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport java.util.Properties;
442d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport java.util.TooManyListenersException;
452d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
462d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.ClientTransaction;
472d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.Dialog;
482d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.DialogTerminatedEvent;
492d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.IOExceptionEvent;
502d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.InvalidArgumentException;
512d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.ListeningPoint;
529ea96c6cade1f25d4d77dcbd24854df431548b36Hung-ying Tyanimport javax.sip.ObjectInUseException;
532d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.RequestEvent;
542d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.ResponseEvent;
552d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.ServerTransaction;
562d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.SipException;
572d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.SipFactory;
582d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.SipListener;
592d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.SipProvider;
602d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.SipStack;
612d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.TimeoutEvent;
622d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.Transaction;
632d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.TransactionState;
642d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.TransactionTerminatedEvent;
65903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyanimport javax.sip.TransactionUnavailableException;
662d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.address.Address;
672d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.address.SipURI;
682d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.header.CSeqHeader;
692d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.header.ExpiresHeader;
702d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.header.FromHeader;
712d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.header.MinExpiresHeader;
722d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.header.ViaHeader;
732d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.message.Message;
742d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.message.Request;
752d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.message.Response;
762d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
772d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang/**
782d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang * Manages {@link ISipSession}'s for a SIP account.
792d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang */
802d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangclass SipSessionGroup implements SipListener {
812d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private static final String TAG = "SipSession";
82c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan    private static final boolean DEBUG = true;
83c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan    private static final boolean DEBUG_PING = DEBUG && false;
842d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private static final String ANONYMOUS = "anonymous";
8566cc5355a137e291cc1e3c5d871e1d9cd35ee0abChung-yih Wang    // Limit the size of thread pool to 1 for the order issue when the phone is
8666cc5355a137e291cc1e3c5d871e1d9cd35ee0abChung-yih Wang    // waken up from sleep and there are many packets to be processed in the SIP
8766cc5355a137e291cc1e3c5d871e1d9cd35ee0abChung-yih Wang    // stack. Note: The default thread pool size in NIST SIP stack is -1 which is
8866cc5355a137e291cc1e3c5d871e1d9cd35ee0abChung-yih Wang    // unlimited.
8966cc5355a137e291cc1e3c5d871e1d9cd35ee0abChung-yih Wang    private static final String THREAD_POOL_SIZE = "1";
909352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan    private static final int EXPIRY_TIME = 3600; // in seconds
91194bbcce9ba15634500f542b9ea017b2cf154b45Hung-ying Tyan    private static final int CANCEL_CALL_TIMER = 3; // in seconds
9228f63c06894b9ca9252f43bc54a098c0a785d4b4Hung-ying Tyan    private static final long WAKE_LOCK_HOLDING_TIME = 500; // in milliseconds
932d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
942d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private static final EventObject DEREGISTER = new EventObject("Deregister");
952d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private static final EventObject END_CALL = new EventObject("End call");
962d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private static final EventObject HOLD_CALL = new EventObject("Hold call");
972d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private static final EventObject CONTINUE_CALL
982d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            = new EventObject("Continue call");
992d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
1002d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private final SipProfile mLocalProfile;
1012d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private final String mPassword;
1022d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
1032d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private SipStack mSipStack;
1042d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private SipHelper mSipHelper;
1052d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
1062d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    // session that processes INVITE requests
1072d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private SipSessionImpl mCallReceiverSession;
1082d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private String mLocalIp;
1092d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
11028f63c06894b9ca9252f43bc54a098c0a785d4b4Hung-ying Tyan    private SipWakeLock mWakeLock;
11128f63c06894b9ca9252f43bc54a098c0a785d4b4Hung-ying Tyan
1122d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    // call-id-to-SipSession map
1132d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private Map<String, SipSessionImpl> mSessionMap =
1142d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            new HashMap<String, SipSessionImpl>();
1152d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
1162d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    /**
1172d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang     * @param myself the local profile with password crossed out
1182d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang     * @param password the password of the profile
1192d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang     * @throws IOException if cannot assign requested address
1202d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang     */
12128f63c06894b9ca9252f43bc54a098c0a785d4b4Hung-ying Tyan    public SipSessionGroup(String localIp, SipProfile myself, String password,
12228f63c06894b9ca9252f43bc54a098c0a785d4b4Hung-ying Tyan            SipWakeLock wakeLock) throws SipException, IOException {
1232d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        mLocalProfile = myself;
1242d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        mPassword = password;
12528f63c06894b9ca9252f43bc54a098c0a785d4b4Hung-ying Tyan        mWakeLock = wakeLock;
1262d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        reset(localIp);
1272d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
1282d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
12984a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan    synchronized void reset(String localIp) throws SipException, IOException {
1302d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        mLocalIp = localIp;
1312d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        if (localIp == null) return;
1322d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
1332d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        SipProfile myself = mLocalProfile;
1342d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        SipFactory sipFactory = SipFactory.getInstance();
1352d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        Properties properties = new Properties();
1362d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        properties.setProperty("javax.sip.STACK_NAME", getStackName());
137b4116c09fb9784551911ea0a10b4dd321ff9aa7dChung-yih Wang        properties.setProperty(
138b4116c09fb9784551911ea0a10b4dd321ff9aa7dChung-yih Wang                "gov.nist.javax.sip.THREAD_POOL_SIZE", THREAD_POOL_SIZE);
1392d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        String outboundProxy = myself.getProxyAddress();
1402d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        if (!TextUtils.isEmpty(outboundProxy)) {
1415de1d36dd0415c4cf9afdf093a4915951ef6c770Chung-yih Wang            Log.v(TAG, "outboundProxy is " + outboundProxy);
1422d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            properties.setProperty("javax.sip.OUTBOUND_PROXY", outboundProxy
1432d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    + ":" + myself.getPort() + "/" + myself.getProtocol());
1442d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
1452d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        SipStack stack = mSipStack = sipFactory.createSipStack(properties);
1462d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
1472d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        try {
1482d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            SipProvider provider = stack.createSipProvider(
1492d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    stack.createListeningPoint(localIp, allocateLocalPort(),
1502d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                            myself.getProtocol()));
1512d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            provider.addSipListener(this);
1522d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            mSipHelper = new SipHelper(stack, provider);
1532d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        } catch (InvalidArgumentException e) {
1542d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            throw new IOException(e.getMessage());
1552d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        } catch (TooManyListenersException e) {
1562d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            // must never happen
1572d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            throw new SipException("SipSessionGroup constructor", e);
1582d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
1592d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        Log.d(TAG, " start stack for " + myself.getUriString());
1602d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        stack.start();
1612d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
1622d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        mCallReceiverSession = null;
1632d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        mSessionMap.clear();
1642d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
1652d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
166d231aa880ab006d51ffe03454c1fc082f1c97bb8Hung-ying Tyan    synchronized void onConnectivityChanged() {
167ebc886c857a702d788c2bdfb9e0b3d20746ad745Hung-ying Tyan        SipSessionImpl[] ss = mSessionMap.values().toArray(
168ebc886c857a702d788c2bdfb9e0b3d20746ad745Hung-ying Tyan                    new SipSessionImpl[mSessionMap.size()]);
169ebc886c857a702d788c2bdfb9e0b3d20746ad745Hung-ying Tyan        // Iterate on the copied array instead of directly on mSessionMap to
170ebc886c857a702d788c2bdfb9e0b3d20746ad745Hung-ying Tyan        // avoid ConcurrentModificationException being thrown when
171ebc886c857a702d788c2bdfb9e0b3d20746ad745Hung-ying Tyan        // SipSessionImpl removes itself from mSessionMap in onError() in the
172ebc886c857a702d788c2bdfb9e0b3d20746ad745Hung-ying Tyan        // following loop.
173ebc886c857a702d788c2bdfb9e0b3d20746ad745Hung-ying Tyan        for (SipSessionImpl s : ss) {
174d231aa880ab006d51ffe03454c1fc082f1c97bb8Hung-ying Tyan            s.onError(SipErrorCode.DATA_CONNECTION_LOST,
175d231aa880ab006d51ffe03454c1fc082f1c97bb8Hung-ying Tyan                    "data connection lost");
176d231aa880ab006d51ffe03454c1fc082f1c97bb8Hung-ying Tyan        }
177d231aa880ab006d51ffe03454c1fc082f1c97bb8Hung-ying Tyan    }
178d231aa880ab006d51ffe03454c1fc082f1c97bb8Hung-ying Tyan
1792d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    public SipProfile getLocalProfile() {
1802d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        return mLocalProfile;
1812d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
1822d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
1832d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    public String getLocalProfileUri() {
1842d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        return mLocalProfile.getUriString();
1852d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
1862d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
1872d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private String getStackName() {
1882d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        return "stack" + System.currentTimeMillis();
1892d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
1902d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
1912d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    public synchronized void close() {
1922d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        Log.d(TAG, " close stack for " + mLocalProfile.getUriString());
1935d0c5cf2d6c6e82bcdce95d72d9000a934b2f354Hung-ying Tyan        onConnectivityChanged();
1942d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        mSessionMap.clear();
1952d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        closeToNotReceiveCalls();
1962d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        if (mSipStack != null) {
1972d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            mSipStack.stop();
1982d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            mSipStack = null;
1992d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            mSipHelper = null;
2002d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
2012d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
2022d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
2032d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    public synchronized boolean isClosed() {
2042d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        return (mSipStack == null);
2052d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
2062d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
2072d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    // For internal use, require listener not to block in callbacks.
2082d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    public synchronized void openToReceiveCalls(ISipSessionListener listener) {
2092d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        if (mCallReceiverSession == null) {
2102d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            mCallReceiverSession = new SipSessionCallReceiverImpl(listener);
2112d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        } else {
2122d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            mCallReceiverSession.setListener(listener);
2132d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
2142d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
2152d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
2162d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    public synchronized void closeToNotReceiveCalls() {
2172d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        mCallReceiverSession = null;
2182d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
2192d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
2202d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    public ISipSession createSession(ISipSessionListener listener) {
2212d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        return (isClosed() ? null : new SipSessionImpl(listener));
2222d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
2232d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
2242d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private static int allocateLocalPort() throws SipException {
2252d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        try {
2262d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            DatagramSocket s = new DatagramSocket();
2272d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            int localPort = s.getLocalPort();
2282d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            s.close();
2292d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return localPort;
2302d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        } catch (IOException e) {
2312d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            throw new SipException("allocateLocalPort()", e);
2322d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
2332d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
2342d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
2350a6e717fb6846f66b8dc853e079f2166307bfc60Hung-ying Tyan    synchronized boolean containsSession(String callId) {
2360a6e717fb6846f66b8dc853e079f2166307bfc60Hung-ying Tyan        return mSessionMap.containsKey(callId);
2370a6e717fb6846f66b8dc853e079f2166307bfc60Hung-ying Tyan    }
2380a6e717fb6846f66b8dc853e079f2166307bfc60Hung-ying Tyan
2392d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private synchronized SipSessionImpl getSipSession(EventObject event) {
2402d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        String key = SipHelper.getCallId(event);
2412d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        SipSessionImpl session = mSessionMap.get(key);
242c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan        if ((session != null) && isLoggable(session)) {
243c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan            Log.d(TAG, "session key from event: " + key);
244c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan            Log.d(TAG, "active sessions:");
245c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan            for (String k : mSessionMap.keySet()) {
246c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan                Log.d(TAG, " ..." + k + ": " + mSessionMap.get(k));
247c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan            }
248c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan        }
2492d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        return ((session != null) ? session : mCallReceiverSession);
2502d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
2512d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
2522d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private synchronized void addSipSession(SipSessionImpl newSession) {
2532d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        removeSipSession(newSession);
2542d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        String key = newSession.getCallId();
2552d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        mSessionMap.put(key, newSession);
256c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan        if (isLoggable(newSession)) {
257c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan            Log.d(TAG, "+++  add a session with key:  '" + key + "'");
258c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan            for (String k : mSessionMap.keySet()) {
259c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan                Log.d(TAG, "  " + k + ": " + mSessionMap.get(k));
260c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan            }
2612d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
2622d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
2632d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
2642d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private synchronized void removeSipSession(SipSessionImpl session) {
2652d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        if (session == mCallReceiverSession) return;
2662d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        String key = session.getCallId();
2672d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        SipSessionImpl s = mSessionMap.remove(key);
2682d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        // sanity check
2692d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        if ((s != null) && (s != session)) {
2702d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            Log.w(TAG, "session " + session + " is not associated with key '"
2712d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    + key + "'");
2722d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            mSessionMap.put(key, s);
2732d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            for (Map.Entry<String, SipSessionImpl> entry
2742d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    : mSessionMap.entrySet()) {
2752d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                if (entry.getValue() == s) {
2762d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    key = entry.getKey();
2772d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    mSessionMap.remove(key);
2782d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                }
2792d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
2802d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
2812d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
282c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan        if ((s != null) && isLoggable(s)) {
283c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan            Log.d(TAG, "remove session " + session + " @key '" + key + "'");
284c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan            for (String k : mSessionMap.keySet()) {
285c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan                Log.d(TAG, "  " + k + ": " + mSessionMap.get(k));
286c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan            }
2872d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
2882d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
2892d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
29028f63c06894b9ca9252f43bc54a098c0a785d4b4Hung-ying Tyan    public void processRequest(final RequestEvent event) {
29128f63c06894b9ca9252f43bc54a098c0a785d4b4Hung-ying Tyan        if (isRequestEvent(Request.INVITE, event)) {
29228f63c06894b9ca9252f43bc54a098c0a785d4b4Hung-ying Tyan            if (DEBUG) Log.d(TAG, "<<<<< got INVITE, thread:"
29328f63c06894b9ca9252f43bc54a098c0a785d4b4Hung-ying Tyan                    + Thread.currentThread());
29428f63c06894b9ca9252f43bc54a098c0a785d4b4Hung-ying Tyan            // Acquire a wake lock and keep it for WAKE_LOCK_HOLDING_TIME;
29528f63c06894b9ca9252f43bc54a098c0a785d4b4Hung-ying Tyan            // should be large enough to bring up the app.
29628f63c06894b9ca9252f43bc54a098c0a785d4b4Hung-ying Tyan            mWakeLock.acquire(WAKE_LOCK_HOLDING_TIME);
29728f63c06894b9ca9252f43bc54a098c0a785d4b4Hung-ying Tyan        }
2982d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        process(event);
2992d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
3002d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
3012d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    public void processResponse(ResponseEvent event) {
3022d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        process(event);
3032d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
3042d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
3052d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    public void processIOException(IOExceptionEvent event) {
3062d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        process(event);
3072d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
3082d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
3092d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    public void processTimeout(TimeoutEvent event) {
3102d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        process(event);
3112d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
3122d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
3132d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    public void processTransactionTerminated(TransactionTerminatedEvent event) {
3142d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        process(event);
3152d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
3162d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
3172d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    public void processDialogTerminated(DialogTerminatedEvent event) {
3182d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        process(event);
3192d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
3202d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
3212d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private synchronized void process(EventObject event) {
3222d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        SipSessionImpl session = getSipSession(event);
3232d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        try {
324c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan            boolean isLoggable = isLoggable(session, event);
325c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan            boolean processed = (session != null) && session.process(event);
326c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan            if (isLoggable && processed) {
32797963794af1e18674dd111e3ad344d90b16c922cHung-ying Tyan                Log.d(TAG, "new state after: "
32884a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                        + SipSession.State.toString(session.mState));
3292d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
3302d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        } catch (Throwable e) {
331903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan            Log.w(TAG, "event process error: " + event, e);
3322d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            session.onError(e);
3332d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
3342d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
3352d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
33695b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh    private String extractContent(Message message) {
33795b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh        // Currently we do not support secure MIME bodies.
33895b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh        byte[] bytes = message.getRawContent();
33995b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh        if (bytes != null) {
34095b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh            try {
34195b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh                if (message instanceof SIPMessage) {
34295b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh                    return ((SIPMessage) message).getMessageContent();
34395b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh                } else {
34495b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh                    return new String(bytes, "UTF-8");
34595b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh                }
34695b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh            } catch (UnsupportedEncodingException e) {
34795b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh            }
34895b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh        }
34995b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh        return null;
35095b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh    }
35195b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh
3522d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private class SipSessionCallReceiverImpl extends SipSessionImpl {
3532d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public SipSessionCallReceiverImpl(ISipSessionListener listener) {
3542d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            super(listener);
3552d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
3562d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
3572d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public boolean process(EventObject evt) throws SipException {
358c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan            if (isLoggable(this, evt)) Log.d(TAG, " ~~~~~   " + this + ": "
35984a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                    + SipSession.State.toString(mState) + ": processing "
36097963794af1e18674dd111e3ad344d90b16c922cHung-ying Tyan                    + log(evt));
3612d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            if (isRequestEvent(Request.INVITE, evt)) {
3622d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                RequestEvent event = (RequestEvent) evt;
3632d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                SipSessionImpl newSession = new SipSessionImpl(mProxy);
364fb3a98b1d8d0ad040980d509c4c5341928b9460bHung-ying Tyan                newSession.mState = SipSession.State.INCOMING_CALL;
3652d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                newSession.mServerTransaction = mSipHelper.sendRinging(event,
3662d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        generateTag());
3672d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                newSession.mDialog = newSession.mServerTransaction.getDialog();
3682d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                newSession.mInviteReceived = event;
3692d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                newSession.mPeerProfile = createPeerProfile(event.getRequest());
3702d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                newSession.mPeerSessionDescription =
37195b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh                        extractContent(event.getRequest());
3722d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                addSipSession(newSession);
3732d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mProxy.onRinging(newSession, newSession.mPeerProfile,
3742d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        newSession.mPeerSessionDescription);
3752d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return true;
3760b4d2fb11405e2e785ec30cabe7bb311c654c0d2Chung-yih Wang            } else if (isRequestEvent(Request.OPTIONS, evt)) {
3770b4d2fb11405e2e785ec30cabe7bb311c654c0d2Chung-yih Wang                mSipHelper.sendResponse((RequestEvent) evt, Response.OK);
3780b4d2fb11405e2e785ec30cabe7bb311c654c0d2Chung-yih Wang                return true;
3792d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            } else {
3802d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return false;
3812d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
3822d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
3832d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
3842d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
3852d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    class SipSessionImpl extends ISipSession.Stub {
3862d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        SipProfile mPeerProfile;
3872d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        SipSessionListenerProxy mProxy = new SipSessionListenerProxy();
38884a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan        int mState = SipSession.State.READY_TO_CALL;
3892d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        RequestEvent mInviteReceived;
3902d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        Dialog mDialog;
3912d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        ServerTransaction mServerTransaction;
3922d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        ClientTransaction mClientTransaction;
39395b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh        String mPeerSessionDescription;
3942d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        boolean mInCall;
3959352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan        SessionTimer mTimer;
396ee8a884f3504c981be8a1d6888b4590a0a394e05Hung-ying Tyan        int mAuthenticationRetryCount;
397ee8a884f3504c981be8a1d6888b4590a0a394e05Hung-ying Tyan
398ee8a884f3504c981be8a1d6888b4590a0a394e05Hung-ying Tyan        // for registration
399ee8a884f3504c981be8a1d6888b4590a0a394e05Hung-ying Tyan        boolean mReRegisterFlag = false;
400ee8a884f3504c981be8a1d6888b4590a0a394e05Hung-ying Tyan        int mRPort;
4019352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan
4029352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan        // lightweight timer
4039352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan        class SessionTimer {
4049352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan            private boolean mRunning = true;
4059352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan
4069352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan            void start(final int timeout) {
4079352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                new Thread(new Runnable() {
4089352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                    public void run() {
4099352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                        sleep(timeout);
4109352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                        if (mRunning) timeout();
4119352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                    }
41284a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                }, "SipSessionTimerThread").start();
4139352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan            }
4149352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan
4159352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan            synchronized void cancel() {
4169352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                mRunning = false;
4179352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                this.notify();
4189352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan            }
4199352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan
4209352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan            private void timeout() {
4219352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                synchronized (SipSessionGroup.this) {
4229352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                    onError(SipErrorCode.TIME_OUT, "Session timed out!");
4239352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                }
4249352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan            }
4259352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan
4269352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan            private synchronized void sleep(int timeout) {
4279352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                try {
4289352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                    this.wait(timeout * 1000);
4299352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                } catch (InterruptedException e) {
4309352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                    Log.e(TAG, "session timer interrupted!");
4319352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                }
4329352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan            }
4339352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan        }
4342d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
4352d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public SipSessionImpl(ISipSessionListener listener) {
4362d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            setListener(listener);
4372d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
4382d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
4392d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        SipSessionImpl duplicate() {
4402d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return new SipSessionImpl(mProxy.getListener());
4412d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
4422d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
4432d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private void reset() {
4442d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            mInCall = false;
4452d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            removeSipSession(this);
4462d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            mPeerProfile = null;
44784a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan            mState = SipSession.State.READY_TO_CALL;
4482d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            mInviteReceived = null;
4499ea96c6cade1f25d4d77dcbd24854df431548b36Hung-ying Tyan            mPeerSessionDescription = null;
450ee8a884f3504c981be8a1d6888b4590a0a394e05Hung-ying Tyan            mRPort = 0;
451ee8a884f3504c981be8a1d6888b4590a0a394e05Hung-ying Tyan            mAuthenticationRetryCount = 0;
4529ea96c6cade1f25d4d77dcbd24854df431548b36Hung-ying Tyan
4539ea96c6cade1f25d4d77dcbd24854df431548b36Hung-ying Tyan            if (mDialog != null) mDialog.delete();
4542d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            mDialog = null;
4559ea96c6cade1f25d4d77dcbd24854df431548b36Hung-ying Tyan
4569ea96c6cade1f25d4d77dcbd24854df431548b36Hung-ying Tyan            try {
4579ea96c6cade1f25d4d77dcbd24854df431548b36Hung-ying Tyan                if (mServerTransaction != null) mServerTransaction.terminate();
4589ea96c6cade1f25d4d77dcbd24854df431548b36Hung-ying Tyan            } catch (ObjectInUseException e) {
4599ea96c6cade1f25d4d77dcbd24854df431548b36Hung-ying Tyan                // ignored
4609ea96c6cade1f25d4d77dcbd24854df431548b36Hung-ying Tyan            }
4612d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            mServerTransaction = null;
4629ea96c6cade1f25d4d77dcbd24854df431548b36Hung-ying Tyan
4639ea96c6cade1f25d4d77dcbd24854df431548b36Hung-ying Tyan            try {
4649ea96c6cade1f25d4d77dcbd24854df431548b36Hung-ying Tyan                if (mClientTransaction != null) mClientTransaction.terminate();
4659ea96c6cade1f25d4d77dcbd24854df431548b36Hung-ying Tyan            } catch (ObjectInUseException e) {
4669ea96c6cade1f25d4d77dcbd24854df431548b36Hung-ying Tyan                // ignored
4679ea96c6cade1f25d4d77dcbd24854df431548b36Hung-ying Tyan            }
4682d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            mClientTransaction = null;
4699352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan
4709352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan            cancelSessionTimer();
4712d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
4722d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
4732d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public boolean isInCall() {
4742d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return mInCall;
4752d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
4762d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
4772d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public String getLocalIp() {
4782d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return mLocalIp;
4792d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
4802d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
4812d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public SipProfile getLocalProfile() {
4822d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return mLocalProfile;
4832d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
4842d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
4852d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public SipProfile getPeerProfile() {
4862d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return mPeerProfile;
4872d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
4882d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
4892d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public String getCallId() {
4902d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return SipHelper.getCallId(getTransaction());
4912d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
4922d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
4932d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private Transaction getTransaction() {
4942d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            if (mClientTransaction != null) return mClientTransaction;
4952d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            if (mServerTransaction != null) return mServerTransaction;
4962d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return null;
4972d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
4982d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
49997963794af1e18674dd111e3ad344d90b16c922cHung-ying Tyan        public int getState() {
50097963794af1e18674dd111e3ad344d90b16c922cHung-ying Tyan            return mState;
5012d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
5022d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
5032d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public void setListener(ISipSessionListener listener) {
5042d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            mProxy.setListener((listener instanceof SipSessionListenerProxy)
5052d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    ? ((SipSessionListenerProxy) listener).getListener()
5062d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    : listener);
5072d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
5082d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
509dba514c6d8e8263d4b8f31cb2fdebfc1d4f84c35Hung-ying Tyan        // process the command in a new thread
510dba514c6d8e8263d4b8f31cb2fdebfc1d4f84c35Hung-ying Tyan        private void doCommandAsync(final EventObject command) {
511dba514c6d8e8263d4b8f31cb2fdebfc1d4f84c35Hung-ying Tyan            new Thread(new Runnable() {
512dba514c6d8e8263d4b8f31cb2fdebfc1d4f84c35Hung-ying Tyan                    public void run() {
513dba514c6d8e8263d4b8f31cb2fdebfc1d4f84c35Hung-ying Tyan                        try {
514dba514c6d8e8263d4b8f31cb2fdebfc1d4f84c35Hung-ying Tyan                            processCommand(command);
515c6548fd9eda7b58f5a2e2a9c01e3c7cafd42fafbHung-ying Tyan                        } catch (Throwable e) {
516903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                            Log.w(TAG, "command error: " + command, e);
5173d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                            onError(e);
518dba514c6d8e8263d4b8f31cb2fdebfc1d4f84c35Hung-ying Tyan                        }
519dba514c6d8e8263d4b8f31cb2fdebfc1d4f84c35Hung-ying Tyan                    }
52084a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan            }, "SipSessionAsyncCmdThread").start();
521dba514c6d8e8263d4b8f31cb2fdebfc1d4f84c35Hung-ying Tyan        }
522dba514c6d8e8263d4b8f31cb2fdebfc1d4f84c35Hung-ying Tyan
5239352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan        public void makeCall(SipProfile peerProfile, String sessionDescription,
5249352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                int timeout) {
5259352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan            doCommandAsync(new MakeCallCommand(peerProfile, sessionDescription,
5269352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                    timeout));
5272d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
5282d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
5299352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan        public void answerCall(String sessionDescription, int timeout) {
53006e8cdc0f81ead604d5adf9d7b3f982e10226fd2Hung-ying Tyan            synchronized (SipSessionGroup.this) {
53106e8cdc0f81ead604d5adf9d7b3f982e10226fd2Hung-ying Tyan                if (mPeerProfile == null) return;
53206e8cdc0f81ead604d5adf9d7b3f982e10226fd2Hung-ying Tyan                try {
53306e8cdc0f81ead604d5adf9d7b3f982e10226fd2Hung-ying Tyan                    processCommand(new MakeCallCommand(mPeerProfile,
53406e8cdc0f81ead604d5adf9d7b3f982e10226fd2Hung-ying Tyan                            sessionDescription, timeout));
53506e8cdc0f81ead604d5adf9d7b3f982e10226fd2Hung-ying Tyan                } catch (SipException e) {
53606e8cdc0f81ead604d5adf9d7b3f982e10226fd2Hung-ying Tyan                    onError(e);
53706e8cdc0f81ead604d5adf9d7b3f982e10226fd2Hung-ying Tyan                }
5382d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
5392d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
5402d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
5412d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public void endCall() {
542dba514c6d8e8263d4b8f31cb2fdebfc1d4f84c35Hung-ying Tyan            doCommandAsync(END_CALL);
5432d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
5442d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
5459352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan        public void changeCall(String sessionDescription, int timeout) {
54606e8cdc0f81ead604d5adf9d7b3f982e10226fd2Hung-ying Tyan            synchronized (SipSessionGroup.this) {
54706e8cdc0f81ead604d5adf9d7b3f982e10226fd2Hung-ying Tyan                if (mPeerProfile == null) return;
54806e8cdc0f81ead604d5adf9d7b3f982e10226fd2Hung-ying Tyan                doCommandAsync(new MakeCallCommand(mPeerProfile,
54906e8cdc0f81ead604d5adf9d7b3f982e10226fd2Hung-ying Tyan                        sessionDescription, timeout));
55006e8cdc0f81ead604d5adf9d7b3f982e10226fd2Hung-ying Tyan            }
5512d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
5522d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
5532d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public void register(int duration) {
554dba514c6d8e8263d4b8f31cb2fdebfc1d4f84c35Hung-ying Tyan            doCommandAsync(new RegisterCommand(duration));
5552d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
5562d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
5572d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public void unregister() {
558dba514c6d8e8263d4b8f31cb2fdebfc1d4f84c35Hung-ying Tyan            doCommandAsync(DEREGISTER);
5592d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
5602d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
5612d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public boolean isReRegisterRequired() {
5622d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return mReRegisterFlag;
5632d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
5642d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
5652d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public void clearReRegisterRequired() {
5662d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            mReRegisterFlag = false;
5672d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
5682d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
5692d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public void sendKeepAlive() {
57084a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan            mState = SipSession.State.PINGING;
5712d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            try {
5722d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                processCommand(new OptionsCommand());
573bd57eeafe034cf850225db403700b5dc5db5ebccHung-ying Tyan                for (int i = 0; i < 15; i++) {
574bd57eeafe034cf850225db403700b5dc5db5ebccHung-ying Tyan                    if (SipSession.State.PINGING != mState) break;
575bd57eeafe034cf850225db403700b5dc5db5ebccHung-ying Tyan                    Thread.sleep(200);
576bd57eeafe034cf850225db403700b5dc5db5ebccHung-ying Tyan                }
577bd57eeafe034cf850225db403700b5dc5db5ebccHung-ying Tyan                if (SipSession.State.PINGING == mState) {
578bd57eeafe034cf850225db403700b5dc5db5ebccHung-ying Tyan                    // FIXME: what to do if server doesn't respond
579bd57eeafe034cf850225db403700b5dc5db5ebccHung-ying Tyan                    reset();
580bd57eeafe034cf850225db403700b5dc5db5ebccHung-ying Tyan                    if (DEBUG) Log.w(TAG, "no response from ping");
5812d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                }
5822d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            } catch (SipException e) {
5832d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                Log.e(TAG, "sendKeepAlive failed", e);
5842d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            } catch (InterruptedException e) {
5852d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                Log.e(TAG, "sendKeepAlive interrupted", e);
5862d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
5872d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
5882d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
5892d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private void processCommand(EventObject command) throws SipException {
5900a6e717fb6846f66b8dc853e079f2166307bfc60Hung-ying Tyan            if (isLoggable(command)) Log.d(TAG, "process cmd: " + command);
5912d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            if (!process(command)) {
5923d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                onError(SipErrorCode.IN_PROGRESS,
5933d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                        "cannot initiate a new transaction to execute: "
5943d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                        + command);
5952d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
5962d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
5972d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
5982d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        protected String generateTag() {
5992d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            // 32-bit randomness
6002d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return String.valueOf((long) (Math.random() * 0x100000000L));
6012d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
6022d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
6032d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public String toString() {
6042d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            try {
6052d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                String s = super.toString();
60697963794af1e18674dd111e3ad344d90b16c922cHung-ying Tyan                return s.substring(s.indexOf("@")) + ":"
60784a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                        + SipSession.State.toString(mState);
6082d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            } catch (Throwable e) {
6092d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return super.toString();
6102d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
6112d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
6122d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
6132d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public boolean process(EventObject evt) throws SipException {
614c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan            if (isLoggable(this, evt)) Log.d(TAG, " ~~~~~   " + this + ": "
61584a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                    + SipSession.State.toString(mState) + ": processing "
61697963794af1e18674dd111e3ad344d90b16c922cHung-ying Tyan                    + log(evt));
6172d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            synchronized (SipSessionGroup.this) {
6182d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                if (isClosed()) return false;
6192d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
6202d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                Dialog dialog = null;
6212d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                if (evt instanceof RequestEvent) {
6222d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    dialog = ((RequestEvent) evt).getDialog();
6232d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                } else if (evt instanceof ResponseEvent) {
6242d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    dialog = ((ResponseEvent) evt).getDialog();
6252d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                }
6262d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                if (dialog != null) mDialog = dialog;
6272d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
6282d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                boolean processed;
6292d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
6302d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                switch (mState) {
63184a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                case SipSession.State.REGISTERING:
63284a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                case SipSession.State.DEREGISTERING:
6332d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    processed = registeringToReady(evt);
6342d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    break;
63584a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                case SipSession.State.PINGING:
6362d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    processed = keepAliveProcess(evt);
6372d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    break;
63884a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                case SipSession.State.READY_TO_CALL:
6392d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    processed = readyForCall(evt);
6402d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    break;
64184a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                case SipSession.State.INCOMING_CALL:
6422d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    processed = incomingCall(evt);
6432d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    break;
64484a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                case SipSession.State.INCOMING_CALL_ANSWERING:
6452d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    processed = incomingCallToInCall(evt);
6462d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    break;
64784a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                case SipSession.State.OUTGOING_CALL:
64884a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                case SipSession.State.OUTGOING_CALL_RING_BACK:
6492d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    processed = outgoingCall(evt);
6502d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    break;
65184a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                case SipSession.State.OUTGOING_CALL_CANCELING:
6522d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    processed = outgoingCallToReady(evt);
6532d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    break;
65484a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                case SipSession.State.IN_CALL:
6552d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    processed = inCall(evt);
6562d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    break;
6572d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                default:
6582d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    processed = false;
6592d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                }
6602d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return (processed || processExceptions(evt));
6612d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
6622d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
6632d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
6642d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private boolean processExceptions(EventObject evt) throws SipException {
6652d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            if (isRequestEvent(Request.BYE, evt)) {
6662d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                // terminate the call whenever a BYE is received
6672d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mSipHelper.sendResponse((RequestEvent) evt, Response.OK);
6682d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                endCallNormally();
6692d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return true;
6702d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            } else if (isRequestEvent(Request.CANCEL, evt)) {
6712d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mSipHelper.sendResponse((RequestEvent) evt,
6722d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        Response.CALL_OR_TRANSACTION_DOES_NOT_EXIST);
6732d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return true;
6742d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            } else if (evt instanceof TransactionTerminatedEvent) {
675025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan                if (isCurrentTransaction((TransactionTerminatedEvent) evt)) {
676025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan                    if (evt instanceof TimeoutEvent) {
677025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan                        processTimeout((TimeoutEvent) evt);
678025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan                    } else {
679025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan                        processTransactionTerminated(
680025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan                                (TransactionTerminatedEvent) evt);
681025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan                    }
682025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan                    return true;
6832d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                }
6840b4d2fb11405e2e785ec30cabe7bb311c654c0d2Chung-yih Wang            } else if (isRequestEvent(Request.OPTIONS, evt)) {
6850b4d2fb11405e2e785ec30cabe7bb311c654c0d2Chung-yih Wang                mSipHelper.sendResponse((RequestEvent) evt, Response.OK);
6860b4d2fb11405e2e785ec30cabe7bb311c654c0d2Chung-yih Wang                return true;
6872d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            } else if (evt instanceof DialogTerminatedEvent) {
6882d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                processDialogTerminated((DialogTerminatedEvent) evt);
6892d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return true;
6902d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
6912d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return false;
6922d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
6932d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
6942d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private void processDialogTerminated(DialogTerminatedEvent event) {
6952d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            if (mDialog == event.getDialog()) {
6962d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                onError(new SipException("dialog terminated"));
6972d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            } else {
6982d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                Log.d(TAG, "not the current dialog; current=" + mDialog
6992d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        + ", terminated=" + event.getDialog());
7002d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
7012d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
7022d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
703025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan        private boolean isCurrentTransaction(TransactionTerminatedEvent event) {
704025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan            Transaction current = event.isServerTransaction()
705025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan                    ? mServerTransaction
706025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan                    : mClientTransaction;
707025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan            Transaction target = event.isServerTransaction()
708025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan                    ? event.getServerTransaction()
709025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan                    : event.getClientTransaction();
710025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan
711025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan            if ((current != target) && (mState != SipSession.State.PINGING)) {
712025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan                Log.d(TAG, "not the current transaction; current="
713025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan                        + toString(current) + ", target=" + toString(target));
714025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan                return false;
715025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan            } else if (current != null) {
716025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan                Log.d(TAG, "transaction terminated: " + toString(current));
717025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan                return true;
718fccd5bc78f94b7dcfbcf78ddca83719c9cd1a74fHung-ying Tyan            } else {
719fccd5bc78f94b7dcfbcf78ddca83719c9cd1a74fHung-ying Tyan                // no transaction; shouldn't be here; ignored
720fccd5bc78f94b7dcfbcf78ddca83719c9cd1a74fHung-ying Tyan                return true;
721025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan            }
722025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan        }
723025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan
724025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan        private String toString(Transaction transaction) {
725025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan            if (transaction == null) return "null";
726025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan            Request request = transaction.getRequest();
727025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan            Dialog dialog = transaction.getDialog();
728025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan            CSeqHeader cseq = (CSeqHeader) request.getHeader(CSeqHeader.NAME);
729025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan            return String.format("req=%s,%s,s=%s,ds=%s,", request.getMethod(),
730025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan                    cseq.getSeqNumber(), transaction.getState(),
731025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan                    ((dialog == null) ? "-" : dialog.getState()));
732025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan        }
733025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan
7343d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan        private void processTransactionTerminated(
7353d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                TransactionTerminatedEvent event) {
7363d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan            switch (mState) {
73784a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                case SipSession.State.IN_CALL:
73884a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                case SipSession.State.READY_TO_CALL:
7393d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                    Log.d(TAG, "Transaction terminated; do nothing");
7403d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                    break;
7413d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                default:
7423d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                    Log.d(TAG, "Transaction terminated early: " + this);
7433d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                    onError(SipErrorCode.TRANSACTION_TERMINTED,
7443d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                            "transaction terminated");
7453d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan            }
7463d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan        }
7473d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan
7482d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private void processTimeout(TimeoutEvent event) {
749025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan            Log.d(TAG, "processing Timeout...");
7502d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            switch (mState) {
75184a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                case SipSession.State.REGISTERING:
75284a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                case SipSession.State.DEREGISTERING:
7533d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                    reset();
7543d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                    mProxy.onRegistrationTimeout(this);
7553d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                    break;
75684a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                case SipSession.State.INCOMING_CALL:
75784a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                case SipSession.State.INCOMING_CALL_ANSWERING:
75884a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                case SipSession.State.OUTGOING_CALL:
75984a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                case SipSession.State.OUTGOING_CALL_CANCELING:
7603d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                    onError(SipErrorCode.TIME_OUT, event.toString());
7613d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                    break;
76284a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                case SipSession.State.PINGING:
7633d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                    reset();
7643d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                    mReRegisterFlag = true;
7653d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                    break;
7662d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
7673d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                default:
7683d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                    Log.d(TAG, "   do nothing");
7693d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                    break;
7702d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
7712d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
7722d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
7732d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private int getExpiryTime(Response response) {
7742d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            int expires = EXPIRY_TIME;
7752d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            ExpiresHeader expiresHeader = (ExpiresHeader)
7762d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    response.getHeader(ExpiresHeader.NAME);
7772d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            if (expiresHeader != null) expires = expiresHeader.getExpires();
7782d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            expiresHeader = (ExpiresHeader)
7792d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    response.getHeader(MinExpiresHeader.NAME);
7802d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            if (expiresHeader != null) {
7812d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                expires = Math.max(expires, expiresHeader.getExpires());
7822d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
7832d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return expires;
7842d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
7852d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
7862d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private boolean keepAliveProcess(EventObject evt) throws SipException {
7872d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            if (evt instanceof OptionsCommand) {
7882d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mClientTransaction = mSipHelper.sendKeepAlive(mLocalProfile,
7892d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        generateTag());
7902d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mDialog = mClientTransaction.getDialog();
7912d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                addSipSession(this);
7922d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return true;
7932d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            } else if (evt instanceof ResponseEvent) {
7942d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return parseOptionsResult(evt);
7952d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
7962d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return false;
7972d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
7982d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
7992d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private boolean parseOptionsResult(EventObject evt) {
8002d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            if (expectResponse(Request.OPTIONS, evt)) {
8012d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                ResponseEvent event = (ResponseEvent) evt;
8022d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                int rPort = getRPortFromResponse(event.getResponse());
8032d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                if (rPort != -1) {
8042d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    if (mRPort == 0) mRPort = rPort;
8052d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    if (mRPort != rPort) {
8062d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        mReRegisterFlag = true;
807c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan                        if (DEBUG) Log.w(TAG, String.format(
808c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan                                "rport is changed: %d <> %d", mRPort, rPort));
8092d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        mRPort = rPort;
8102d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    } else {
811c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan                        if (DEBUG_PING) Log.w(TAG, "rport is the same: " + rPort);
8122d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    }
8132d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                } else {
814c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan                    if (DEBUG) Log.w(TAG, "peer did not respond rport");
8152d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                }
8163d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                reset();
8172d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return true;
8182d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
8192d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return false;
8202d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
8212d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
8222d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private int getRPortFromResponse(Response response) {
8232d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            ViaHeader viaHeader = (ViaHeader)(response.getHeader(
8242d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    SIPHeaderNames.VIA));
8252d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return (viaHeader == null) ? -1 : viaHeader.getRPort();
8262d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
8272d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
8282d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private boolean registeringToReady(EventObject evt)
8292d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                throws SipException {
8302d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            if (expectResponse(Request.REGISTER, evt)) {
8312d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                ResponseEvent event = (ResponseEvent) evt;
8322d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                Response response = event.getResponse();
8332d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
8342d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                int statusCode = response.getStatusCode();
8352d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                switch (statusCode) {
8362d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                case Response.OK:
83797963794af1e18674dd111e3ad344d90b16c922cHung-ying Tyan                    int state = mState;
83884a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                    onRegistrationDone((state == SipSession.State.REGISTERING)
8392d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                            ? getExpiryTime(((ResponseEvent) evt).getResponse())
8402d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                            : -1);
8412d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    return true;
8422d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                case Response.UNAUTHORIZED:
8432d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                case Response.PROXY_AUTHENTICATION_REQUIRED:
844ee8a884f3504c981be8a1d6888b4590a0a394e05Hung-ying Tyan                    handleAuthentication(event);
8452d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    return true;
8462d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                default:
8472d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    if (statusCode >= 500) {
8483d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                        onRegistrationFailed(response);
8492d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        return true;
8502d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    }
8512d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                }
8522d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
8532d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return false;
8542d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
8552d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
856903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan        private boolean handleAuthentication(ResponseEvent event)
857903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                throws SipException {
858903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan            Response response = event.getResponse();
859903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan            String nonce = getNonceFromResponse(response);
860ee8a884f3504c981be8a1d6888b4590a0a394e05Hung-ying Tyan            if (nonce == null) {
861ee8a884f3504c981be8a1d6888b4590a0a394e05Hung-ying Tyan                onError(SipErrorCode.SERVER_ERROR,
862ee8a884f3504c981be8a1d6888b4590a0a394e05Hung-ying Tyan                        "server does not provide challenge");
863903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                return false;
864ee8a884f3504c981be8a1d6888b4590a0a394e05Hung-ying Tyan            } else if (mAuthenticationRetryCount < 2) {
865903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                mClientTransaction = mSipHelper.handleChallenge(
866903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                        event, getAccountManager());
867903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                mDialog = mClientTransaction.getDialog();
868ee8a884f3504c981be8a1d6888b4590a0a394e05Hung-ying Tyan                mAuthenticationRetryCount++;
869ee8a884f3504c981be8a1d6888b4590a0a394e05Hung-ying Tyan                if (isLoggable(this, event)) {
870ee8a884f3504c981be8a1d6888b4590a0a394e05Hung-ying Tyan                    Log.d(TAG, "   authentication retry count="
871ee8a884f3504c981be8a1d6888b4590a0a394e05Hung-ying Tyan                            + mAuthenticationRetryCount);
872ee8a884f3504c981be8a1d6888b4590a0a394e05Hung-ying Tyan                }
873903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                return true;
874ee8a884f3504c981be8a1d6888b4590a0a394e05Hung-ying Tyan            } else {
875a936b256eb1611b5d8b88d0cd61f21225152cc82Hung-ying Tyan                if (crossDomainAuthenticationRequired(response)) {
876a936b256eb1611b5d8b88d0cd61f21225152cc82Hung-ying Tyan                    onError(SipErrorCode.CROSS_DOMAIN_AUTHENTICATION,
877a936b256eb1611b5d8b88d0cd61f21225152cc82Hung-ying Tyan                            getRealmFromResponse(response));
878a936b256eb1611b5d8b88d0cd61f21225152cc82Hung-ying Tyan                } else {
879a936b256eb1611b5d8b88d0cd61f21225152cc82Hung-ying Tyan                    onError(SipErrorCode.INVALID_CREDENTIALS,
880a936b256eb1611b5d8b88d0cd61f21225152cc82Hung-ying Tyan                            "incorrect username or password");
881a936b256eb1611b5d8b88d0cd61f21225152cc82Hung-ying Tyan                }
882ee8a884f3504c981be8a1d6888b4590a0a394e05Hung-ying Tyan                return false;
883903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan            }
884903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan        }
885903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan
88600a22064efef4f574e439079aae2deae1a087a31Hung-ying Tyan        private boolean crossDomainAuthenticationRequired(Response response) {
88700a22064efef4f574e439079aae2deae1a087a31Hung-ying Tyan            String realm = getRealmFromResponse(response);
88800a22064efef4f574e439079aae2deae1a087a31Hung-ying Tyan            if (realm == null) realm = "";
88900a22064efef4f574e439079aae2deae1a087a31Hung-ying Tyan            return !mLocalProfile.getSipDomain().trim().equals(realm.trim());
89000a22064efef4f574e439079aae2deae1a087a31Hung-ying Tyan        }
89100a22064efef4f574e439079aae2deae1a087a31Hung-ying Tyan
8922d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private AccountManager getAccountManager() {
8932d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return new AccountManager() {
8942d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                public UserCredentials getCredentials(ClientTransaction
8952d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        challengedTransaction, String realm) {
8962d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    return new UserCredentials() {
8972d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        public String getUserName() {
8982d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                            return mLocalProfile.getUserName();
8992d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        }
9002d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
9012d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        public String getPassword() {
9022d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                            return mPassword;
9032d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        }
9042d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
9052d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        public String getSipDomain() {
9062d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                            return mLocalProfile.getSipDomain();
9072d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        }
9082d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    };
9092d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                }
9102d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            };
9112d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
9122d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
91300a22064efef4f574e439079aae2deae1a087a31Hung-ying Tyan        private String getRealmFromResponse(Response response) {
91400a22064efef4f574e439079aae2deae1a087a31Hung-ying Tyan            WWWAuthenticate wwwAuth = (WWWAuthenticate)response.getHeader(
91500a22064efef4f574e439079aae2deae1a087a31Hung-ying Tyan                    SIPHeaderNames.WWW_AUTHENTICATE);
91600a22064efef4f574e439079aae2deae1a087a31Hung-ying Tyan            if (wwwAuth != null) return wwwAuth.getRealm();
91700a22064efef4f574e439079aae2deae1a087a31Hung-ying Tyan            ProxyAuthenticate proxyAuth = (ProxyAuthenticate)response.getHeader(
91800a22064efef4f574e439079aae2deae1a087a31Hung-ying Tyan                    SIPHeaderNames.PROXY_AUTHENTICATE);
91900a22064efef4f574e439079aae2deae1a087a31Hung-ying Tyan            return (proxyAuth == null) ? null : proxyAuth.getRealm();
92000a22064efef4f574e439079aae2deae1a087a31Hung-ying Tyan        }
92100a22064efef4f574e439079aae2deae1a087a31Hung-ying Tyan
9222d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private String getNonceFromResponse(Response response) {
9237d137e40cd36290c6bfb5beaf66f4018ae92c97fChung-yih Wang            WWWAuthenticate wwwAuth = (WWWAuthenticate)response.getHeader(
9247d137e40cd36290c6bfb5beaf66f4018ae92c97fChung-yih Wang                    SIPHeaderNames.WWW_AUTHENTICATE);
9257d137e40cd36290c6bfb5beaf66f4018ae92c97fChung-yih Wang            if (wwwAuth != null) return wwwAuth.getNonce();
9267d137e40cd36290c6bfb5beaf66f4018ae92c97fChung-yih Wang            ProxyAuthenticate proxyAuth = (ProxyAuthenticate)response.getHeader(
9277d137e40cd36290c6bfb5beaf66f4018ae92c97fChung-yih Wang                    SIPHeaderNames.PROXY_AUTHENTICATE);
9287d137e40cd36290c6bfb5beaf66f4018ae92c97fChung-yih Wang            return (proxyAuth == null) ? null : proxyAuth.getNonce();
9292d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
9302d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
9312d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private boolean readyForCall(EventObject evt) throws SipException {
9322d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            // expect MakeCallCommand, RegisterCommand, DEREGISTER
9332d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            if (evt instanceof MakeCallCommand) {
934fb3a98b1d8d0ad040980d509c4c5341928b9460bHung-ying Tyan                mState = SipSession.State.OUTGOING_CALL;
9352d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                MakeCallCommand cmd = (MakeCallCommand) evt;
9362d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mPeerProfile = cmd.getPeerProfile();
9372d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mClientTransaction = mSipHelper.sendInvite(mLocalProfile,
93895b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh                        mPeerProfile, cmd.getSessionDescription(),
93995b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh                        generateTag());
9402d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mDialog = mClientTransaction.getDialog();
9412d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                addSipSession(this);
9429352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                startSessionTimer(cmd.getTimeout());
9439ea96c6cade1f25d4d77dcbd24854df431548b36Hung-ying Tyan                mProxy.onCalling(this);
9442d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return true;
9452d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            } else if (evt instanceof RegisterCommand) {
946fb3a98b1d8d0ad040980d509c4c5341928b9460bHung-ying Tyan                mState = SipSession.State.REGISTERING;
9472d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                int duration = ((RegisterCommand) evt).getDuration();
9482d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mClientTransaction = mSipHelper.sendRegister(mLocalProfile,
9492d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        generateTag(), duration);
9502d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mDialog = mClientTransaction.getDialog();
9512d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                addSipSession(this);
9522d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mProxy.onRegistering(this);
9532d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return true;
9542d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            } else if (DEREGISTER == evt) {
955fb3a98b1d8d0ad040980d509c4c5341928b9460bHung-ying Tyan                mState = SipSession.State.DEREGISTERING;
9562d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mClientTransaction = mSipHelper.sendRegister(mLocalProfile,
9572d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        generateTag(), 0);
9582d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mDialog = mClientTransaction.getDialog();
9592d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                addSipSession(this);
9602d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mProxy.onRegistering(this);
9612d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return true;
9622d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
9632d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return false;
9642d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
9652d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
9662d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private boolean incomingCall(EventObject evt) throws SipException {
9672d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            // expect MakeCallCommand(answering) , END_CALL cmd , Cancel
9682d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            if (evt instanceof MakeCallCommand) {
9692d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                // answer call
970fb3a98b1d8d0ad040980d509c4c5341928b9460bHung-ying Tyan                mState = SipSession.State.INCOMING_CALL_ANSWERING;
9712d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mServerTransaction = mSipHelper.sendInviteOk(mInviteReceived,
9722d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        mLocalProfile,
9732d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        ((MakeCallCommand) evt).getSessionDescription(),
9742d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        mServerTransaction);
9759352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                startSessionTimer(((MakeCallCommand) evt).getTimeout());
9762d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return true;
9772d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            } else if (END_CALL == evt) {
9782d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mSipHelper.sendInviteBusyHere(mInviteReceived,
9792d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        mServerTransaction);
9802d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                endCallNormally();
9812d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return true;
9822d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            } else if (isRequestEvent(Request.CANCEL, evt)) {
9832d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                RequestEvent event = (RequestEvent) evt;
9842d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mSipHelper.sendResponse(event, Response.OK);
9852d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mSipHelper.sendInviteRequestTerminated(
9862d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        mInviteReceived.getRequest(), mServerTransaction);
9872d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                endCallNormally();
9882d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return true;
9892d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
9902d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return false;
9912d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
9922d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
9932d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private boolean incomingCallToInCall(EventObject evt)
9942d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                throws SipException {
9952d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            // expect ACK, CANCEL request
9962d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            if (isRequestEvent(Request.ACK, evt)) {
9972d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                establishCall();
9982d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return true;
9992d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            } else if (isRequestEvent(Request.CANCEL, evt)) {
10002d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                // http://tools.ietf.org/html/rfc3261#section-9.2
10012d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                // Final response has been sent; do nothing here.
10022d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return true;
10032d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
10042d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return false;
10052d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
10062d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
10072d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private boolean outgoingCall(EventObject evt) throws SipException {
10082d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            if (expectResponse(Request.INVITE, evt)) {
10092d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                ResponseEvent event = (ResponseEvent) evt;
10102d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                Response response = event.getResponse();
10112d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
10122d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                int statusCode = response.getStatusCode();
10132d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                switch (statusCode) {
10142d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                case Response.RINGING:
10156057cd00d95c756b78f22c67279cb982bc0674efHung-ying Tyan                case Response.CALL_IS_BEING_FORWARDED:
10166057cd00d95c756b78f22c67279cb982bc0674efHung-ying Tyan                case Response.QUEUED:
10176057cd00d95c756b78f22c67279cb982bc0674efHung-ying Tyan                case Response.SESSION_PROGRESS:
10186057cd00d95c756b78f22c67279cb982bc0674efHung-ying Tyan                    // feedback any provisional responses (except TRYING) as
10196057cd00d95c756b78f22c67279cb982bc0674efHung-ying Tyan                    // ring back for better UX
102084a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                    if (mState == SipSession.State.OUTGOING_CALL) {
102184a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                        mState = SipSession.State.OUTGOING_CALL_RING_BACK;
10229352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                        cancelSessionTimer();
10239ea96c6cade1f25d4d77dcbd24854df431548b36Hung-ying Tyan                        mProxy.onRingingBack(this);
10242d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    }
10252d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    return true;
10262d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                case Response.OK:
10272d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    mSipHelper.sendInviteAck(event, mDialog);
102895b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh                    mPeerSessionDescription = extractContent(response);
10292d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    establishCall();
10302d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    return true;
10310e0633828928481658c0e09e5893f6214b57ba38Chung-yih Wang                case Response.UNAUTHORIZED:
10322d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                case Response.PROXY_AUTHENTICATION_REQUIRED:
1033a936b256eb1611b5d8b88d0cd61f21225152cc82Hung-ying Tyan                    if (handleAuthentication(event)) {
1034903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                        addSipSession(this);
1035903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                    }
10362d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    return true;
10372d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                case Response.REQUEST_PENDING:
10382d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    // TODO:
10392d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    // rfc3261#section-14.1; re-schedule invite
10402d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    return true;
10412d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                default:
10422d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    if (statusCode >= 400) {
10432d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        // error: an ack is sent automatically by the stack
1044903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                        onError(response);
10452d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        return true;
10462d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    } else if (statusCode >= 300) {
10472d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        // TODO: handle 3xx (redirect)
10482d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    } else {
10492d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        return true;
10502d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    }
10512d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                }
10522d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return false;
10532d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            } else if (END_CALL == evt) {
10542d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                // RFC says that UA should not send out cancel when no
10552d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                // response comes back yet. We are cheating for not checking
10562d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                // response.
105784a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                mState = SipSession.State.OUTGOING_CALL_CANCELING;
1058fb3a98b1d8d0ad040980d509c4c5341928b9460bHung-ying Tyan                mSipHelper.sendCancel(mClientTransaction);
10599352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                startSessionTimer(CANCEL_CALL_TIMER);
10602d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return true;
10610a6e717fb6846f66b8dc853e079f2166307bfc60Hung-ying Tyan            } else if (isRequestEvent(Request.INVITE, evt)) {
10620a6e717fb6846f66b8dc853e079f2166307bfc60Hung-ying Tyan                // Call self? Send BUSY HERE so server may redirect the call to
10630a6e717fb6846f66b8dc853e079f2166307bfc60Hung-ying Tyan                // voice mailbox.
10640a6e717fb6846f66b8dc853e079f2166307bfc60Hung-ying Tyan                RequestEvent event = (RequestEvent) evt;
10650a6e717fb6846f66b8dc853e079f2166307bfc60Hung-ying Tyan                mSipHelper.sendInviteBusyHere(event,
10660a6e717fb6846f66b8dc853e079f2166307bfc60Hung-ying Tyan                        event.getServerTransaction());
10670a6e717fb6846f66b8dc853e079f2166307bfc60Hung-ying Tyan                return true;
10682d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
10692d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return false;
10702d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
10712d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
10722d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private boolean outgoingCallToReady(EventObject evt)
10732d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                throws SipException {
10742d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            if (evt instanceof ResponseEvent) {
10752d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                ResponseEvent event = (ResponseEvent) evt;
10762d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                Response response = event.getResponse();
10772d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                int statusCode = response.getStatusCode();
10782d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                if (expectResponse(Request.CANCEL, evt)) {
1079025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan                    if (statusCode == Response.OK) {
1080025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan                        // do nothing; wait for REQUEST_TERMINATED
1081025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan                        return true;
1082025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan                    }
1083025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan                } else if (expectResponse(Request.INVITE, evt)) {
10849352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                    switch (statusCode) {
10859352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                        case Response.OK:
1086025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan                            outgoingCall(evt); // abort Cancel
10879352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                            return true;
10889352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                        case Response.REQUEST_TERMINATED:
10899352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                            endCallNormally();
10909352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                            return true;
10912d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    }
10922d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                } else {
10932d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    return false;
10942d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                }
10952d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
10962d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                if (statusCode >= 400) {
10973d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                    onError(response);
10982d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    return true;
10992d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                }
11002d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            } else if (evt instanceof TransactionTerminatedEvent) {
11012d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                // rfc3261#section-14.1:
11022d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                // if re-invite gets timed out, terminate the dialog; but
11032d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                // re-invite is not reliable, just let it go and pretend
11042d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                // nothing happened.
11052d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                onError(new SipException("timed out"));
11062d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
11072d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return false;
11082d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
11092d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
11102d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private boolean inCall(EventObject evt) throws SipException {
11112d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            // expect END_CALL cmd, BYE request, hold call (MakeCallCommand)
11122d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            // OK retransmission is handled in SipStack
11132d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            if (END_CALL == evt) {
11142d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                // rfc3261#section-15.1.1
11152d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mSipHelper.sendBye(mDialog);
11162d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                endCallNormally();
11172d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return true;
11182d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            } else if (isRequestEvent(Request.INVITE, evt)) {
11192d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                // got Re-INVITE
112084a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                mState = SipSession.State.INCOMING_CALL;
1121fb3a98b1d8d0ad040980d509c4c5341928b9460bHung-ying Tyan                RequestEvent event = mInviteReceived = (RequestEvent) evt;
112295b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh                mPeerSessionDescription = extractContent(event.getRequest());
11232d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mServerTransaction = null;
11242d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mProxy.onRinging(this, mPeerProfile, mPeerSessionDescription);
11252d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return true;
11262d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            } else if (isRequestEvent(Request.BYE, evt)) {
11272d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mSipHelper.sendResponse((RequestEvent) evt, Response.OK);
11282d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                endCallNormally();
11292d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return true;
11302d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            } else if (evt instanceof MakeCallCommand) {
11312d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                // to change call
1132fb3a98b1d8d0ad040980d509c4c5341928b9460bHung-ying Tyan                mState = SipSession.State.OUTGOING_CALL;
11332d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                mClientTransaction = mSipHelper.sendReinvite(mDialog,
11342d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                        ((MakeCallCommand) evt).getSessionDescription());
11359352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                startSessionTimer(((MakeCallCommand) evt).getTimeout());
11362d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return true;
11372d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
11382d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return false;
11392d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
11402d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
11419352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan        // timeout in seconds
11429352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan        private void startSessionTimer(int timeout) {
11439352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan            if (timeout > 0) {
11449352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                mTimer = new SessionTimer();
11459352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                mTimer.start(timeout);
11469352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan            }
11479352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan        }
11489352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan
11499352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan        private void cancelSessionTimer() {
11509352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan            if (mTimer != null) {
11519352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                mTimer.cancel();
11529352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                mTimer = null;
11539352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan            }
11549352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan        }
11559352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan
1156903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan        private String createErrorMessage(Response response) {
1157624d5b4e8c20516516d0bff74479b9f5abdfe61cHung-ying Tyan            return String.format("%s (%d)", response.getReasonPhrase(),
1158624d5b4e8c20516516d0bff74479b9f5abdfe61cHung-ying Tyan                    response.getStatusCode());
1159903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan        }
1160903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan
11612d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private void establishCall() {
116284a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan            mState = SipSession.State.IN_CALL;
11632d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            mInCall = true;
11649352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan            cancelSessionTimer();
11652d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            mProxy.onCallEstablished(this, mPeerSessionDescription);
11662d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
11672d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
11682d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private void endCallNormally() {
11692d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            reset();
11702d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            mProxy.onCallEnded(this);
11712d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
11722d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
117397963794af1e18674dd111e3ad344d90b16c922cHung-ying Tyan        private void endCallOnError(int errorCode, String message) {
11742d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            reset();
117597963794af1e18674dd111e3ad344d90b16c922cHung-ying Tyan            mProxy.onError(this, errorCode, message);
1176903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan        }
1177903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan
1178903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan        private void endCallOnBusy() {
1179903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan            reset();
1180903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan            mProxy.onCallBusy(this);
11812d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
11822d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
118397963794af1e18674dd111e3ad344d90b16c922cHung-ying Tyan        private void onError(int errorCode, String message) {
11849352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan            cancelSessionTimer();
11853d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan            switch (mState) {
118684a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                case SipSession.State.REGISTERING:
118784a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                case SipSession.State.DEREGISTERING:
11883d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                    onRegistrationFailed(errorCode, message);
11893d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                    break;
11903d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                default:
11914189d99b6e4877352049b7447b7f0734ef99b9e8Hung-ying Tyan                    endCallOnError(errorCode, message);
11922d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
11932d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
11942d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
11953d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan
11963d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan        private void onError(Throwable exception) {
11973d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan            exception = getRootCause(exception);
11983d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan            onError(getErrorCode(exception), exception.toString());
11993d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan        }
12003d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan
1201903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan        private void onError(Response response) {
12023d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan            int statusCode = response.getStatusCode();
1203ae076d3981fda732d54b6c6e37e5659b2e7ba130Hung-ying Tyan            if (!mInCall && (statusCode == Response.BUSY_HERE)) {
12043d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                endCallOnBusy();
1205903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan            } else {
12063d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                onError(getErrorCode(statusCode), createErrorMessage(response));
1207903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan            }
1208903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan        }
1209903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan
121097963794af1e18674dd111e3ad344d90b16c922cHung-ying Tyan        private int getErrorCode(int responseStatusCode) {
1211903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan            switch (responseStatusCode) {
1212ae076d3981fda732d54b6c6e37e5659b2e7ba130Hung-ying Tyan                case Response.TEMPORARILY_UNAVAILABLE:
1213ae076d3981fda732d54b6c6e37e5659b2e7ba130Hung-ying Tyan                case Response.FORBIDDEN:
1214ae076d3981fda732d54b6c6e37e5659b2e7ba130Hung-ying Tyan                case Response.GONE:
1215903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                case Response.NOT_FOUND:
1216ae076d3981fda732d54b6c6e37e5659b2e7ba130Hung-ying Tyan                case Response.NOT_ACCEPTABLE:
1217ae076d3981fda732d54b6c6e37e5659b2e7ba130Hung-ying Tyan                case Response.NOT_ACCEPTABLE_HERE:
1218ae076d3981fda732d54b6c6e37e5659b2e7ba130Hung-ying Tyan                    return SipErrorCode.PEER_NOT_REACHABLE;
1219ae076d3981fda732d54b6c6e37e5659b2e7ba130Hung-ying Tyan
1220ae076d3981fda732d54b6c6e37e5659b2e7ba130Hung-ying Tyan                case Response.REQUEST_URI_TOO_LONG:
1221903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                case Response.ADDRESS_INCOMPLETE:
1222ae076d3981fda732d54b6c6e37e5659b2e7ba130Hung-ying Tyan                case Response.AMBIGUOUS:
1223903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                    return SipErrorCode.INVALID_REMOTE_URI;
1224ae076d3981fda732d54b6c6e37e5659b2e7ba130Hung-ying Tyan
1225903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                case Response.REQUEST_TIMEOUT:
1226903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                    return SipErrorCode.TIME_OUT;
1227ae076d3981fda732d54b6c6e37e5659b2e7ba130Hung-ying Tyan
1228903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                default:
1229903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                    if (responseStatusCode < 500) {
1230903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                        return SipErrorCode.CLIENT_ERROR;
1231903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                    } else {
1232903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                        return SipErrorCode.SERVER_ERROR;
1233903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                    }
1234903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan            }
1235903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan        }
1236903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan
1237903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan        private Throwable getRootCause(Throwable exception) {
1238903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan            Throwable cause = exception.getCause();
1239903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan            while (cause != null) {
1240903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                exception = cause;
1241903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                cause = exception.getCause();
1242903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan            }
1243903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan            return exception;
1244903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan        }
1245903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan
124697963794af1e18674dd111e3ad344d90b16c922cHung-ying Tyan        private int getErrorCode(Throwable exception) {
1247903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan            String message = exception.getMessage();
1248903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan            if (exception instanceof UnknownHostException) {
1249c6548fd9eda7b58f5a2e2a9c01e3c7cafd42fafbHung-ying Tyan                return SipErrorCode.SERVER_UNREACHABLE;
1250903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan            } else if (exception instanceof IOException) {
1251903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                return SipErrorCode.SOCKET_ERROR;
1252903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan            } else {
1253903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                return SipErrorCode.CLIENT_ERROR;
1254903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan            }
1255903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan        }
1256903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan
12572d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private void onRegistrationDone(int duration) {
12583d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan            reset();
12592d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            mProxy.onRegistrationDone(this, duration);
12602d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
12612d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
126297963794af1e18674dd111e3ad344d90b16c922cHung-ying Tyan        private void onRegistrationFailed(int errorCode, String message) {
12633d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan            reset();
126497963794af1e18674dd111e3ad344d90b16c922cHung-ying Tyan            mProxy.onRegistrationFailed(this, errorCode, message);
1265903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan        }
1266903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan
12672d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private void onRegistrationFailed(Throwable exception) {
1268903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan            exception = getRootCause(exception);
1269903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan            onRegistrationFailed(getErrorCode(exception),
1270903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan                    exception.toString());
12712d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
12723d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan
12733d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan        private void onRegistrationFailed(Response response) {
12743d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan            int statusCode = response.getStatusCode();
12753d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan            onRegistrationFailed(getErrorCode(statusCode),
12763d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan                    createErrorMessage(response));
12773d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan        }
12782d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
12792d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
12802d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    /**
12812d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang     * @return true if the event is a request event matching the specified
12822d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang     *      method; false otherwise
12832d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang     */
12842d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private static boolean isRequestEvent(String method, EventObject event) {
12852d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        try {
12862d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            if (event instanceof RequestEvent) {
12872d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                RequestEvent requestEvent = (RequestEvent) event;
12882d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return method.equals(requestEvent.getRequest().getMethod());
12892d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
12902d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        } catch (Throwable e) {
12912d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
12922d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        return false;
12932d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
12942d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
12952d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private static String getCseqMethod(Message message) {
12962d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        return ((CSeqHeader) message.getHeader(CSeqHeader.NAME)).getMethod();
12972d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
12982d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
12992d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    /**
13002d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang     * @return true if the event is a response event and the CSeqHeader method
13012d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang     * match the given arguments; false otherwise
13022d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang     */
13032d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private static boolean expectResponse(
13042d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            String expectedMethod, EventObject evt) {
13052d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        if (evt instanceof ResponseEvent) {
13062d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            ResponseEvent event = (ResponseEvent) evt;
13072d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            Response response = event.getResponse();
13082d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return expectedMethod.equalsIgnoreCase(getCseqMethod(response));
13092d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
13102d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        return false;
13112d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
13122d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
13132d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    /**
13142d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang     * @return true if the event is a response event and the response code and
13152d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang     *      CSeqHeader method match the given arguments; false otherwise
13162d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang     */
13172d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private static boolean expectResponse(
13182d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            int responseCode, String expectedMethod, EventObject evt) {
13192d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        if (evt instanceof ResponseEvent) {
13202d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            ResponseEvent event = (ResponseEvent) evt;
13212d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            Response response = event.getResponse();
13222d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            if (response.getStatusCode() == responseCode) {
13232d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                return expectedMethod.equalsIgnoreCase(getCseqMethod(response));
13242d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            }
13252d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
13262d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        return false;
13272d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
13282d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
13292d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private static SipProfile createPeerProfile(Request request)
13302d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            throws SipException {
13312d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        try {
13322d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            FromHeader fromHeader =
13332d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang                    (FromHeader) request.getHeader(FromHeader.NAME);
13342d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            Address address = fromHeader.getAddress();
13352d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            SipURI uri = (SipURI) address.getURI();
13362d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            String username = uri.getUser();
13372d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            if (username == null) username = ANONYMOUS;
133858ee2acba8953814cc4bf65d2f28f7dd498b5779Hung-ying Tyan            int port = uri.getPort();
133958ee2acba8953814cc4bf65d2f28f7dd498b5779Hung-ying Tyan            SipProfile.Builder builder =
134058ee2acba8953814cc4bf65d2f28f7dd498b5779Hung-ying Tyan                    new SipProfile.Builder(username, uri.getHost())
134158ee2acba8953814cc4bf65d2f28f7dd498b5779Hung-ying Tyan                    .setDisplayName(address.getDisplayName());
134258ee2acba8953814cc4bf65d2f28f7dd498b5779Hung-ying Tyan            if (port > 0) builder.setPort(port);
134358ee2acba8953814cc4bf65d2f28f7dd498b5779Hung-ying Tyan            return builder.build();
134499bf4e45c4566172189735b34b368b76660ca57aHung-ying Tyan        } catch (IllegalArgumentException e) {
13452d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            throw new SipException("createPeerProfile()", e);
13462d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        } catch (ParseException e) {
13472d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            throw new SipException("createPeerProfile()", e);
13482d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
13492d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
13502d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
1351c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan    private static boolean isLoggable(SipSessionImpl s) {
1352c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan        if (s != null) {
1353c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan            switch (s.mState) {
135484a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan                case SipSession.State.PINGING:
1355c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan                    return DEBUG_PING;
1356c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan            }
1357c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan        }
1358c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan        return DEBUG;
1359c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan    }
1360c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan
13610a6e717fb6846f66b8dc853e079f2166307bfc60Hung-ying Tyan    private static boolean isLoggable(EventObject evt) {
13620a6e717fb6846f66b8dc853e079f2166307bfc60Hung-ying Tyan        return isLoggable(null, evt);
13630a6e717fb6846f66b8dc853e079f2166307bfc60Hung-ying Tyan    }
13640a6e717fb6846f66b8dc853e079f2166307bfc60Hung-ying Tyan
1365c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan    private static boolean isLoggable(SipSessionImpl s, EventObject evt) {
1366c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan        if (!isLoggable(s)) return false;
1367c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan        if (evt == null) return false;
1368c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan
1369c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan        if (evt instanceof OptionsCommand) {
1370c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan            return DEBUG_PING;
1371c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan        } else if (evt instanceof ResponseEvent) {
1372c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan            Response response = ((ResponseEvent) evt).getResponse();
1373c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan            if (Request.OPTIONS.equals(response.getHeader(CSeqHeader.NAME))) {
1374c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan                return DEBUG_PING;
1375c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan            }
1376c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan            return DEBUG;
1377c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan        } else if (evt instanceof RequestEvent) {
1378c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan            return DEBUG;
1379c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan        }
1380c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan        return false;
1381c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan    }
1382c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan
13832d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private static String log(EventObject evt) {
13842d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        if (evt instanceof RequestEvent) {
13852d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return ((RequestEvent) evt).getRequest().toString();
13862d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        } else if (evt instanceof ResponseEvent) {
13872d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return ((ResponseEvent) evt).getResponse().toString();
13882d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        } else {
13892d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return evt.toString();
13902d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
13912d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
13922d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
13932d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private class OptionsCommand extends EventObject {
13942d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public OptionsCommand() {
13952d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            super(SipSessionGroup.this);
13962d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
13972d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
13982d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
13992d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private class RegisterCommand extends EventObject {
14002d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        private int mDuration;
14012d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
14022d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public RegisterCommand(int duration) {
14032d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            super(SipSessionGroup.this);
14042d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            mDuration = duration;
14052d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
14062d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
14072d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public int getDuration() {
14082d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return mDuration;
14092d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
14102d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    }
14112d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
14122d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang    private class MakeCallCommand extends EventObject {
141395b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh        private String mSessionDescription;
14149352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan        private int mTimeout; // in seconds
14152d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
14162d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public MakeCallCommand(SipProfile peerProfile,
141795b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh                String sessionDescription) {
14189352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan            this(peerProfile, sessionDescription, -1);
14199352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan        }
14209352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan
14219352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan        public MakeCallCommand(SipProfile peerProfile,
14229352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan                String sessionDescription, int timeout) {
14232d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            super(peerProfile);
14242d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            mSessionDescription = sessionDescription;
14259352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan            mTimeout = timeout;
14262d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
14272d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
14282d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        public SipProfile getPeerProfile() {
14292d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return (SipProfile) getSource();
14302d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
14312d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
143295b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh        public String getSessionDescription() {
14332d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang            return mSessionDescription;
14342d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang        }
14352d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang
14369352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan        public int getTimeout() {
14379352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan            return mTimeout;
14389352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan        }
14399352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan    }
14402d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang}
1441