SipSessionGroup.java revision 1aceda35cc607856ec2e960e0c6cfc6aea87ab8e
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; 241aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo syncimport gov.nist.javax.sip.header.extensions.ReferredByHeader; 251aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo syncimport gov.nist.javax.sip.header.extensions.ReplacesHeader; 2695b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yehimport gov.nist.javax.sip.message.SIPMessage; 272d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 282d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport android.net.sip.ISipSession; 292d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport android.net.sip.ISipSessionListener; 30903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyanimport android.net.sip.SipErrorCode; 312d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport android.net.sip.SipProfile; 3284a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyanimport android.net.sip.SipSession; 334a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyanimport android.net.sip.SipSessionAdapter; 342d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport android.text.TextUtils; 352d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport android.util.Log; 362d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 372d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport java.io.IOException; 3895b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yehimport java.io.UnsupportedEncodingException; 392d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport java.net.DatagramSocket; 40903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyanimport java.net.UnknownHostException; 412d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport java.text.ParseException; 422d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport java.util.Collection; 432d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport java.util.EventObject; 442d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport java.util.HashMap; 452d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport java.util.Map; 462d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport java.util.Properties; 472d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport java.util.TooManyListenersException; 482d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 492d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.ClientTransaction; 502d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.Dialog; 512d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.DialogTerminatedEvent; 522d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.IOExceptionEvent; 532d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.InvalidArgumentException; 542d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.ListeningPoint; 559ea96c6cade1f25d4d77dcbd24854df431548b36Hung-ying Tyanimport javax.sip.ObjectInUseException; 562d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.RequestEvent; 572d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.ResponseEvent; 582d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.ServerTransaction; 592d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.SipException; 602d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.SipFactory; 612d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.SipListener; 622d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.SipProvider; 632d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.SipStack; 642d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.TimeoutEvent; 652d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.Transaction; 662d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.TransactionState; 672d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.TransactionTerminatedEvent; 68903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyanimport javax.sip.TransactionUnavailableException; 692d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.address.Address; 702d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.address.SipURI; 712d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.header.CSeqHeader; 722d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.header.ExpiresHeader; 732d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.header.FromHeader; 742d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.header.MinExpiresHeader; 752d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.header.ViaHeader; 762d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.message.Message; 772d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.message.Request; 782d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.message.Response; 792d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 802d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang/** 812d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang * Manages {@link ISipSession}'s for a SIP account. 822d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang */ 832d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangclass SipSessionGroup implements SipListener { 842d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang private static final String TAG = "SipSession"; 85c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan private static final boolean DEBUG = true; 86c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan private static final boolean DEBUG_PING = DEBUG && false; 872d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang private static final String ANONYMOUS = "anonymous"; 8866cc5355a137e291cc1e3c5d871e1d9cd35ee0abChung-yih Wang // Limit the size of thread pool to 1 for the order issue when the phone is 8966cc5355a137e291cc1e3c5d871e1d9cd35ee0abChung-yih Wang // waken up from sleep and there are many packets to be processed in the SIP 9066cc5355a137e291cc1e3c5d871e1d9cd35ee0abChung-yih Wang // stack. Note: The default thread pool size in NIST SIP stack is -1 which is 9166cc5355a137e291cc1e3c5d871e1d9cd35ee0abChung-yih Wang // unlimited. 9266cc5355a137e291cc1e3c5d871e1d9cd35ee0abChung-yih Wang private static final String THREAD_POOL_SIZE = "1"; 939352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan private static final int EXPIRY_TIME = 3600; // in seconds 94194bbcce9ba15634500f542b9ea017b2cf154b45Hung-ying Tyan private static final int CANCEL_CALL_TIMER = 3; // in seconds 954a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan private static final int KEEPALIVE_TIMEOUT = 3; // in seconds 9628f63c06894b9ca9252f43bc54a098c0a785d4b4Hung-ying Tyan private static final long WAKE_LOCK_HOLDING_TIME = 500; // in milliseconds 972d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 982d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang private static final EventObject DEREGISTER = new EventObject("Deregister"); 992d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang private static final EventObject END_CALL = new EventObject("End call"); 1002d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang private static final EventObject HOLD_CALL = new EventObject("Hold call"); 1012d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang private static final EventObject CONTINUE_CALL 1022d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang = new EventObject("Continue call"); 1032d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 1042d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang private final SipProfile mLocalProfile; 1052d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang private final String mPassword; 1062d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 1072d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang private SipStack mSipStack; 1082d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang private SipHelper mSipHelper; 1092d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 1102d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang // session that processes INVITE requests 1112d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang private SipSessionImpl mCallReceiverSession; 1122d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang private String mLocalIp; 1132d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 1144a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan private SipWakeupTimer mWakeupTimer; 11528f63c06894b9ca9252f43bc54a098c0a785d4b4Hung-ying Tyan private SipWakeLock mWakeLock; 11628f63c06894b9ca9252f43bc54a098c0a785d4b4Hung-ying Tyan 1172d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang // call-id-to-SipSession map 1182d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang private Map<String, SipSessionImpl> mSessionMap = 1192d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang new HashMap<String, SipSessionImpl>(); 1202d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 1212d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang /** 1222d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang * @param myself the local profile with password crossed out 1232d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang * @param password the password of the profile 1242d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang * @throws IOException if cannot assign requested address 1252d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang */ 12628f63c06894b9ca9252f43bc54a098c0a785d4b4Hung-ying Tyan public SipSessionGroup(String localIp, SipProfile myself, String password, 1274a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan SipWakeupTimer timer, SipWakeLock wakeLock) throws SipException, 1284a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan IOException { 1292d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mLocalProfile = myself; 1302d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mPassword = password; 1314a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan mWakeupTimer = timer; 13228f63c06894b9ca9252f43bc54a098c0a785d4b4Hung-ying Tyan mWakeLock = wakeLock; 1332d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang reset(localIp); 1342d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 1352d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 1364a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan // TODO: remove this method once SipWakeupTimer can better handle variety 1374a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan // of timeout values 1384a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan void setWakeupTimer(SipWakeupTimer timer) { 1394a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan mWakeupTimer = timer; 1404a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } 1414a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan 14284a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan synchronized void reset(String localIp) throws SipException, IOException { 1432d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mLocalIp = localIp; 1442d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang if (localIp == null) return; 1452d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 1462d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang SipProfile myself = mLocalProfile; 1472d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang SipFactory sipFactory = SipFactory.getInstance(); 1482d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang Properties properties = new Properties(); 1492d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang properties.setProperty("javax.sip.STACK_NAME", getStackName()); 150b4116c09fb9784551911ea0a10b4dd321ff9aa7dChung-yih Wang properties.setProperty( 151b4116c09fb9784551911ea0a10b4dd321ff9aa7dChung-yih Wang "gov.nist.javax.sip.THREAD_POOL_SIZE", THREAD_POOL_SIZE); 1522d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang String outboundProxy = myself.getProxyAddress(); 1532d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang if (!TextUtils.isEmpty(outboundProxy)) { 1545de1d36dd0415c4cf9afdf093a4915951ef6c770Chung-yih Wang Log.v(TAG, "outboundProxy is " + outboundProxy); 1552d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang properties.setProperty("javax.sip.OUTBOUND_PROXY", outboundProxy 1562d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang + ":" + myself.getPort() + "/" + myself.getProtocol()); 1572d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 1582d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang SipStack stack = mSipStack = sipFactory.createSipStack(properties); 1592d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 1602d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang try { 1612d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang SipProvider provider = stack.createSipProvider( 1622d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang stack.createListeningPoint(localIp, allocateLocalPort(), 1632d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang myself.getProtocol())); 1642d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang provider.addSipListener(this); 1652d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mSipHelper = new SipHelper(stack, provider); 1662d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } catch (InvalidArgumentException e) { 1672d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang throw new IOException(e.getMessage()); 1682d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } catch (TooManyListenersException e) { 1692d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang // must never happen 1702d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang throw new SipException("SipSessionGroup constructor", e); 1712d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 1722d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang Log.d(TAG, " start stack for " + myself.getUriString()); 1732d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang stack.start(); 1742d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 1752d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mCallReceiverSession = null; 1762d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mSessionMap.clear(); 1772d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 1782d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 179d231aa880ab006d51ffe03454c1fc082f1c97bb8Hung-ying Tyan synchronized void onConnectivityChanged() { 180ebc886c857a702d788c2bdfb9e0b3d20746ad745Hung-ying Tyan SipSessionImpl[] ss = mSessionMap.values().toArray( 181ebc886c857a702d788c2bdfb9e0b3d20746ad745Hung-ying Tyan new SipSessionImpl[mSessionMap.size()]); 182ebc886c857a702d788c2bdfb9e0b3d20746ad745Hung-ying Tyan // Iterate on the copied array instead of directly on mSessionMap to 183ebc886c857a702d788c2bdfb9e0b3d20746ad745Hung-ying Tyan // avoid ConcurrentModificationException being thrown when 184ebc886c857a702d788c2bdfb9e0b3d20746ad745Hung-ying Tyan // SipSessionImpl removes itself from mSessionMap in onError() in the 185ebc886c857a702d788c2bdfb9e0b3d20746ad745Hung-ying Tyan // following loop. 186ebc886c857a702d788c2bdfb9e0b3d20746ad745Hung-ying Tyan for (SipSessionImpl s : ss) { 187d231aa880ab006d51ffe03454c1fc082f1c97bb8Hung-ying Tyan s.onError(SipErrorCode.DATA_CONNECTION_LOST, 188d231aa880ab006d51ffe03454c1fc082f1c97bb8Hung-ying Tyan "data connection lost"); 189d231aa880ab006d51ffe03454c1fc082f1c97bb8Hung-ying Tyan } 190d231aa880ab006d51ffe03454c1fc082f1c97bb8Hung-ying Tyan } 191d231aa880ab006d51ffe03454c1fc082f1c97bb8Hung-ying Tyan 1922d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang public SipProfile getLocalProfile() { 1932d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return mLocalProfile; 1942d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 1952d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 1962d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang public String getLocalProfileUri() { 1972d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return mLocalProfile.getUriString(); 1982d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 1992d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 2002d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang private String getStackName() { 2012d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return "stack" + System.currentTimeMillis(); 2022d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 2032d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 2042d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang public synchronized void close() { 2052d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang Log.d(TAG, " close stack for " + mLocalProfile.getUriString()); 2065d0c5cf2d6c6e82bcdce95d72d9000a934b2f354Hung-ying Tyan onConnectivityChanged(); 2072d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mSessionMap.clear(); 2082d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang closeToNotReceiveCalls(); 2092d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang if (mSipStack != null) { 2102d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mSipStack.stop(); 2112d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mSipStack = null; 2122d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mSipHelper = null; 2132d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 2142d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 2152d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 2162d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang public synchronized boolean isClosed() { 2172d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return (mSipStack == null); 2182d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 2192d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 2202d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang // For internal use, require listener not to block in callbacks. 2212d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang public synchronized void openToReceiveCalls(ISipSessionListener listener) { 2222d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang if (mCallReceiverSession == null) { 2232d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mCallReceiverSession = new SipSessionCallReceiverImpl(listener); 2242d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } else { 2252d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mCallReceiverSession.setListener(listener); 2262d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 2272d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 2282d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 2292d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang public synchronized void closeToNotReceiveCalls() { 2302d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mCallReceiverSession = null; 2312d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 2322d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 2332d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang public ISipSession createSession(ISipSessionListener listener) { 2342d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return (isClosed() ? null : new SipSessionImpl(listener)); 2352d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 2362d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 2372d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang private static int allocateLocalPort() throws SipException { 2382d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang try { 2392d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang DatagramSocket s = new DatagramSocket(); 2402d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang int localPort = s.getLocalPort(); 2412d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang s.close(); 2422d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return localPort; 2432d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } catch (IOException e) { 2442d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang throw new SipException("allocateLocalPort()", e); 2452d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 2462d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 2472d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 2480a6e717fb6846f66b8dc853e079f2166307bfc60Hung-ying Tyan synchronized boolean containsSession(String callId) { 2490a6e717fb6846f66b8dc853e079f2166307bfc60Hung-ying Tyan return mSessionMap.containsKey(callId); 2500a6e717fb6846f66b8dc853e079f2166307bfc60Hung-ying Tyan } 2510a6e717fb6846f66b8dc853e079f2166307bfc60Hung-ying Tyan 2522d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang private synchronized SipSessionImpl getSipSession(EventObject event) { 2532d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang String key = SipHelper.getCallId(event); 2542d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang SipSessionImpl session = mSessionMap.get(key); 255c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan if ((session != null) && isLoggable(session)) { 256c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan Log.d(TAG, "session key from event: " + key); 257c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan Log.d(TAG, "active sessions:"); 258c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan for (String k : mSessionMap.keySet()) { 259c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan Log.d(TAG, " ..." + k + ": " + mSessionMap.get(k)); 260c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan } 261c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan } 2622d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return ((session != null) ? session : mCallReceiverSession); 2632d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 2642d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 2652d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang private synchronized void addSipSession(SipSessionImpl newSession) { 2662d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang removeSipSession(newSession); 2672d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang String key = newSession.getCallId(); 2682d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mSessionMap.put(key, newSession); 269c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan if (isLoggable(newSession)) { 270c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan Log.d(TAG, "+++ add a session with key: '" + key + "'"); 271c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan for (String k : mSessionMap.keySet()) { 272c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan Log.d(TAG, " " + k + ": " + mSessionMap.get(k)); 273c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan } 2742d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 2752d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 2762d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 2772d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang private synchronized void removeSipSession(SipSessionImpl session) { 2782d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang if (session == mCallReceiverSession) return; 2792d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang String key = session.getCallId(); 2802d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang SipSessionImpl s = mSessionMap.remove(key); 2812d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang // sanity check 2822d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang if ((s != null) && (s != session)) { 2832d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang Log.w(TAG, "session " + session + " is not associated with key '" 2842d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang + key + "'"); 2852d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mSessionMap.put(key, s); 2862d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang for (Map.Entry<String, SipSessionImpl> entry 2872d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang : mSessionMap.entrySet()) { 2882d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang if (entry.getValue() == s) { 2892d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang key = entry.getKey(); 2902d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mSessionMap.remove(key); 2912d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 2922d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 2932d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 2942d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 295c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan if ((s != null) && isLoggable(s)) { 296c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan Log.d(TAG, "remove session " + session + " @key '" + key + "'"); 297c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan for (String k : mSessionMap.keySet()) { 298c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan Log.d(TAG, " " + k + ": " + mSessionMap.get(k)); 299c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan } 3002d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 3012d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 3022d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 30328f63c06894b9ca9252f43bc54a098c0a785d4b4Hung-ying Tyan public void processRequest(final RequestEvent event) { 30428f63c06894b9ca9252f43bc54a098c0a785d4b4Hung-ying Tyan if (isRequestEvent(Request.INVITE, event)) { 30528f63c06894b9ca9252f43bc54a098c0a785d4b4Hung-ying Tyan if (DEBUG) Log.d(TAG, "<<<<< got INVITE, thread:" 30628f63c06894b9ca9252f43bc54a098c0a785d4b4Hung-ying Tyan + Thread.currentThread()); 30728f63c06894b9ca9252f43bc54a098c0a785d4b4Hung-ying Tyan // Acquire a wake lock and keep it for WAKE_LOCK_HOLDING_TIME; 30828f63c06894b9ca9252f43bc54a098c0a785d4b4Hung-ying Tyan // should be large enough to bring up the app. 30928f63c06894b9ca9252f43bc54a098c0a785d4b4Hung-ying Tyan mWakeLock.acquire(WAKE_LOCK_HOLDING_TIME); 31028f63c06894b9ca9252f43bc54a098c0a785d4b4Hung-ying Tyan } 3112d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang process(event); 3122d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 3132d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 3142d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang public void processResponse(ResponseEvent event) { 3152d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang process(event); 3162d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 3172d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 3182d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang public void processIOException(IOExceptionEvent event) { 3192d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang process(event); 3202d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 3212d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 3222d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang public void processTimeout(TimeoutEvent event) { 3232d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang process(event); 3242d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 3252d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 3262d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang public void processTransactionTerminated(TransactionTerminatedEvent event) { 3272d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang process(event); 3282d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 3292d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 3302d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang public void processDialogTerminated(DialogTerminatedEvent event) { 3312d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang process(event); 3322d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 3332d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 3342d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang private synchronized void process(EventObject event) { 3352d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang SipSessionImpl session = getSipSession(event); 3362d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang try { 337c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan boolean isLoggable = isLoggable(session, event); 338c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan boolean processed = (session != null) && session.process(event); 339c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan if (isLoggable && processed) { 34097963794af1e18674dd111e3ad344d90b16c922cHung-ying Tyan Log.d(TAG, "new state after: " 34184a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan + SipSession.State.toString(session.mState)); 3422d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 3432d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } catch (Throwable e) { 344903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan Log.w(TAG, "event process error: " + event, e); 3452d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang session.onError(e); 3462d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 3472d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 3482d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 34995b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh private String extractContent(Message message) { 35095b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh // Currently we do not support secure MIME bodies. 35195b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh byte[] bytes = message.getRawContent(); 35295b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh if (bytes != null) { 35395b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh try { 35495b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh if (message instanceof SIPMessage) { 35595b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh return ((SIPMessage) message).getMessageContent(); 35695b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh } else { 35795b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh return new String(bytes, "UTF-8"); 35895b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh } 35995b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh } catch (UnsupportedEncodingException e) { 36095b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh } 36195b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh } 36295b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh return null; 36395b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh } 36495b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh 3652d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang private class SipSessionCallReceiverImpl extends SipSessionImpl { 3662d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang public SipSessionCallReceiverImpl(ISipSessionListener listener) { 3672d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang super(listener); 3682d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 3692d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 3701aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync private SipSessionImpl createNewSession(RequestEvent event, 3711aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync ISipSessionListener listener, ServerTransaction transaction) 3721aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync throws SipException { 3731aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync SipSessionImpl newSession = new SipSessionImpl(listener); 3741aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync newSession.mServerTransaction = transaction; 3751aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync newSession.mState = SipSession.State.INCOMING_CALL; 3761aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync newSession.mDialog = newSession.mServerTransaction.getDialog(); 3771aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync newSession.mInviteReceived = event; 3781aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync newSession.mPeerProfile = createPeerProfile(event.getRequest()); 3791aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync newSession.mPeerSessionDescription = 3801aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync extractContent(event.getRequest()); 3811aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync return newSession; 3821aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync } 3831aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync 3841aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync private int processInviteWithReplaces(RequestEvent event, 3851aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync ReplacesHeader replaces) { 3861aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync String callId = replaces.getCallId(); 3871aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync SipSessionImpl session = mSessionMap.get(callId); 3881aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync if (session == null) { 3891aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync return Response.CALL_OR_TRANSACTION_DOES_NOT_EXIST; 3901aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync } 3911aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync 3921aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync Dialog dialog = session.mDialog; 3931aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync if (dialog == null) return Response.DECLINE; 3941aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync 3951aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync if (!dialog.getLocalTag().equals(replaces.getToTag()) || 3961aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync !dialog.getRemoteTag().equals(replaces.getFromTag())) { 3971aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync // No match is found, returns 481. 3981aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync return Response.CALL_OR_TRANSACTION_DOES_NOT_EXIST; 3991aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync } 4001aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync 4011aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync ReferredByHeader referredBy = (ReferredByHeader) event.getRequest() 4021aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync .getHeader(ReferredByHeader.NAME); 4031aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync if ((referredBy == null) || 4041aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync !dialog.getRemoteParty().equals(referredBy.getAddress())) { 4051aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync return Response.CALL_OR_TRANSACTION_DOES_NOT_EXIST; 4061aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync } 4071aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync return Response.OK; 4081aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync } 4091aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync 4101aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync private void processNewInviteRequest(RequestEvent event) 4111aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync throws SipException { 4121aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync ReplacesHeader replaces = (ReplacesHeader) event.getRequest() 4131aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync .getHeader(ReplacesHeader.NAME); 4141aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync SipSessionImpl newSession = null; 4151aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync if (replaces != null) { 4161aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync int response = processInviteWithReplaces(event, replaces); 4171aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync if (DEBUG) { 4181aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync Log.v(TAG, "ReplacesHeader: " + replaces 4191aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync + " response=" + response); 4201aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync } 4211aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync if (response == Response.OK) { 4221aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync SipSessionImpl replacedSession = 4231aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync mSessionMap.get(replaces.getCallId()); 4241aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync // got INVITE w/ replaces request. 4251aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync newSession = createNewSession(event, 4261aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync replacedSession.mProxy.getListener(), 4271aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync mSipHelper.getServerTransaction(event)); 4281aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync newSession.mProxy.onCallTransferring(newSession, 4291aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync newSession.mPeerSessionDescription); 4301aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync } else { 4311aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync mSipHelper.sendResponse(event, response); 4321aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync } 4331aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync } else { 4341aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync // New Incoming call. 4351aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync newSession = createNewSession(event, mProxy, 4361aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync mSipHelper.sendRinging(event, generateTag())); 4371aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync mProxy.onRinging(newSession, newSession.mPeerProfile, 4381aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync newSession.mPeerSessionDescription); 4391aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync } 4401aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync if (newSession != null) addSipSession(newSession); 4411aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync } 4421aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync 4432d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang public boolean process(EventObject evt) throws SipException { 444c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan if (isLoggable(this, evt)) Log.d(TAG, " ~~~~~ " + this + ": " 44584a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan + SipSession.State.toString(mState) + ": processing " 44697963794af1e18674dd111e3ad344d90b16c922cHung-ying Tyan + log(evt)); 4472d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang if (isRequestEvent(Request.INVITE, evt)) { 4481aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync processNewInviteRequest((RequestEvent) evt); 4492d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return true; 4500b4d2fb11405e2e785ec30cabe7bb311c654c0d2Chung-yih Wang } else if (isRequestEvent(Request.OPTIONS, evt)) { 4510b4d2fb11405e2e785ec30cabe7bb311c654c0d2Chung-yih Wang mSipHelper.sendResponse((RequestEvent) evt, Response.OK); 4520b4d2fb11405e2e785ec30cabe7bb311c654c0d2Chung-yih Wang return true; 4532d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } else { 4542d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return false; 4552d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 4562d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 4572d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 4582d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 4594a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan static interface KeepAliveProcessCallback { 4604a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan /** Invoked when the response of keeping alive comes back. */ 4614a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan void onResponse(boolean portChanged); 4624a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan void onError(int errorCode, String description); 4634a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } 4644a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan 4652d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang class SipSessionImpl extends ISipSession.Stub { 4662d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang SipProfile mPeerProfile; 4672d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang SipSessionListenerProxy mProxy = new SipSessionListenerProxy(); 46884a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan int mState = SipSession.State.READY_TO_CALL; 4692d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang RequestEvent mInviteReceived; 4702d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang Dialog mDialog; 4712d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang ServerTransaction mServerTransaction; 4722d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang ClientTransaction mClientTransaction; 47395b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh String mPeerSessionDescription; 4742d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang boolean mInCall; 4754a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan SessionTimer mSessionTimer; 476ee8a884f3504c981be8a1d6888b4590a0a394e05Hung-ying Tyan int mAuthenticationRetryCount; 477ee8a884f3504c981be8a1d6888b4590a0a394e05Hung-ying Tyan 4784a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan private KeepAliveProcess mKeepAliveProcess; 4799352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan 4809352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan // lightweight timer 4819352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan class SessionTimer { 4829352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan private boolean mRunning = true; 4839352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan 4849352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan void start(final int timeout) { 4859352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan new Thread(new Runnable() { 4869352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan public void run() { 4879352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan sleep(timeout); 4889352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan if (mRunning) timeout(); 4899352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan } 49084a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan }, "SipSessionTimerThread").start(); 4919352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan } 4929352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan 4939352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan synchronized void cancel() { 4949352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan mRunning = false; 4959352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan this.notify(); 4969352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan } 4979352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan 4989352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan private void timeout() { 4999352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan synchronized (SipSessionGroup.this) { 5009352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan onError(SipErrorCode.TIME_OUT, "Session timed out!"); 5019352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan } 5029352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan } 5039352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan 5049352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan private synchronized void sleep(int timeout) { 5059352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan try { 5069352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan this.wait(timeout * 1000); 5079352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan } catch (InterruptedException e) { 5089352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan Log.e(TAG, "session timer interrupted!"); 5099352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan } 5109352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan } 5119352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan } 5122d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 5132d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang public SipSessionImpl(ISipSessionListener listener) { 5142d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang setListener(listener); 5152d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 5162d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 5172d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang SipSessionImpl duplicate() { 5182d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return new SipSessionImpl(mProxy.getListener()); 5192d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 5202d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 5212d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang private void reset() { 5222d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mInCall = false; 5232d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang removeSipSession(this); 5242d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mPeerProfile = null; 52584a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan mState = SipSession.State.READY_TO_CALL; 5262d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mInviteReceived = null; 5279ea96c6cade1f25d4d77dcbd24854df431548b36Hung-ying Tyan mPeerSessionDescription = null; 528ee8a884f3504c981be8a1d6888b4590a0a394e05Hung-ying Tyan mAuthenticationRetryCount = 0; 5299ea96c6cade1f25d4d77dcbd24854df431548b36Hung-ying Tyan 5309ea96c6cade1f25d4d77dcbd24854df431548b36Hung-ying Tyan if (mDialog != null) mDialog.delete(); 5312d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mDialog = null; 5329ea96c6cade1f25d4d77dcbd24854df431548b36Hung-ying Tyan 5339ea96c6cade1f25d4d77dcbd24854df431548b36Hung-ying Tyan try { 5349ea96c6cade1f25d4d77dcbd24854df431548b36Hung-ying Tyan if (mServerTransaction != null) mServerTransaction.terminate(); 5359ea96c6cade1f25d4d77dcbd24854df431548b36Hung-ying Tyan } catch (ObjectInUseException e) { 5369ea96c6cade1f25d4d77dcbd24854df431548b36Hung-ying Tyan // ignored 5379ea96c6cade1f25d4d77dcbd24854df431548b36Hung-ying Tyan } 5382d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mServerTransaction = null; 5399ea96c6cade1f25d4d77dcbd24854df431548b36Hung-ying Tyan 5409ea96c6cade1f25d4d77dcbd24854df431548b36Hung-ying Tyan try { 5419ea96c6cade1f25d4d77dcbd24854df431548b36Hung-ying Tyan if (mClientTransaction != null) mClientTransaction.terminate(); 5429ea96c6cade1f25d4d77dcbd24854df431548b36Hung-ying Tyan } catch (ObjectInUseException e) { 5439ea96c6cade1f25d4d77dcbd24854df431548b36Hung-ying Tyan // ignored 5449ea96c6cade1f25d4d77dcbd24854df431548b36Hung-ying Tyan } 5452d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mClientTransaction = null; 5469352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan 5479352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan cancelSessionTimer(); 5482d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 5492d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 5502d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang public boolean isInCall() { 5512d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return mInCall; 5522d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 5532d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 5542d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang public String getLocalIp() { 5552d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return mLocalIp; 5562d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 5572d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 5582d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang public SipProfile getLocalProfile() { 5592d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return mLocalProfile; 5602d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 5612d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 5622d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang public SipProfile getPeerProfile() { 5632d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return mPeerProfile; 5642d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 5652d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 5662d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang public String getCallId() { 5672d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return SipHelper.getCallId(getTransaction()); 5682d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 5692d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 5702d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang private Transaction getTransaction() { 5712d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang if (mClientTransaction != null) return mClientTransaction; 5722d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang if (mServerTransaction != null) return mServerTransaction; 5732d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return null; 5742d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 5752d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 57697963794af1e18674dd111e3ad344d90b16c922cHung-ying Tyan public int getState() { 57797963794af1e18674dd111e3ad344d90b16c922cHung-ying Tyan return mState; 5782d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 5792d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 5802d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang public void setListener(ISipSessionListener listener) { 5812d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mProxy.setListener((listener instanceof SipSessionListenerProxy) 5822d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang ? ((SipSessionListenerProxy) listener).getListener() 5832d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang : listener); 5842d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 5852d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 586dba514c6d8e8263d4b8f31cb2fdebfc1d4f84c35Hung-ying Tyan // process the command in a new thread 587dba514c6d8e8263d4b8f31cb2fdebfc1d4f84c35Hung-ying Tyan private void doCommandAsync(final EventObject command) { 588dba514c6d8e8263d4b8f31cb2fdebfc1d4f84c35Hung-ying Tyan new Thread(new Runnable() { 589dba514c6d8e8263d4b8f31cb2fdebfc1d4f84c35Hung-ying Tyan public void run() { 590dba514c6d8e8263d4b8f31cb2fdebfc1d4f84c35Hung-ying Tyan try { 591dba514c6d8e8263d4b8f31cb2fdebfc1d4f84c35Hung-ying Tyan processCommand(command); 592c6548fd9eda7b58f5a2e2a9c01e3c7cafd42fafbHung-ying Tyan } catch (Throwable e) { 5934a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan Log.w(TAG, "command error: " + command + ": " 5944a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan + mLocalProfile.getUriString(), 5954a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan getRootCause(e)); 5963d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan onError(e); 597dba514c6d8e8263d4b8f31cb2fdebfc1d4f84c35Hung-ying Tyan } 598dba514c6d8e8263d4b8f31cb2fdebfc1d4f84c35Hung-ying Tyan } 59984a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan }, "SipSessionAsyncCmdThread").start(); 600dba514c6d8e8263d4b8f31cb2fdebfc1d4f84c35Hung-ying Tyan } 601dba514c6d8e8263d4b8f31cb2fdebfc1d4f84c35Hung-ying Tyan 6029352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan public void makeCall(SipProfile peerProfile, String sessionDescription, 6039352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan int timeout) { 6049352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan doCommandAsync(new MakeCallCommand(peerProfile, sessionDescription, 6059352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan timeout)); 6062d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 6072d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 6089352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan public void answerCall(String sessionDescription, int timeout) { 60906e8cdc0f81ead604d5adf9d7b3f982e10226fd2Hung-ying Tyan synchronized (SipSessionGroup.this) { 61006e8cdc0f81ead604d5adf9d7b3f982e10226fd2Hung-ying Tyan if (mPeerProfile == null) return; 611c133781723f64d1321685d02ad6a208286bf0a42repo sync doCommandAsync(new MakeCallCommand(mPeerProfile, 612c133781723f64d1321685d02ad6a208286bf0a42repo sync sessionDescription, timeout)); 6132d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 6142d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 6152d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 6162d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang public void endCall() { 617dba514c6d8e8263d4b8f31cb2fdebfc1d4f84c35Hung-ying Tyan doCommandAsync(END_CALL); 6182d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 6192d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 6209352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan public void changeCall(String sessionDescription, int timeout) { 62106e8cdc0f81ead604d5adf9d7b3f982e10226fd2Hung-ying Tyan synchronized (SipSessionGroup.this) { 62206e8cdc0f81ead604d5adf9d7b3f982e10226fd2Hung-ying Tyan if (mPeerProfile == null) return; 62306e8cdc0f81ead604d5adf9d7b3f982e10226fd2Hung-ying Tyan doCommandAsync(new MakeCallCommand(mPeerProfile, 62406e8cdc0f81ead604d5adf9d7b3f982e10226fd2Hung-ying Tyan sessionDescription, timeout)); 62506e8cdc0f81ead604d5adf9d7b3f982e10226fd2Hung-ying Tyan } 6262d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 6272d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 6282d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang public void register(int duration) { 629dba514c6d8e8263d4b8f31cb2fdebfc1d4f84c35Hung-ying Tyan doCommandAsync(new RegisterCommand(duration)); 6302d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 6312d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 6322d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang public void unregister() { 633dba514c6d8e8263d4b8f31cb2fdebfc1d4f84c35Hung-ying Tyan doCommandAsync(DEREGISTER); 6342d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 6352d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 6362d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang private void processCommand(EventObject command) throws SipException { 6370a6e717fb6846f66b8dc853e079f2166307bfc60Hung-ying Tyan if (isLoggable(command)) Log.d(TAG, "process cmd: " + command); 6382d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang if (!process(command)) { 6393d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan onError(SipErrorCode.IN_PROGRESS, 6403d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan "cannot initiate a new transaction to execute: " 6413d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan + command); 6422d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 6432d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 6442d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 6452d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang protected String generateTag() { 6462d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang // 32-bit randomness 6472d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return String.valueOf((long) (Math.random() * 0x100000000L)); 6482d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 6492d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 6502d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang public String toString() { 6512d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang try { 6522d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang String s = super.toString(); 65397963794af1e18674dd111e3ad344d90b16c922cHung-ying Tyan return s.substring(s.indexOf("@")) + ":" 65484a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan + SipSession.State.toString(mState); 6552d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } catch (Throwable e) { 6562d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return super.toString(); 6572d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 6582d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 6592d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 6602d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang public boolean process(EventObject evt) throws SipException { 661c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan if (isLoggable(this, evt)) Log.d(TAG, " ~~~~~ " + this + ": " 66284a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan + SipSession.State.toString(mState) + ": processing " 66397963794af1e18674dd111e3ad344d90b16c922cHung-ying Tyan + log(evt)); 6642d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang synchronized (SipSessionGroup.this) { 6652d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang if (isClosed()) return false; 6662d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 6674a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan if (mKeepAliveProcess != null) { 6684a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan // event consumed by keepalive process 6694a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan if (mKeepAliveProcess.process(evt)) return true; 6704a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } 6714a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan 6722d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang Dialog dialog = null; 6732d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang if (evt instanceof RequestEvent) { 6742d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang dialog = ((RequestEvent) evt).getDialog(); 6752d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } else if (evt instanceof ResponseEvent) { 6762d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang dialog = ((ResponseEvent) evt).getDialog(); 6772d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 6782d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang if (dialog != null) mDialog = dialog; 6792d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 6802d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang boolean processed; 6812d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 6822d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang switch (mState) { 68384a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan case SipSession.State.REGISTERING: 68484a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan case SipSession.State.DEREGISTERING: 6852d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang processed = registeringToReady(evt); 6862d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang break; 68784a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan case SipSession.State.READY_TO_CALL: 6882d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang processed = readyForCall(evt); 6892d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang break; 69084a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan case SipSession.State.INCOMING_CALL: 6912d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang processed = incomingCall(evt); 6922d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang break; 69384a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan case SipSession.State.INCOMING_CALL_ANSWERING: 6942d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang processed = incomingCallToInCall(evt); 6952d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang break; 69684a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan case SipSession.State.OUTGOING_CALL: 69784a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan case SipSession.State.OUTGOING_CALL_RING_BACK: 6982d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang processed = outgoingCall(evt); 6992d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang break; 70084a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan case SipSession.State.OUTGOING_CALL_CANCELING: 7012d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang processed = outgoingCallToReady(evt); 7022d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang break; 70384a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan case SipSession.State.IN_CALL: 7042d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang processed = inCall(evt); 7052d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang break; 7062d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang default: 7072d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang processed = false; 7082d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 7092d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return (processed || processExceptions(evt)); 7102d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 7112d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 7122d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 7132d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang private boolean processExceptions(EventObject evt) throws SipException { 7142d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang if (isRequestEvent(Request.BYE, evt)) { 7152d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang // terminate the call whenever a BYE is received 7162d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mSipHelper.sendResponse((RequestEvent) evt, Response.OK); 7172d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang endCallNormally(); 7182d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return true; 7192d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } else if (isRequestEvent(Request.CANCEL, evt)) { 7202d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mSipHelper.sendResponse((RequestEvent) evt, 7212d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang Response.CALL_OR_TRANSACTION_DOES_NOT_EXIST); 7222d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return true; 7232d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } else if (evt instanceof TransactionTerminatedEvent) { 724025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan if (isCurrentTransaction((TransactionTerminatedEvent) evt)) { 725025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan if (evt instanceof TimeoutEvent) { 726025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan processTimeout((TimeoutEvent) evt); 727025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan } else { 728025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan processTransactionTerminated( 729025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan (TransactionTerminatedEvent) evt); 730025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan } 731025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan return true; 7322d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 7330b4d2fb11405e2e785ec30cabe7bb311c654c0d2Chung-yih Wang } else if (isRequestEvent(Request.OPTIONS, evt)) { 7340b4d2fb11405e2e785ec30cabe7bb311c654c0d2Chung-yih Wang mSipHelper.sendResponse((RequestEvent) evt, Response.OK); 7350b4d2fb11405e2e785ec30cabe7bb311c654c0d2Chung-yih Wang return true; 7362d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } else if (evt instanceof DialogTerminatedEvent) { 7372d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang processDialogTerminated((DialogTerminatedEvent) evt); 7382d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return true; 7392d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 7402d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return false; 7412d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 7422d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 7432d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang private void processDialogTerminated(DialogTerminatedEvent event) { 7442d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang if (mDialog == event.getDialog()) { 7452d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang onError(new SipException("dialog terminated")); 7462d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } else { 7472d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang Log.d(TAG, "not the current dialog; current=" + mDialog 7482d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang + ", terminated=" + event.getDialog()); 7492d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 7502d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 7512d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 752025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan private boolean isCurrentTransaction(TransactionTerminatedEvent event) { 753025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan Transaction current = event.isServerTransaction() 754025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan ? mServerTransaction 755025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan : mClientTransaction; 756025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan Transaction target = event.isServerTransaction() 757025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan ? event.getServerTransaction() 758025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan : event.getClientTransaction(); 759025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan 760025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan if ((current != target) && (mState != SipSession.State.PINGING)) { 761025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan Log.d(TAG, "not the current transaction; current=" 762025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan + toString(current) + ", target=" + toString(target)); 763025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan return false; 764025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan } else if (current != null) { 765025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan Log.d(TAG, "transaction terminated: " + toString(current)); 766025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan return true; 767fccd5bc78f94b7dcfbcf78ddca83719c9cd1a74fHung-ying Tyan } else { 768fccd5bc78f94b7dcfbcf78ddca83719c9cd1a74fHung-ying Tyan // no transaction; shouldn't be here; ignored 769fccd5bc78f94b7dcfbcf78ddca83719c9cd1a74fHung-ying Tyan return true; 770025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan } 771025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan } 772025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan 773025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan private String toString(Transaction transaction) { 774025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan if (transaction == null) return "null"; 775025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan Request request = transaction.getRequest(); 776025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan Dialog dialog = transaction.getDialog(); 777025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan CSeqHeader cseq = (CSeqHeader) request.getHeader(CSeqHeader.NAME); 778025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan return String.format("req=%s,%s,s=%s,ds=%s,", request.getMethod(), 779025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan cseq.getSeqNumber(), transaction.getState(), 780025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan ((dialog == null) ? "-" : dialog.getState())); 781025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan } 782025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan 7833d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan private void processTransactionTerminated( 7843d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan TransactionTerminatedEvent event) { 7853d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan switch (mState) { 78684a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan case SipSession.State.IN_CALL: 78784a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan case SipSession.State.READY_TO_CALL: 7883d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan Log.d(TAG, "Transaction terminated; do nothing"); 7893d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan break; 7903d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan default: 7913d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan Log.d(TAG, "Transaction terminated early: " + this); 7923d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan onError(SipErrorCode.TRANSACTION_TERMINTED, 7933d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan "transaction terminated"); 7943d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan } 7953d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan } 7963d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan 7972d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang private void processTimeout(TimeoutEvent event) { 798025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan Log.d(TAG, "processing Timeout..."); 7992d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang switch (mState) { 80084a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan case SipSession.State.REGISTERING: 80184a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan case SipSession.State.DEREGISTERING: 8023d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan reset(); 8033d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan mProxy.onRegistrationTimeout(this); 8043d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan break; 80584a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan case SipSession.State.INCOMING_CALL: 80684a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan case SipSession.State.INCOMING_CALL_ANSWERING: 80784a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan case SipSession.State.OUTGOING_CALL: 80884a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan case SipSession.State.OUTGOING_CALL_CANCELING: 8093d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan onError(SipErrorCode.TIME_OUT, event.toString()); 8103d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan break; 8112d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 8123d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan default: 8133d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan Log.d(TAG, " do nothing"); 8143d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan break; 8152d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 8162d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 8172d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 8182d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang private int getExpiryTime(Response response) { 8192d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang int expires = EXPIRY_TIME; 8202d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang ExpiresHeader expiresHeader = (ExpiresHeader) 8212d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang response.getHeader(ExpiresHeader.NAME); 8222d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang if (expiresHeader != null) expires = expiresHeader.getExpires(); 8232d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang expiresHeader = (ExpiresHeader) 8242d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang response.getHeader(MinExpiresHeader.NAME); 8252d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang if (expiresHeader != null) { 8262d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang expires = Math.max(expires, expiresHeader.getExpires()); 8272d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 8282d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return expires; 8292d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 8302d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 8312d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang private boolean registeringToReady(EventObject evt) 8322d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang throws SipException { 8332d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang if (expectResponse(Request.REGISTER, evt)) { 8342d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang ResponseEvent event = (ResponseEvent) evt; 8352d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang Response response = event.getResponse(); 8362d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 8372d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang int statusCode = response.getStatusCode(); 8382d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang switch (statusCode) { 8392d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang case Response.OK: 84097963794af1e18674dd111e3ad344d90b16c922cHung-ying Tyan int state = mState; 84184a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan onRegistrationDone((state == SipSession.State.REGISTERING) 8422d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang ? getExpiryTime(((ResponseEvent) evt).getResponse()) 8432d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang : -1); 8442d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return true; 8452d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang case Response.UNAUTHORIZED: 8462d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang case Response.PROXY_AUTHENTICATION_REQUIRED: 847ee8a884f3504c981be8a1d6888b4590a0a394e05Hung-ying Tyan handleAuthentication(event); 8482d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return true; 8492d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang default: 8502d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang if (statusCode >= 500) { 8513d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan onRegistrationFailed(response); 8522d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return true; 8532d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 8542d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 8552d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 8562d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return false; 8572d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 8582d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 859903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan private boolean handleAuthentication(ResponseEvent event) 860903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan throws SipException { 861903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan Response response = event.getResponse(); 862903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan String nonce = getNonceFromResponse(response); 863ee8a884f3504c981be8a1d6888b4590a0a394e05Hung-ying Tyan if (nonce == null) { 864ee8a884f3504c981be8a1d6888b4590a0a394e05Hung-ying Tyan onError(SipErrorCode.SERVER_ERROR, 865ee8a884f3504c981be8a1d6888b4590a0a394e05Hung-ying Tyan "server does not provide challenge"); 866903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan return false; 867ee8a884f3504c981be8a1d6888b4590a0a394e05Hung-ying Tyan } else if (mAuthenticationRetryCount < 2) { 868903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan mClientTransaction = mSipHelper.handleChallenge( 869903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan event, getAccountManager()); 870903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan mDialog = mClientTransaction.getDialog(); 871ee8a884f3504c981be8a1d6888b4590a0a394e05Hung-ying Tyan mAuthenticationRetryCount++; 872ee8a884f3504c981be8a1d6888b4590a0a394e05Hung-ying Tyan if (isLoggable(this, event)) { 873ee8a884f3504c981be8a1d6888b4590a0a394e05Hung-ying Tyan Log.d(TAG, " authentication retry count=" 874ee8a884f3504c981be8a1d6888b4590a0a394e05Hung-ying Tyan + mAuthenticationRetryCount); 875ee8a884f3504c981be8a1d6888b4590a0a394e05Hung-ying Tyan } 876903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan return true; 877ee8a884f3504c981be8a1d6888b4590a0a394e05Hung-ying Tyan } else { 878a936b256eb1611b5d8b88d0cd61f21225152cc82Hung-ying Tyan if (crossDomainAuthenticationRequired(response)) { 879a936b256eb1611b5d8b88d0cd61f21225152cc82Hung-ying Tyan onError(SipErrorCode.CROSS_DOMAIN_AUTHENTICATION, 880a936b256eb1611b5d8b88d0cd61f21225152cc82Hung-ying Tyan getRealmFromResponse(response)); 881a936b256eb1611b5d8b88d0cd61f21225152cc82Hung-ying Tyan } else { 882a936b256eb1611b5d8b88d0cd61f21225152cc82Hung-ying Tyan onError(SipErrorCode.INVALID_CREDENTIALS, 883a936b256eb1611b5d8b88d0cd61f21225152cc82Hung-ying Tyan "incorrect username or password"); 884a936b256eb1611b5d8b88d0cd61f21225152cc82Hung-ying Tyan } 885ee8a884f3504c981be8a1d6888b4590a0a394e05Hung-ying Tyan return false; 886903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan } 887903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan } 888903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan 88900a22064efef4f574e439079aae2deae1a087a31Hung-ying Tyan private boolean crossDomainAuthenticationRequired(Response response) { 89000a22064efef4f574e439079aae2deae1a087a31Hung-ying Tyan String realm = getRealmFromResponse(response); 89100a22064efef4f574e439079aae2deae1a087a31Hung-ying Tyan if (realm == null) realm = ""; 89200a22064efef4f574e439079aae2deae1a087a31Hung-ying Tyan return !mLocalProfile.getSipDomain().trim().equals(realm.trim()); 89300a22064efef4f574e439079aae2deae1a087a31Hung-ying Tyan } 89400a22064efef4f574e439079aae2deae1a087a31Hung-ying Tyan 8952d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang private AccountManager getAccountManager() { 8962d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return new AccountManager() { 8972d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang public UserCredentials getCredentials(ClientTransaction 8982d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang challengedTransaction, String realm) { 8992d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return new UserCredentials() { 9002d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang public String getUserName() { 9010f7de88cb9eef781117fa2f2b69ba2698237637eChung-yih Wang String username = mLocalProfile.getAuthUserName(); 9020f7de88cb9eef781117fa2f2b69ba2698237637eChung-yih Wang return (!TextUtils.isEmpty(username) ? username : 9030f7de88cb9eef781117fa2f2b69ba2698237637eChung-yih Wang mLocalProfile.getUserName()); 9042d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 9052d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 9062d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang public String getPassword() { 9072d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return mPassword; 9082d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 9092d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 9102d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang public String getSipDomain() { 9112d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return mLocalProfile.getSipDomain(); 9122d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 9132d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang }; 9142d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 9152d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang }; 9162d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 9172d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 91800a22064efef4f574e439079aae2deae1a087a31Hung-ying Tyan private String getRealmFromResponse(Response response) { 91900a22064efef4f574e439079aae2deae1a087a31Hung-ying Tyan WWWAuthenticate wwwAuth = (WWWAuthenticate)response.getHeader( 92000a22064efef4f574e439079aae2deae1a087a31Hung-ying Tyan SIPHeaderNames.WWW_AUTHENTICATE); 92100a22064efef4f574e439079aae2deae1a087a31Hung-ying Tyan if (wwwAuth != null) return wwwAuth.getRealm(); 92200a22064efef4f574e439079aae2deae1a087a31Hung-ying Tyan ProxyAuthenticate proxyAuth = (ProxyAuthenticate)response.getHeader( 92300a22064efef4f574e439079aae2deae1a087a31Hung-ying Tyan SIPHeaderNames.PROXY_AUTHENTICATE); 92400a22064efef4f574e439079aae2deae1a087a31Hung-ying Tyan return (proxyAuth == null) ? null : proxyAuth.getRealm(); 92500a22064efef4f574e439079aae2deae1a087a31Hung-ying Tyan } 92600a22064efef4f574e439079aae2deae1a087a31Hung-ying Tyan 9272d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang private String getNonceFromResponse(Response response) { 9287d137e40cd36290c6bfb5beaf66f4018ae92c97fChung-yih Wang WWWAuthenticate wwwAuth = (WWWAuthenticate)response.getHeader( 9297d137e40cd36290c6bfb5beaf66f4018ae92c97fChung-yih Wang SIPHeaderNames.WWW_AUTHENTICATE); 9307d137e40cd36290c6bfb5beaf66f4018ae92c97fChung-yih Wang if (wwwAuth != null) return wwwAuth.getNonce(); 9317d137e40cd36290c6bfb5beaf66f4018ae92c97fChung-yih Wang ProxyAuthenticate proxyAuth = (ProxyAuthenticate)response.getHeader( 9327d137e40cd36290c6bfb5beaf66f4018ae92c97fChung-yih Wang SIPHeaderNames.PROXY_AUTHENTICATE); 9337d137e40cd36290c6bfb5beaf66f4018ae92c97fChung-yih Wang return (proxyAuth == null) ? null : proxyAuth.getNonce(); 9342d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 9352d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 9362d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang private boolean readyForCall(EventObject evt) throws SipException { 9372d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang // expect MakeCallCommand, RegisterCommand, DEREGISTER 9382d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang if (evt instanceof MakeCallCommand) { 939fb3a98b1d8d0ad040980d509c4c5341928b9460bHung-ying Tyan mState = SipSession.State.OUTGOING_CALL; 9402d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang MakeCallCommand cmd = (MakeCallCommand) evt; 9412d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mPeerProfile = cmd.getPeerProfile(); 9422d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mClientTransaction = mSipHelper.sendInvite(mLocalProfile, 94395b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh mPeerProfile, cmd.getSessionDescription(), 94495b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh generateTag()); 9452d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mDialog = mClientTransaction.getDialog(); 9462d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang addSipSession(this); 9479352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan startSessionTimer(cmd.getTimeout()); 9489ea96c6cade1f25d4d77dcbd24854df431548b36Hung-ying Tyan mProxy.onCalling(this); 9492d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return true; 9502d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } else if (evt instanceof RegisterCommand) { 951fb3a98b1d8d0ad040980d509c4c5341928b9460bHung-ying Tyan mState = SipSession.State.REGISTERING; 9522d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang int duration = ((RegisterCommand) evt).getDuration(); 9532d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mClientTransaction = mSipHelper.sendRegister(mLocalProfile, 9542d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang generateTag(), duration); 9552d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mDialog = mClientTransaction.getDialog(); 9562d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang addSipSession(this); 9572d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mProxy.onRegistering(this); 9582d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return true; 9592d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } else if (DEREGISTER == evt) { 960fb3a98b1d8d0ad040980d509c4c5341928b9460bHung-ying Tyan mState = SipSession.State.DEREGISTERING; 9612d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mClientTransaction = mSipHelper.sendRegister(mLocalProfile, 9622d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang generateTag(), 0); 9632d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mDialog = mClientTransaction.getDialog(); 9642d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang addSipSession(this); 9652d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mProxy.onRegistering(this); 9662d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return true; 9672d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 9682d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return false; 9692d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 9702d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 9712d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang private boolean incomingCall(EventObject evt) throws SipException { 9722d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang // expect MakeCallCommand(answering) , END_CALL cmd , Cancel 9732d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang if (evt instanceof MakeCallCommand) { 9742d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang // answer call 975fb3a98b1d8d0ad040980d509c4c5341928b9460bHung-ying Tyan mState = SipSession.State.INCOMING_CALL_ANSWERING; 9762d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mServerTransaction = mSipHelper.sendInviteOk(mInviteReceived, 9772d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mLocalProfile, 9782d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang ((MakeCallCommand) evt).getSessionDescription(), 9792d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mServerTransaction); 9809352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan startSessionTimer(((MakeCallCommand) evt).getTimeout()); 9812d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return true; 9822d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } else if (END_CALL == evt) { 9832d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mSipHelper.sendInviteBusyHere(mInviteReceived, 9842d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mServerTransaction); 9852d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang endCallNormally(); 9862d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return true; 9872d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } else if (isRequestEvent(Request.CANCEL, evt)) { 9882d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang RequestEvent event = (RequestEvent) evt; 9892d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mSipHelper.sendResponse(event, Response.OK); 9902d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mSipHelper.sendInviteRequestTerminated( 9912d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mInviteReceived.getRequest(), mServerTransaction); 9922d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang endCallNormally(); 9932d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return true; 9942d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 9952d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return false; 9962d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 9972d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 9982d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang private boolean incomingCallToInCall(EventObject evt) 9992d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang throws SipException { 10002d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang // expect ACK, CANCEL request 10012d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang if (isRequestEvent(Request.ACK, evt)) { 10022d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang establishCall(); 10032d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return true; 10042d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } else if (isRequestEvent(Request.CANCEL, evt)) { 10052d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang // http://tools.ietf.org/html/rfc3261#section-9.2 10062d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang // Final response has been sent; do nothing here. 10072d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return true; 10082d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 10092d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return false; 10102d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 10112d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 10122d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang private boolean outgoingCall(EventObject evt) throws SipException { 10132d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang if (expectResponse(Request.INVITE, evt)) { 10142d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang ResponseEvent event = (ResponseEvent) evt; 10152d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang Response response = event.getResponse(); 10162d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 10172d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang int statusCode = response.getStatusCode(); 10182d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang switch (statusCode) { 10192d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang case Response.RINGING: 10206057cd00d95c756b78f22c67279cb982bc0674efHung-ying Tyan case Response.CALL_IS_BEING_FORWARDED: 10216057cd00d95c756b78f22c67279cb982bc0674efHung-ying Tyan case Response.QUEUED: 10226057cd00d95c756b78f22c67279cb982bc0674efHung-ying Tyan case Response.SESSION_PROGRESS: 10236057cd00d95c756b78f22c67279cb982bc0674efHung-ying Tyan // feedback any provisional responses (except TRYING) as 10246057cd00d95c756b78f22c67279cb982bc0674efHung-ying Tyan // ring back for better UX 102584a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan if (mState == SipSession.State.OUTGOING_CALL) { 102684a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan mState = SipSession.State.OUTGOING_CALL_RING_BACK; 10279352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan cancelSessionTimer(); 10289ea96c6cade1f25d4d77dcbd24854df431548b36Hung-ying Tyan mProxy.onRingingBack(this); 10292d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 10302d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return true; 10312d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang case Response.OK: 10322d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mSipHelper.sendInviteAck(event, mDialog); 103395b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh mPeerSessionDescription = extractContent(response); 10342d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang establishCall(); 10352d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return true; 10360e0633828928481658c0e09e5893f6214b57ba38Chung-yih Wang case Response.UNAUTHORIZED: 10372d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang case Response.PROXY_AUTHENTICATION_REQUIRED: 1038a936b256eb1611b5d8b88d0cd61f21225152cc82Hung-ying Tyan if (handleAuthentication(event)) { 1039903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan addSipSession(this); 1040903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan } 10412d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return true; 10422d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang case Response.REQUEST_PENDING: 10432d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang // TODO: 10442d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang // rfc3261#section-14.1; re-schedule invite 10452d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return true; 10462d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang default: 10472d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang if (statusCode >= 400) { 10482d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang // error: an ack is sent automatically by the stack 1049903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan onError(response); 10502d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return true; 10512d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } else if (statusCode >= 300) { 10522d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang // TODO: handle 3xx (redirect) 10532d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } else { 10542d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return true; 10552d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 10562d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 10572d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return false; 10582d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } else if (END_CALL == evt) { 10592d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang // RFC says that UA should not send out cancel when no 10602d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang // response comes back yet. We are cheating for not checking 10612d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang // response. 106284a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan mState = SipSession.State.OUTGOING_CALL_CANCELING; 1063fb3a98b1d8d0ad040980d509c4c5341928b9460bHung-ying Tyan mSipHelper.sendCancel(mClientTransaction); 10649352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan startSessionTimer(CANCEL_CALL_TIMER); 10652d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return true; 10660a6e717fb6846f66b8dc853e079f2166307bfc60Hung-ying Tyan } else if (isRequestEvent(Request.INVITE, evt)) { 10670a6e717fb6846f66b8dc853e079f2166307bfc60Hung-ying Tyan // Call self? Send BUSY HERE so server may redirect the call to 10680a6e717fb6846f66b8dc853e079f2166307bfc60Hung-ying Tyan // voice mailbox. 10690a6e717fb6846f66b8dc853e079f2166307bfc60Hung-ying Tyan RequestEvent event = (RequestEvent) evt; 10700a6e717fb6846f66b8dc853e079f2166307bfc60Hung-ying Tyan mSipHelper.sendInviteBusyHere(event, 10710a6e717fb6846f66b8dc853e079f2166307bfc60Hung-ying Tyan event.getServerTransaction()); 10720a6e717fb6846f66b8dc853e079f2166307bfc60Hung-ying Tyan return true; 10732d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 10742d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return false; 10752d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 10762d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 10772d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang private boolean outgoingCallToReady(EventObject evt) 10782d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang throws SipException { 10792d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang if (evt instanceof ResponseEvent) { 10802d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang ResponseEvent event = (ResponseEvent) evt; 10812d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang Response response = event.getResponse(); 10822d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang int statusCode = response.getStatusCode(); 10832d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang if (expectResponse(Request.CANCEL, evt)) { 1084025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan if (statusCode == Response.OK) { 1085025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan // do nothing; wait for REQUEST_TERMINATED 1086025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan return true; 1087025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan } 1088025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan } else if (expectResponse(Request.INVITE, evt)) { 10899352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan switch (statusCode) { 10909352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan case Response.OK: 1091025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan outgoingCall(evt); // abort Cancel 10929352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan return true; 10939352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan case Response.REQUEST_TERMINATED: 10949352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan endCallNormally(); 10959352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan return true; 10962d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 10972d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } else { 10982d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return false; 10992d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 11002d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 11012d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang if (statusCode >= 400) { 11023d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan onError(response); 11032d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return true; 11042d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 11052d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } else if (evt instanceof TransactionTerminatedEvent) { 11062d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang // rfc3261#section-14.1: 11072d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang // if re-invite gets timed out, terminate the dialog; but 11082d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang // re-invite is not reliable, just let it go and pretend 11092d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang // nothing happened. 11102d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang onError(new SipException("timed out")); 11112d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 11122d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return false; 11132d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 11142d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 11152d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang private boolean inCall(EventObject evt) throws SipException { 11162d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang // expect END_CALL cmd, BYE request, hold call (MakeCallCommand) 11172d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang // OK retransmission is handled in SipStack 11182d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang if (END_CALL == evt) { 11192d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang // rfc3261#section-15.1.1 11202d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mSipHelper.sendBye(mDialog); 11212d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang endCallNormally(); 11222d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return true; 11232d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } else if (isRequestEvent(Request.INVITE, evt)) { 11242d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang // got Re-INVITE 112584a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan mState = SipSession.State.INCOMING_CALL; 1126fb3a98b1d8d0ad040980d509c4c5341928b9460bHung-ying Tyan RequestEvent event = mInviteReceived = (RequestEvent) evt; 112795b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh mPeerSessionDescription = extractContent(event.getRequest()); 11282d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mServerTransaction = null; 11292d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mProxy.onRinging(this, mPeerProfile, mPeerSessionDescription); 11302d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return true; 11312d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } else if (isRequestEvent(Request.BYE, evt)) { 11322d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mSipHelper.sendResponse((RequestEvent) evt, Response.OK); 11332d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang endCallNormally(); 11342d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return true; 11352d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } else if (evt instanceof MakeCallCommand) { 11362d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang // to change call 1137fb3a98b1d8d0ad040980d509c4c5341928b9460bHung-ying Tyan mState = SipSession.State.OUTGOING_CALL; 11382d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mClientTransaction = mSipHelper.sendReinvite(mDialog, 11392d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang ((MakeCallCommand) evt).getSessionDescription()); 11409352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan startSessionTimer(((MakeCallCommand) evt).getTimeout()); 11412d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return true; 11422d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 11432d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return false; 11442d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 11452d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 11469352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan // timeout in seconds 11479352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan private void startSessionTimer(int timeout) { 11489352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan if (timeout > 0) { 11494a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan mSessionTimer = new SessionTimer(); 11504a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan mSessionTimer.start(timeout); 11519352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan } 11529352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan } 11539352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan 11549352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan private void cancelSessionTimer() { 11554a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan if (mSessionTimer != null) { 11564a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan mSessionTimer.cancel(); 11574a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan mSessionTimer = null; 11589352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan } 11599352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan } 11609352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan 1161903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan private String createErrorMessage(Response response) { 1162624d5b4e8c20516516d0bff74479b9f5abdfe61cHung-ying Tyan return String.format("%s (%d)", response.getReasonPhrase(), 1163624d5b4e8c20516516d0bff74479b9f5abdfe61cHung-ying Tyan response.getStatusCode()); 1164903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan } 1165903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan 11662d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang private void establishCall() { 116784a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan mState = SipSession.State.IN_CALL; 11682d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mInCall = true; 11699352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan cancelSessionTimer(); 11702d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mProxy.onCallEstablished(this, mPeerSessionDescription); 11712d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 11722d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 11732d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang private void endCallNormally() { 11742d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang reset(); 11752d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mProxy.onCallEnded(this); 11762d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 11772d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 117897963794af1e18674dd111e3ad344d90b16c922cHung-ying Tyan private void endCallOnError(int errorCode, String message) { 11792d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang reset(); 118097963794af1e18674dd111e3ad344d90b16c922cHung-ying Tyan mProxy.onError(this, errorCode, message); 1181903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan } 1182903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan 1183903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan private void endCallOnBusy() { 1184903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan reset(); 1185903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan mProxy.onCallBusy(this); 11862d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 11872d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 118897963794af1e18674dd111e3ad344d90b16c922cHung-ying Tyan private void onError(int errorCode, String message) { 11899352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan cancelSessionTimer(); 11903d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan switch (mState) { 119184a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan case SipSession.State.REGISTERING: 119284a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan case SipSession.State.DEREGISTERING: 11933d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan onRegistrationFailed(errorCode, message); 11943d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan break; 11953d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan default: 11964189d99b6e4877352049b7447b7f0734ef99b9e8Hung-ying Tyan endCallOnError(errorCode, message); 11972d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 11982d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 11992d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 12003d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan 12013d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan private void onError(Throwable exception) { 12023d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan exception = getRootCause(exception); 12033d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan onError(getErrorCode(exception), exception.toString()); 12043d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan } 12053d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan 1206903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan private void onError(Response response) { 12073d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan int statusCode = response.getStatusCode(); 1208ae076d3981fda732d54b6c6e37e5659b2e7ba130Hung-ying Tyan if (!mInCall && (statusCode == Response.BUSY_HERE)) { 12093d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan endCallOnBusy(); 1210903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan } else { 12113d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan onError(getErrorCode(statusCode), createErrorMessage(response)); 1212903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan } 1213903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan } 1214903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan 121597963794af1e18674dd111e3ad344d90b16c922cHung-ying Tyan private int getErrorCode(int responseStatusCode) { 1216903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan switch (responseStatusCode) { 1217ae076d3981fda732d54b6c6e37e5659b2e7ba130Hung-ying Tyan case Response.TEMPORARILY_UNAVAILABLE: 1218ae076d3981fda732d54b6c6e37e5659b2e7ba130Hung-ying Tyan case Response.FORBIDDEN: 1219ae076d3981fda732d54b6c6e37e5659b2e7ba130Hung-ying Tyan case Response.GONE: 1220903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan case Response.NOT_FOUND: 1221ae076d3981fda732d54b6c6e37e5659b2e7ba130Hung-ying Tyan case Response.NOT_ACCEPTABLE: 1222ae076d3981fda732d54b6c6e37e5659b2e7ba130Hung-ying Tyan case Response.NOT_ACCEPTABLE_HERE: 1223ae076d3981fda732d54b6c6e37e5659b2e7ba130Hung-ying Tyan return SipErrorCode.PEER_NOT_REACHABLE; 1224ae076d3981fda732d54b6c6e37e5659b2e7ba130Hung-ying Tyan 1225ae076d3981fda732d54b6c6e37e5659b2e7ba130Hung-ying Tyan case Response.REQUEST_URI_TOO_LONG: 1226903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan case Response.ADDRESS_INCOMPLETE: 1227ae076d3981fda732d54b6c6e37e5659b2e7ba130Hung-ying Tyan case Response.AMBIGUOUS: 1228903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan return SipErrorCode.INVALID_REMOTE_URI; 1229ae076d3981fda732d54b6c6e37e5659b2e7ba130Hung-ying Tyan 1230903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan case Response.REQUEST_TIMEOUT: 1231903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan return SipErrorCode.TIME_OUT; 1232ae076d3981fda732d54b6c6e37e5659b2e7ba130Hung-ying Tyan 1233903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan default: 1234903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan if (responseStatusCode < 500) { 1235903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan return SipErrorCode.CLIENT_ERROR; 1236903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan } else { 1237903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan return SipErrorCode.SERVER_ERROR; 1238903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan } 1239903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan } 1240903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan } 1241903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan 1242903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan private Throwable getRootCause(Throwable exception) { 1243903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan Throwable cause = exception.getCause(); 1244903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan while (cause != null) { 1245903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan exception = cause; 1246903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan cause = exception.getCause(); 1247903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan } 1248903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan return exception; 1249903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan } 1250903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan 125197963794af1e18674dd111e3ad344d90b16c922cHung-ying Tyan private int getErrorCode(Throwable exception) { 1252903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan String message = exception.getMessage(); 1253903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan if (exception instanceof UnknownHostException) { 1254c6548fd9eda7b58f5a2e2a9c01e3c7cafd42fafbHung-ying Tyan return SipErrorCode.SERVER_UNREACHABLE; 1255903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan } else if (exception instanceof IOException) { 1256903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan return SipErrorCode.SOCKET_ERROR; 1257903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan } else { 1258903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan return SipErrorCode.CLIENT_ERROR; 1259903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan } 1260903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan } 1261903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan 12622d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang private void onRegistrationDone(int duration) { 12633d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan reset(); 12642d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mProxy.onRegistrationDone(this, duration); 12652d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 12662d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 126797963794af1e18674dd111e3ad344d90b16c922cHung-ying Tyan private void onRegistrationFailed(int errorCode, String message) { 12683d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan reset(); 126997963794af1e18674dd111e3ad344d90b16c922cHung-ying Tyan mProxy.onRegistrationFailed(this, errorCode, message); 1270903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan } 1271903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan 12722d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang private void onRegistrationFailed(Throwable exception) { 1273903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan exception = getRootCause(exception); 1274903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan onRegistrationFailed(getErrorCode(exception), 1275903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan exception.toString()); 12762d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 12773d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan 12783d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan private void onRegistrationFailed(Response response) { 12793d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan int statusCode = response.getStatusCode(); 12803d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan onRegistrationFailed(getErrorCode(statusCode), 12813d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan createErrorMessage(response)); 12823d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan } 12834a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan 12844a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan // Notes: SipSessionListener will be replaced by the keepalive process 12854a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan // @param interval in seconds 12864a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan public void startKeepAliveProcess(int interval, 12874a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan KeepAliveProcessCallback callback) throws SipException { 12884a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan synchronized (SipSessionGroup.this) { 12894a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan startKeepAliveProcess(interval, mLocalProfile, callback); 12904a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } 12914a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } 12924a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan 12934a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan // Notes: SipSessionListener will be replaced by the keepalive process 12944a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan // @param interval in seconds 12954a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan public void startKeepAliveProcess(int interval, SipProfile peerProfile, 12964a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan KeepAliveProcessCallback callback) throws SipException { 12974a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan synchronized (SipSessionGroup.this) { 12984a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan if (mKeepAliveProcess != null) { 12994a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan throw new SipException("Cannot create more than one " 13004a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan + "keepalive process in a SipSession"); 13014a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } 13024a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan mPeerProfile = peerProfile; 13034a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan mKeepAliveProcess = new KeepAliveProcess(); 13044a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan mProxy.setListener(mKeepAliveProcess); 13054a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan mKeepAliveProcess.start(interval, callback); 13064a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } 13074a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } 13084a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan 13094a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan public void stopKeepAliveProcess() { 13104a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan synchronized (SipSessionGroup.this) { 13114a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan if (mKeepAliveProcess != null) { 13124a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan mKeepAliveProcess.stop(); 13134a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan mKeepAliveProcess = null; 13144a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } 13154a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } 13164a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } 13174a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan 13184a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan class KeepAliveProcess extends SipSessionAdapter implements Runnable { 13194a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan private static final String TAG = "SipKeepAlive"; 13204a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan private boolean mRunning = false; 13214a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan private KeepAliveProcessCallback mCallback; 13224a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan 13234a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan private boolean mPortChanged = false; 13244a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan private int mRPort = 0; 1325e65f3a896f03bba5327ce4f3989c0422855450caHung-ying Tyan private int mInterval; // just for debugging 13264a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan 13274a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan // @param interval in seconds 13284a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan void start(int interval, KeepAliveProcessCallback callback) { 13294a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan if (mRunning) return; 13304a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan mRunning = true; 1331e65f3a896f03bba5327ce4f3989c0422855450caHung-ying Tyan mInterval = interval; 13324a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan mCallback = new KeepAliveProcessCallbackProxy(callback); 13334a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan mWakeupTimer.set(interval * 1000, this); 13344a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan if (DEBUG) { 13354a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan Log.d(TAG, "start keepalive:" 13364a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan + mLocalProfile.getUriString()); 13374a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } 13384a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan 13394a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan // No need to run the first time in a separate thread for now 13404a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan run(); 13414a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } 13424a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan 13434a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan // return true if the event is consumed 13444a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan boolean process(EventObject evt) throws SipException { 13454a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan if (mRunning && (mState == SipSession.State.PINGING)) { 13464a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan if (evt instanceof ResponseEvent) { 13474a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan if (parseOptionsResult(evt)) { 13484a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan if (mPortChanged) { 13494a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan stop(); 13504a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } else { 13514a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan cancelSessionTimer(); 13524a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan removeSipSession(SipSessionImpl.this); 13534a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } 13544a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan mCallback.onResponse(mPortChanged); 13554a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan return true; 13564a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } 13574a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } 13584a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } 13594a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan return false; 13604a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } 13614a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan 13624a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan // SipSessionAdapter 13634a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan // To react to the session timeout event and network error. 13644a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan @Override 13654a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan public void onError(ISipSession session, int errorCode, String message) { 13664a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan stop(); 13674a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan mCallback.onError(errorCode, message); 13684a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } 13694a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan 13704a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan // SipWakeupTimer timeout handler 13714a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan // To send out keepalive message. 13724a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan @Override 13734a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan public void run() { 13744a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan synchronized (SipSessionGroup.this) { 13754a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan if (!mRunning) return; 13764a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan 13774a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan if (DEBUG_PING) { 13784a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan Log.d(TAG, "keepalive: " + mLocalProfile.getUriString() 1379e65f3a896f03bba5327ce4f3989c0422855450caHung-ying Tyan + " --> " + mPeerProfile + ", interval=" + mInterval); 13804a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } 13814a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan try { 13824a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan sendKeepAlive(); 13834a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } catch (Throwable t) { 13844a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan Log.w(TAG, "keepalive error: " + ": " 13854a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan + mLocalProfile.getUriString(), getRootCause(t)); 13864a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan // It's possible that the keepalive process is being stopped 13874a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan // during session.sendKeepAlive() so need to check mRunning 13884a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan // again here. 13894a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan if (mRunning) SipSessionImpl.this.onError(t); 13904a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } 13914a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } 13924a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } 13934a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan 13944a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan void stop() { 13954a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan synchronized (SipSessionGroup.this) { 13964a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan if (DEBUG) { 13974a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan Log.d(TAG, "stop keepalive:" + mLocalProfile.getUriString() 13984a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan + ",RPort=" + mRPort); 13994a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } 14004a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan mRunning = false; 14014a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan mWakeupTimer.cancel(this); 14024a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan reset(); 14034a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } 14044a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } 14054a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan 14064a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan private void sendKeepAlive() throws SipException, InterruptedException { 14074a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan synchronized (SipSessionGroup.this) { 14084a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan mState = SipSession.State.PINGING; 14094a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan mClientTransaction = mSipHelper.sendOptions( 14104a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan mLocalProfile, mPeerProfile, generateTag()); 14114a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan mDialog = mClientTransaction.getDialog(); 14124a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan addSipSession(SipSessionImpl.this); 14134a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan 14144a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan startSessionTimer(KEEPALIVE_TIMEOUT); 14154a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan // when timed out, onError() will be called with SipErrorCode.TIME_OUT 14164a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } 14174a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } 14184a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan 14194a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan private boolean parseOptionsResult(EventObject evt) { 14204a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan if (expectResponse(Request.OPTIONS, evt)) { 14214a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan ResponseEvent event = (ResponseEvent) evt; 14224a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan int rPort = getRPortFromResponse(event.getResponse()); 14234a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan if (rPort != -1) { 14244a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan if (mRPort == 0) mRPort = rPort; 14254a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan if (mRPort != rPort) { 14264a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan mPortChanged = true; 14274a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan if (DEBUG) Log.d(TAG, String.format( 14284a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan "rport is changed: %d <> %d", mRPort, rPort)); 14294a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan mRPort = rPort; 14304a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } else { 14314a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan if (DEBUG) Log.d(TAG, "rport is the same: " + rPort); 14324a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } 14334a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } else { 14344a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan if (DEBUG) Log.w(TAG, "peer did not respond rport"); 14354a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } 14364a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan return true; 14374a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } 14384a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan return false; 14394a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } 14404a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan 14414a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan private int getRPortFromResponse(Response response) { 14424a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan ViaHeader viaHeader = (ViaHeader)(response.getHeader( 14434a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan SIPHeaderNames.VIA)); 14444a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan return (viaHeader == null) ? -1 : viaHeader.getRPort(); 14454a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } 14464a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } 14472d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 14482d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 14492d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang /** 14502d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang * @return true if the event is a request event matching the specified 14512d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang * method; false otherwise 14522d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang */ 14532d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang private static boolean isRequestEvent(String method, EventObject event) { 14542d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang try { 14552d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang if (event instanceof RequestEvent) { 14562d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang RequestEvent requestEvent = (RequestEvent) event; 14572d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return method.equals(requestEvent.getRequest().getMethod()); 14582d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 14592d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } catch (Throwable e) { 14602d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 14612d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return false; 14622d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 14632d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 14642d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang private static String getCseqMethod(Message message) { 14652d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return ((CSeqHeader) message.getHeader(CSeqHeader.NAME)).getMethod(); 14662d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 14672d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 14682d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang /** 14692d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang * @return true if the event is a response event and the CSeqHeader method 14702d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang * match the given arguments; false otherwise 14712d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang */ 14722d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang private static boolean expectResponse( 14732d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang String expectedMethod, EventObject evt) { 14742d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang if (evt instanceof ResponseEvent) { 14752d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang ResponseEvent event = (ResponseEvent) evt; 14762d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang Response response = event.getResponse(); 14772d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return expectedMethod.equalsIgnoreCase(getCseqMethod(response)); 14782d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 14792d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return false; 14802d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 14812d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 14822d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang /** 14832d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang * @return true if the event is a response event and the response code and 14842d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang * CSeqHeader method match the given arguments; false otherwise 14852d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang */ 14862d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang private static boolean expectResponse( 14872d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang int responseCode, String expectedMethod, EventObject evt) { 14882d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang if (evt instanceof ResponseEvent) { 14892d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang ResponseEvent event = (ResponseEvent) evt; 14902d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang Response response = event.getResponse(); 14912d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang if (response.getStatusCode() == responseCode) { 14922d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return expectedMethod.equalsIgnoreCase(getCseqMethod(response)); 14932d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 14942d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 14952d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return false; 14962d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 14972d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 14982d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang private static SipProfile createPeerProfile(Request request) 14992d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang throws SipException { 15002d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang try { 15012d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang FromHeader fromHeader = 15022d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang (FromHeader) request.getHeader(FromHeader.NAME); 15032d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang Address address = fromHeader.getAddress(); 15042d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang SipURI uri = (SipURI) address.getURI(); 15052d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang String username = uri.getUser(); 15062d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang if (username == null) username = ANONYMOUS; 150758ee2acba8953814cc4bf65d2f28f7dd498b5779Hung-ying Tyan int port = uri.getPort(); 150858ee2acba8953814cc4bf65d2f28f7dd498b5779Hung-ying Tyan SipProfile.Builder builder = 150958ee2acba8953814cc4bf65d2f28f7dd498b5779Hung-ying Tyan new SipProfile.Builder(username, uri.getHost()) 151058ee2acba8953814cc4bf65d2f28f7dd498b5779Hung-ying Tyan .setDisplayName(address.getDisplayName()); 151158ee2acba8953814cc4bf65d2f28f7dd498b5779Hung-ying Tyan if (port > 0) builder.setPort(port); 151258ee2acba8953814cc4bf65d2f28f7dd498b5779Hung-ying Tyan return builder.build(); 151399bf4e45c4566172189735b34b368b76660ca57aHung-ying Tyan } catch (IllegalArgumentException e) { 15142d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang throw new SipException("createPeerProfile()", e); 15152d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } catch (ParseException e) { 15162d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang throw new SipException("createPeerProfile()", e); 15172d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 15182d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 15192d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 1520c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan private static boolean isLoggable(SipSessionImpl s) { 1521c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan if (s != null) { 1522c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan switch (s.mState) { 152384a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan case SipSession.State.PINGING: 1524c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan return DEBUG_PING; 1525c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan } 1526c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan } 1527c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan return DEBUG; 1528c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan } 1529c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan 15300a6e717fb6846f66b8dc853e079f2166307bfc60Hung-ying Tyan private static boolean isLoggable(EventObject evt) { 15310a6e717fb6846f66b8dc853e079f2166307bfc60Hung-ying Tyan return isLoggable(null, evt); 15320a6e717fb6846f66b8dc853e079f2166307bfc60Hung-ying Tyan } 15330a6e717fb6846f66b8dc853e079f2166307bfc60Hung-ying Tyan 1534c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan private static boolean isLoggable(SipSessionImpl s, EventObject evt) { 1535c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan if (!isLoggable(s)) return false; 1536c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan if (evt == null) return false; 1537c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan 15384a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan if (evt instanceof ResponseEvent) { 1539c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan Response response = ((ResponseEvent) evt).getResponse(); 1540c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan if (Request.OPTIONS.equals(response.getHeader(CSeqHeader.NAME))) { 1541c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan return DEBUG_PING; 1542c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan } 1543c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan return DEBUG; 1544c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan } else if (evt instanceof RequestEvent) { 15454a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan if (isRequestEvent(Request.OPTIONS, evt)) { 15464a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan return DEBUG_PING; 15474a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } 1548c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan return DEBUG; 1549c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan } 1550c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan return false; 1551c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan } 1552c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan 15532d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang private static String log(EventObject evt) { 15542d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang if (evt instanceof RequestEvent) { 15552d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return ((RequestEvent) evt).getRequest().toString(); 15562d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } else if (evt instanceof ResponseEvent) { 15572d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return ((ResponseEvent) evt).getResponse().toString(); 15582d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } else { 15592d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return evt.toString(); 15602d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 15612d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 15622d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 15632d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang private class RegisterCommand extends EventObject { 15642d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang private int mDuration; 15652d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 15662d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang public RegisterCommand(int duration) { 15672d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang super(SipSessionGroup.this); 15682d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mDuration = duration; 15692d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 15702d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 15712d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang public int getDuration() { 15722d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return mDuration; 15732d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 15742d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 15752d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 15762d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang private class MakeCallCommand extends EventObject { 157795b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh private String mSessionDescription; 15789352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan private int mTimeout; // in seconds 15792d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 15802d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang public MakeCallCommand(SipProfile peerProfile, 158195b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh String sessionDescription) { 15829352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan this(peerProfile, sessionDescription, -1); 15839352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan } 15849352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan 15859352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan public MakeCallCommand(SipProfile peerProfile, 15869352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan String sessionDescription, int timeout) { 15872d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang super(peerProfile); 15882d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mSessionDescription = sessionDescription; 15899352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan mTimeout = timeout; 15902d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 15912d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 15922d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang public SipProfile getPeerProfile() { 15932d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return (SipProfile) getSource(); 15942d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 15952d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 159695b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh public String getSessionDescription() { 15972d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return mSessionDescription; 15982d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 15992d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 16009352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan public int getTimeout() { 16019352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan return mTimeout; 16029352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan } 16039352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan } 16044a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan 16054a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan /** Class to help safely run KeepAliveProcessCallback in a different thread. */ 16064a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan static class KeepAliveProcessCallbackProxy implements KeepAliveProcessCallback { 16074a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan private KeepAliveProcessCallback mCallback; 16084a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan 16094a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan KeepAliveProcessCallbackProxy(KeepAliveProcessCallback callback) { 16104a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan mCallback = callback; 16114a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } 16124a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan 16134a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan private void proxy(Runnable runnable) { 16144a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan // One thread for each calling back. 16154a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan // Note: Guarantee ordering if the issue becomes important. Currently, 16164a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan // the chance of handling two callback events at a time is none. 16174a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan new Thread(runnable, "SIP-KeepAliveProcessCallbackThread").start(); 16184a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } 16194a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan 16204a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan public void onResponse(final boolean portChanged) { 16214a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan if (mCallback == null) return; 16224a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan proxy(new Runnable() { 16234a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan public void run() { 16244a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan try { 16254a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan mCallback.onResponse(portChanged); 16264a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } catch (Throwable t) { 16274a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan Log.w(TAG, "onResponse", t); 16284a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } 16294a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } 16304a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan }); 16314a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } 16324a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan 16334a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan public void onError(final int errorCode, final String description) { 16344a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan if (mCallback == null) return; 16354a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan proxy(new Runnable() { 16364a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan public void run() { 16374a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan try { 16384a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan mCallback.onError(errorCode, description); 16394a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } catch (Throwable t) { 16404a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan Log.w(TAG, "onError", t); 16414a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } 16424a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } 16434a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan }); 16444a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } 16454a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } 16462d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang} 1647