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; 217d137e40cd36290c6bfb5beaf66f4018ae92c97fChung-yih Wangimport gov.nist.javax.sip.header.ProxyAuthenticate; 22307f15faafa5a38d9b3b314df22778cd11685d7brepo syncimport gov.nist.javax.sip.header.ReferTo; 23307f15faafa5a38d9b3b314df22778cd11685d7brepo syncimport gov.nist.javax.sip.header.SIPHeaderNames; 24307f15faafa5a38d9b3b314df22778cd11685d7brepo syncimport gov.nist.javax.sip.header.StatusLine; 252d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport gov.nist.javax.sip.header.WWWAuthenticate; 261aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo syncimport gov.nist.javax.sip.header.extensions.ReferredByHeader; 271aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo syncimport gov.nist.javax.sip.header.extensions.ReplacesHeader; 2895b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yehimport gov.nist.javax.sip.message.SIPMessage; 29307f15faafa5a38d9b3b314df22778cd11685d7brepo syncimport gov.nist.javax.sip.message.SIPResponse; 302d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 312d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport android.net.sip.ISipSession; 322d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport android.net.sip.ISipSessionListener; 33903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyanimport android.net.sip.SipErrorCode; 342d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport android.net.sip.SipProfile; 3584a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyanimport android.net.sip.SipSession; 364a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyanimport android.net.sip.SipSessionAdapter; 372d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport android.text.TextUtils; 382d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport android.util.Log; 392d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 402d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport java.io.IOException; 4195b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yehimport java.io.UnsupportedEncodingException; 422d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport java.net.DatagramSocket; 432dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yehimport java.net.InetAddress; 44903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyanimport java.net.UnknownHostException; 452d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport java.text.ParseException; 462d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport java.util.Collection; 472d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport java.util.EventObject; 482d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport java.util.HashMap; 492d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport java.util.Map; 502d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport java.util.Properties; 512d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 522d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.ClientTransaction; 532d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.Dialog; 542d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.DialogTerminatedEvent; 552d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.IOExceptionEvent; 562d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.ListeningPoint; 579ea96c6cade1f25d4d77dcbd24854df431548b36Hung-ying Tyanimport javax.sip.ObjectInUseException; 582d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.RequestEvent; 592d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.ResponseEvent; 602d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.ServerTransaction; 612d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.SipException; 622d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.SipFactory; 632d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.SipListener; 642d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.SipProvider; 652d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.SipStack; 662d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.TimeoutEvent; 672d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.Transaction; 682d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.TransactionState; 692d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.TransactionTerminatedEvent; 70903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyanimport javax.sip.TransactionUnavailableException; 712d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.address.Address; 722d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.address.SipURI; 732d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.header.CSeqHeader; 745f760064e1975a50e4abb63e560731c8b2c7b56cChia-chi Yehimport javax.sip.header.ContactHeader; 752d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.header.ExpiresHeader; 762d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.header.FromHeader; 77307f15faafa5a38d9b3b314df22778cd11685d7brepo syncimport javax.sip.header.HeaderAddress; 782d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.header.MinExpiresHeader; 79307f15faafa5a38d9b3b314df22778cd11685d7brepo syncimport javax.sip.header.ReferToHeader; 802d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.header.ViaHeader; 812d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.message.Message; 822d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.message.Request; 832d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangimport javax.sip.message.Response; 842d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 85307f15faafa5a38d9b3b314df22778cd11685d7brepo sync 862d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang/** 872d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang * Manages {@link ISipSession}'s for a SIP account. 882d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang */ 892d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wangclass SipSessionGroup implements SipListener { 902d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang private static final String TAG = "SipSession"; 91cb6ee06f62c20ae036a206667097f20b837b11abChia-chi Yeh private static final boolean DEBUG = false; 92cb6ee06f62c20ae036a206667097f20b837b11abChia-chi Yeh private static final boolean DEBUG_PING = false; 932d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang private static final String ANONYMOUS = "anonymous"; 9466cc5355a137e291cc1e3c5d871e1d9cd35ee0abChung-yih Wang // Limit the size of thread pool to 1 for the order issue when the phone is 9566cc5355a137e291cc1e3c5d871e1d9cd35ee0abChung-yih Wang // waken up from sleep and there are many packets to be processed in the SIP 9666cc5355a137e291cc1e3c5d871e1d9cd35ee0abChung-yih Wang // stack. Note: The default thread pool size in NIST SIP stack is -1 which is 9766cc5355a137e291cc1e3c5d871e1d9cd35ee0abChung-yih Wang // unlimited. 9866cc5355a137e291cc1e3c5d871e1d9cd35ee0abChung-yih Wang private static final String THREAD_POOL_SIZE = "1"; 999352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan private static final int EXPIRY_TIME = 3600; // in seconds 100194bbcce9ba15634500f542b9ea017b2cf154b45Hung-ying Tyan private static final int CANCEL_CALL_TIMER = 3; // in seconds 101dc5bbe965f7a66238c3f9a6694f4410b3d52af3bHung-ying Tyan private static final int END_CALL_TIMER = 3; // in seconds 102d17b6d526648c372be761097e55c19767d5dba7dChia-chi Yeh private static final int KEEPALIVE_TIMEOUT = 5; // in seconds 103233718c3c5a4f5b4f564af93cb2e42d80a900904Hung-ying Tyan private static final int INCALL_KEEPALIVE_INTERVAL = 10; // in seconds 10428f63c06894b9ca9252f43bc54a098c0a785d4b4Hung-ying Tyan private static final long WAKE_LOCK_HOLDING_TIME = 500; // in milliseconds 1052d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 1062d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang private static final EventObject DEREGISTER = new EventObject("Deregister"); 1072d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang private static final EventObject END_CALL = new EventObject("End call"); 1082d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang private static final EventObject HOLD_CALL = new EventObject("Hold call"); 1092d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang private static final EventObject CONTINUE_CALL 1102d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang = new EventObject("Continue call"); 1112d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 1122d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang private final SipProfile mLocalProfile; 1132d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang private final String mPassword; 1142d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 1152d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang private SipStack mSipStack; 1162d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang private SipHelper mSipHelper; 1172d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 1182d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang // session that processes INVITE requests 1192d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang private SipSessionImpl mCallReceiverSession; 1202d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang private String mLocalIp; 1212d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 1224a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan private SipWakeupTimer mWakeupTimer; 12328f63c06894b9ca9252f43bc54a098c0a785d4b4Hung-ying Tyan private SipWakeLock mWakeLock; 12428f63c06894b9ca9252f43bc54a098c0a785d4b4Hung-ying Tyan 1252d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang // call-id-to-SipSession map 1262d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang private Map<String, SipSessionImpl> mSessionMap = 1272d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang new HashMap<String, SipSessionImpl>(); 1282d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 12999705b52ec952012bedc4aa8e1f62caff80a6a2fHung-ying Tyan // external address observed from any response 13099705b52ec952012bedc4aa8e1f62caff80a6a2fHung-ying Tyan private String mExternalIp; 13199705b52ec952012bedc4aa8e1f62caff80a6a2fHung-ying Tyan private int mExternalPort; 13299705b52ec952012bedc4aa8e1f62caff80a6a2fHung-ying Tyan 1332d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang /** 1342dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh * @param profile the local profile with password crossed out 1352d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang * @param password the password of the profile 1362d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang * @throws IOException if cannot assign requested address 1372d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang */ 1382dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh public SipSessionGroup(SipProfile profile, String password, 1392dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh SipWakeupTimer timer, SipWakeLock wakeLock) throws SipException { 1402dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh mLocalProfile = profile; 1412d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mPassword = password; 1424a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan mWakeupTimer = timer; 14328f63c06894b9ca9252f43bc54a098c0a785d4b4Hung-ying Tyan mWakeLock = wakeLock; 1442dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh reset(); 1452d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 1462d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 1474a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan // TODO: remove this method once SipWakeupTimer can better handle variety 1484a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan // of timeout values 1494a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan void setWakeupTimer(SipWakeupTimer timer) { 1504a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan mWakeupTimer = timer; 1514a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } 1524a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan 1532dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh synchronized void reset() throws SipException { 1542d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang Properties properties = new Properties(); 1552dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh 1562dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh String protocol = mLocalProfile.getProtocol(); 1572dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh int port = mLocalProfile.getPort(); 1582dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh String server = mLocalProfile.getProxyAddress(); 1592dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh 1602dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh if (!TextUtils.isEmpty(server)) { 1612dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh properties.setProperty("javax.sip.OUTBOUND_PROXY", 1622dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh server + ':' + port + '/' + protocol); 1632dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh } else { 1642dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh server = mLocalProfile.getSipDomain(); 1652dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh } 1662dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh if (server.startsWith("[") && server.endsWith("]")) { 1672dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh server = server.substring(1, server.length() - 1); 1682dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh } 1692dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh 1702dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh String local = null; 1712dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh try { 1722dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh for (InetAddress remote : InetAddress.getAllByName(server)) { 1732dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh DatagramSocket socket = new DatagramSocket(); 1742dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh socket.connect(remote, port); 1752dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh if (socket.isConnected()) { 1762dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh local = socket.getLocalAddress().getHostAddress(); 1772dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh port = socket.getLocalPort(); 1782dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh socket.close(); 1792dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh break; 1802dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh } 1812dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh socket.close(); 1822dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh } 1832dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh } catch (Exception e) { 1842dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh // ignore. 1852dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh } 1862dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh if (local == null) { 1872dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh // We are unable to reach the server. Just bail out. 1882dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh return; 1892dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh } 1902dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh 1912dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh close(); 1922dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh mLocalIp = local; 1932dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh 1942d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang properties.setProperty("javax.sip.STACK_NAME", getStackName()); 195b4116c09fb9784551911ea0a10b4dd321ff9aa7dChung-yih Wang properties.setProperty( 196b4116c09fb9784551911ea0a10b4dd321ff9aa7dChung-yih Wang "gov.nist.javax.sip.THREAD_POOL_SIZE", THREAD_POOL_SIZE); 1972dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh mSipStack = SipFactory.getInstance().createSipStack(properties); 1982d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang try { 1992dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh SipProvider provider = mSipStack.createSipProvider( 2002dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh mSipStack.createListeningPoint(local, port, protocol)); 2012d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang provider.addSipListener(this); 2022dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh mSipHelper = new SipHelper(mSipStack, provider); 2032dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh } catch (SipException e) { 2042dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh throw e; 2052dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh } catch (Exception e) { 2062dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh throw new SipException("failed to initialize SIP stack", e); 2072d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 20899705b52ec952012bedc4aa8e1f62caff80a6a2fHung-ying Tyan 2092dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh Log.d(TAG, " start stack for " + mLocalProfile.getUriString()); 2102dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh mSipStack.start(); 2112d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 2122d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 213d231aa880ab006d51ffe03454c1fc082f1c97bb8Hung-ying Tyan synchronized void onConnectivityChanged() { 214ebc886c857a702d788c2bdfb9e0b3d20746ad745Hung-ying Tyan SipSessionImpl[] ss = mSessionMap.values().toArray( 215ebc886c857a702d788c2bdfb9e0b3d20746ad745Hung-ying Tyan new SipSessionImpl[mSessionMap.size()]); 216ebc886c857a702d788c2bdfb9e0b3d20746ad745Hung-ying Tyan // Iterate on the copied array instead of directly on mSessionMap to 217ebc886c857a702d788c2bdfb9e0b3d20746ad745Hung-ying Tyan // avoid ConcurrentModificationException being thrown when 218ebc886c857a702d788c2bdfb9e0b3d20746ad745Hung-ying Tyan // SipSessionImpl removes itself from mSessionMap in onError() in the 219ebc886c857a702d788c2bdfb9e0b3d20746ad745Hung-ying Tyan // following loop. 220ebc886c857a702d788c2bdfb9e0b3d20746ad745Hung-ying Tyan for (SipSessionImpl s : ss) { 221d231aa880ab006d51ffe03454c1fc082f1c97bb8Hung-ying Tyan s.onError(SipErrorCode.DATA_CONNECTION_LOST, 222d231aa880ab006d51ffe03454c1fc082f1c97bb8Hung-ying Tyan "data connection lost"); 223d231aa880ab006d51ffe03454c1fc082f1c97bb8Hung-ying Tyan } 224d231aa880ab006d51ffe03454c1fc082f1c97bb8Hung-ying Tyan } 225d231aa880ab006d51ffe03454c1fc082f1c97bb8Hung-ying Tyan 22699705b52ec952012bedc4aa8e1f62caff80a6a2fHung-ying Tyan synchronized void resetExternalAddress() { 227cb6ee06f62c20ae036a206667097f20b837b11abChia-chi Yeh if (DEBUG) { 228cb6ee06f62c20ae036a206667097f20b837b11abChia-chi Yeh Log.d(TAG, " reset external addr on " + mSipStack); 229cb6ee06f62c20ae036a206667097f20b837b11abChia-chi Yeh } 23099705b52ec952012bedc4aa8e1f62caff80a6a2fHung-ying Tyan mExternalIp = null; 23199705b52ec952012bedc4aa8e1f62caff80a6a2fHung-ying Tyan mExternalPort = 0; 23299705b52ec952012bedc4aa8e1f62caff80a6a2fHung-ying Tyan } 23399705b52ec952012bedc4aa8e1f62caff80a6a2fHung-ying Tyan 2342d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang public SipProfile getLocalProfile() { 2352d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return mLocalProfile; 2362d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 2372d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 2382d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang public String getLocalProfileUri() { 2392d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return mLocalProfile.getUriString(); 2402d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 2412d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 2422d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang private String getStackName() { 2432d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return "stack" + System.currentTimeMillis(); 2442d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 2452d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 2462d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang public synchronized void close() { 2472d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang Log.d(TAG, " close stack for " + mLocalProfile.getUriString()); 2485d0c5cf2d6c6e82bcdce95d72d9000a934b2f354Hung-ying Tyan onConnectivityChanged(); 2492d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mSessionMap.clear(); 2502d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang closeToNotReceiveCalls(); 2512d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang if (mSipStack != null) { 2522d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mSipStack.stop(); 2532d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mSipStack = null; 2542d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mSipHelper = null; 2552d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 2562dd9134c6b2da47f4816bc0f72a0e4924aca4f84Chia-chi Yeh resetExternalAddress(); 2572d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 2582d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 2592d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang public synchronized boolean isClosed() { 2602d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return (mSipStack == null); 2612d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 2622d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 2632d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang // For internal use, require listener not to block in callbacks. 2642d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang public synchronized void openToReceiveCalls(ISipSessionListener listener) { 2652d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang if (mCallReceiverSession == null) { 2662d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mCallReceiverSession = new SipSessionCallReceiverImpl(listener); 2672d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } else { 2682d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mCallReceiverSession.setListener(listener); 2692d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 2702d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 2712d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 2722d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang public synchronized void closeToNotReceiveCalls() { 2732d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mCallReceiverSession = null; 2742d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 2752d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 2762d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang public ISipSession createSession(ISipSessionListener listener) { 2772d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return (isClosed() ? null : new SipSessionImpl(listener)); 2782d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 2792d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 2800a6e717fb6846f66b8dc853e079f2166307bfc60Hung-ying Tyan synchronized boolean containsSession(String callId) { 2810a6e717fb6846f66b8dc853e079f2166307bfc60Hung-ying Tyan return mSessionMap.containsKey(callId); 2820a6e717fb6846f66b8dc853e079f2166307bfc60Hung-ying Tyan } 2830a6e717fb6846f66b8dc853e079f2166307bfc60Hung-ying Tyan 2842d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang private synchronized SipSessionImpl getSipSession(EventObject event) { 2852d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang String key = SipHelper.getCallId(event); 2862d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang SipSessionImpl session = mSessionMap.get(key); 287c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan if ((session != null) && isLoggable(session)) { 288c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan Log.d(TAG, "session key from event: " + key); 289c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan Log.d(TAG, "active sessions:"); 290c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan for (String k : mSessionMap.keySet()) { 291c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan Log.d(TAG, " ..." + k + ": " + mSessionMap.get(k)); 292c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan } 293c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan } 2942d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return ((session != null) ? session : mCallReceiverSession); 2952d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 2962d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 2972d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang private synchronized void addSipSession(SipSessionImpl newSession) { 2982d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang removeSipSession(newSession); 2992d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang String key = newSession.getCallId(); 3002d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mSessionMap.put(key, newSession); 301c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan if (isLoggable(newSession)) { 302c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan Log.d(TAG, "+++ add a session with key: '" + key + "'"); 303c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan for (String k : mSessionMap.keySet()) { 304c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan Log.d(TAG, " " + k + ": " + mSessionMap.get(k)); 305c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan } 3062d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 3072d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 3082d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 3092d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang private synchronized void removeSipSession(SipSessionImpl session) { 3102d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang if (session == mCallReceiverSession) return; 3112d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang String key = session.getCallId(); 3122d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang SipSessionImpl s = mSessionMap.remove(key); 3132d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang // sanity check 3142d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang if ((s != null) && (s != session)) { 3152d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang Log.w(TAG, "session " + session + " is not associated with key '" 3162d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang + key + "'"); 3172d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mSessionMap.put(key, s); 3182d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang for (Map.Entry<String, SipSessionImpl> entry 3192d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang : mSessionMap.entrySet()) { 3202d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang if (entry.getValue() == s) { 3212d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang key = entry.getKey(); 3222d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mSessionMap.remove(key); 3232d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 3242d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 3252d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 3262d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 327c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan if ((s != null) && isLoggable(s)) { 328c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan Log.d(TAG, "remove session " + session + " @key '" + key + "'"); 329c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan for (String k : mSessionMap.keySet()) { 330c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan Log.d(TAG, " " + k + ": " + mSessionMap.get(k)); 331c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan } 3322d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 3332d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 3342d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 33528f63c06894b9ca9252f43bc54a098c0a785d4b4Hung-ying Tyan public void processRequest(final RequestEvent event) { 33628f63c06894b9ca9252f43bc54a098c0a785d4b4Hung-ying Tyan if (isRequestEvent(Request.INVITE, event)) { 33728f63c06894b9ca9252f43bc54a098c0a785d4b4Hung-ying Tyan if (DEBUG) Log.d(TAG, "<<<<< got INVITE, thread:" 33828f63c06894b9ca9252f43bc54a098c0a785d4b4Hung-ying Tyan + Thread.currentThread()); 33928f63c06894b9ca9252f43bc54a098c0a785d4b4Hung-ying Tyan // Acquire a wake lock and keep it for WAKE_LOCK_HOLDING_TIME; 34028f63c06894b9ca9252f43bc54a098c0a785d4b4Hung-ying Tyan // should be large enough to bring up the app. 34128f63c06894b9ca9252f43bc54a098c0a785d4b4Hung-ying Tyan mWakeLock.acquire(WAKE_LOCK_HOLDING_TIME); 34228f63c06894b9ca9252f43bc54a098c0a785d4b4Hung-ying Tyan } 3432d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang process(event); 3442d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 3452d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 3462d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang public void processResponse(ResponseEvent event) { 3472d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang process(event); 3482d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 3492d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 3502d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang public void processIOException(IOExceptionEvent event) { 3512d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang process(event); 3522d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 3532d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 3542d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang public void processTimeout(TimeoutEvent event) { 3552d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang process(event); 3562d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 3572d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 3582d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang public void processTransactionTerminated(TransactionTerminatedEvent event) { 3592d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang process(event); 3602d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 3612d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 3622d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang public void processDialogTerminated(DialogTerminatedEvent event) { 3632d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang process(event); 3642d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 3652d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 3662d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang private synchronized void process(EventObject event) { 3672d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang SipSessionImpl session = getSipSession(event); 3682d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang try { 369c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan boolean isLoggable = isLoggable(session, event); 370c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan boolean processed = (session != null) && session.process(event); 371c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan if (isLoggable && processed) { 37297963794af1e18674dd111e3ad344d90b16c922cHung-ying Tyan Log.d(TAG, "new state after: " 37384a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan + SipSession.State.toString(session.mState)); 3742d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 3752d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } catch (Throwable e) { 376cb6ee06f62c20ae036a206667097f20b837b11abChia-chi Yeh Log.w(TAG, "event process error: " + event, getRootCause(e)); 3772d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang session.onError(e); 3782d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 3792d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 3802d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 38195b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh private String extractContent(Message message) { 38295b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh // Currently we do not support secure MIME bodies. 38395b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh byte[] bytes = message.getRawContent(); 38495b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh if (bytes != null) { 38595b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh try { 38695b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh if (message instanceof SIPMessage) { 38795b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh return ((SIPMessage) message).getMessageContent(); 38895b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh } else { 38995b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh return new String(bytes, "UTF-8"); 39095b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh } 39195b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh } catch (UnsupportedEncodingException e) { 39295b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh } 39395b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh } 39495b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh return null; 39595b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh } 39695b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh 39799705b52ec952012bedc4aa8e1f62caff80a6a2fHung-ying Tyan private void extractExternalAddress(ResponseEvent evt) { 39899705b52ec952012bedc4aa8e1f62caff80a6a2fHung-ying Tyan Response response = evt.getResponse(); 39999705b52ec952012bedc4aa8e1f62caff80a6a2fHung-ying Tyan ViaHeader viaHeader = (ViaHeader)(response.getHeader( 40099705b52ec952012bedc4aa8e1f62caff80a6a2fHung-ying Tyan SIPHeaderNames.VIA)); 40199705b52ec952012bedc4aa8e1f62caff80a6a2fHung-ying Tyan if (viaHeader == null) return; 40299705b52ec952012bedc4aa8e1f62caff80a6a2fHung-ying Tyan int rport = viaHeader.getRPort(); 40399705b52ec952012bedc4aa8e1f62caff80a6a2fHung-ying Tyan String externalIp = viaHeader.getReceived(); 40499705b52ec952012bedc4aa8e1f62caff80a6a2fHung-ying Tyan if ((rport > 0) && (externalIp != null)) { 40599705b52ec952012bedc4aa8e1f62caff80a6a2fHung-ying Tyan mExternalIp = externalIp; 40699705b52ec952012bedc4aa8e1f62caff80a6a2fHung-ying Tyan mExternalPort = rport; 407cb6ee06f62c20ae036a206667097f20b837b11abChia-chi Yeh if (DEBUG) { 408cb6ee06f62c20ae036a206667097f20b837b11abChia-chi Yeh Log.d(TAG, " got external addr " + externalIp + ":" + rport 409cb6ee06f62c20ae036a206667097f20b837b11abChia-chi Yeh + " on " + mSipStack); 410cb6ee06f62c20ae036a206667097f20b837b11abChia-chi Yeh } 411cb6ee06f62c20ae036a206667097f20b837b11abChia-chi Yeh } 412cb6ee06f62c20ae036a206667097f20b837b11abChia-chi Yeh } 413cb6ee06f62c20ae036a206667097f20b837b11abChia-chi Yeh 414cb6ee06f62c20ae036a206667097f20b837b11abChia-chi Yeh private Throwable getRootCause(Throwable exception) { 415cb6ee06f62c20ae036a206667097f20b837b11abChia-chi Yeh Throwable cause = exception.getCause(); 416cb6ee06f62c20ae036a206667097f20b837b11abChia-chi Yeh while (cause != null) { 417cb6ee06f62c20ae036a206667097f20b837b11abChia-chi Yeh exception = cause; 418cb6ee06f62c20ae036a206667097f20b837b11abChia-chi Yeh cause = exception.getCause(); 41999705b52ec952012bedc4aa8e1f62caff80a6a2fHung-ying Tyan } 420cb6ee06f62c20ae036a206667097f20b837b11abChia-chi Yeh return exception; 42199705b52ec952012bedc4aa8e1f62caff80a6a2fHung-ying Tyan } 42299705b52ec952012bedc4aa8e1f62caff80a6a2fHung-ying Tyan 423307f15faafa5a38d9b3b314df22778cd11685d7brepo sync private SipSessionImpl createNewSession(RequestEvent event, 424307f15faafa5a38d9b3b314df22778cd11685d7brepo sync ISipSessionListener listener, ServerTransaction transaction, 425307f15faafa5a38d9b3b314df22778cd11685d7brepo sync int newState) throws SipException { 426307f15faafa5a38d9b3b314df22778cd11685d7brepo sync SipSessionImpl newSession = new SipSessionImpl(listener); 427307f15faafa5a38d9b3b314df22778cd11685d7brepo sync newSession.mServerTransaction = transaction; 428307f15faafa5a38d9b3b314df22778cd11685d7brepo sync newSession.mState = newState; 429307f15faafa5a38d9b3b314df22778cd11685d7brepo sync newSession.mDialog = newSession.mServerTransaction.getDialog(); 430307f15faafa5a38d9b3b314df22778cd11685d7brepo sync newSession.mInviteReceived = event; 431307f15faafa5a38d9b3b314df22778cd11685d7brepo sync newSession.mPeerProfile = createPeerProfile((HeaderAddress) 432307f15faafa5a38d9b3b314df22778cd11685d7brepo sync event.getRequest().getHeader(FromHeader.NAME)); 433307f15faafa5a38d9b3b314df22778cd11685d7brepo sync newSession.mPeerSessionDescription = 434307f15faafa5a38d9b3b314df22778cd11685d7brepo sync extractContent(event.getRequest()); 435307f15faafa5a38d9b3b314df22778cd11685d7brepo sync return newSession; 436307f15faafa5a38d9b3b314df22778cd11685d7brepo sync } 437307f15faafa5a38d9b3b314df22778cd11685d7brepo sync 4382d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang private class SipSessionCallReceiverImpl extends SipSessionImpl { 4392d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang public SipSessionCallReceiverImpl(ISipSessionListener listener) { 4402d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang super(listener); 4412d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 4422d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 4431aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync private int processInviteWithReplaces(RequestEvent event, 4441aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync ReplacesHeader replaces) { 4451aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync String callId = replaces.getCallId(); 4461aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync SipSessionImpl session = mSessionMap.get(callId); 4471aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync if (session == null) { 4481aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync return Response.CALL_OR_TRANSACTION_DOES_NOT_EXIST; 4491aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync } 4501aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync 4511aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync Dialog dialog = session.mDialog; 4521aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync if (dialog == null) return Response.DECLINE; 4531aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync 4541aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync if (!dialog.getLocalTag().equals(replaces.getToTag()) || 4551aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync !dialog.getRemoteTag().equals(replaces.getFromTag())) { 4561aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync // No match is found, returns 481. 4571aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync return Response.CALL_OR_TRANSACTION_DOES_NOT_EXIST; 4581aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync } 4591aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync 4601aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync ReferredByHeader referredBy = (ReferredByHeader) event.getRequest() 4611aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync .getHeader(ReferredByHeader.NAME); 4621aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync if ((referredBy == null) || 4631aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync !dialog.getRemoteParty().equals(referredBy.getAddress())) { 4641aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync return Response.CALL_OR_TRANSACTION_DOES_NOT_EXIST; 4651aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync } 4661aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync return Response.OK; 4671aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync } 4681aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync 4691aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync private void processNewInviteRequest(RequestEvent event) 4701aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync throws SipException { 4711aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync ReplacesHeader replaces = (ReplacesHeader) event.getRequest() 4721aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync .getHeader(ReplacesHeader.NAME); 4731aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync SipSessionImpl newSession = null; 4741aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync if (replaces != null) { 4751aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync int response = processInviteWithReplaces(event, replaces); 4761aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync if (DEBUG) { 4771aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync Log.v(TAG, "ReplacesHeader: " + replaces 4781aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync + " response=" + response); 4791aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync } 4801aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync if (response == Response.OK) { 4811aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync SipSessionImpl replacedSession = 4821aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync mSessionMap.get(replaces.getCallId()); 4831aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync // got INVITE w/ replaces request. 4841aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync newSession = createNewSession(event, 4851aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync replacedSession.mProxy.getListener(), 486307f15faafa5a38d9b3b314df22778cd11685d7brepo sync mSipHelper.getServerTransaction(event), 487307f15faafa5a38d9b3b314df22778cd11685d7brepo sync SipSession.State.INCOMING_CALL); 4881aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync newSession.mProxy.onCallTransferring(newSession, 4891aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync newSession.mPeerSessionDescription); 4901aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync } else { 4911aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync mSipHelper.sendResponse(event, response); 4921aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync } 4931aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync } else { 4941aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync // New Incoming call. 4951aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync newSession = createNewSession(event, mProxy, 496307f15faafa5a38d9b3b314df22778cd11685d7brepo sync mSipHelper.sendRinging(event, generateTag()), 497307f15faafa5a38d9b3b314df22778cd11685d7brepo sync SipSession.State.INCOMING_CALL); 4981aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync mProxy.onRinging(newSession, newSession.mPeerProfile, 4991aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync newSession.mPeerSessionDescription); 5001aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync } 5011aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync if (newSession != null) addSipSession(newSession); 5021aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync } 5031aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync 5042d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang public boolean process(EventObject evt) throws SipException { 505c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan if (isLoggable(this, evt)) Log.d(TAG, " ~~~~~ " + this + ": " 50684a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan + SipSession.State.toString(mState) + ": processing " 50797963794af1e18674dd111e3ad344d90b16c922cHung-ying Tyan + log(evt)); 5082d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang if (isRequestEvent(Request.INVITE, evt)) { 5091aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync processNewInviteRequest((RequestEvent) evt); 5102d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return true; 5110b4d2fb11405e2e785ec30cabe7bb311c654c0d2Chung-yih Wang } else if (isRequestEvent(Request.OPTIONS, evt)) { 5120b4d2fb11405e2e785ec30cabe7bb311c654c0d2Chung-yih Wang mSipHelper.sendResponse((RequestEvent) evt, Response.OK); 5130b4d2fb11405e2e785ec30cabe7bb311c654c0d2Chung-yih Wang return true; 5142d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } else { 5152d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return false; 5162d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 5172d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 5182d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 5192d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 5204a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan static interface KeepAliveProcessCallback { 5214a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan /** Invoked when the response of keeping alive comes back. */ 5224a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan void onResponse(boolean portChanged); 5234a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan void onError(int errorCode, String description); 5244a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } 5254a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan 5262d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang class SipSessionImpl extends ISipSession.Stub { 5272d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang SipProfile mPeerProfile; 5282d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang SipSessionListenerProxy mProxy = new SipSessionListenerProxy(); 52984a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan int mState = SipSession.State.READY_TO_CALL; 5302d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang RequestEvent mInviteReceived; 5312d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang Dialog mDialog; 5322d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang ServerTransaction mServerTransaction; 5332d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang ClientTransaction mClientTransaction; 53495b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh String mPeerSessionDescription; 5352d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang boolean mInCall; 5364a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan SessionTimer mSessionTimer; 537ee8a884f3504c981be8a1d6888b4590a0a394e05Hung-ying Tyan int mAuthenticationRetryCount; 538ee8a884f3504c981be8a1d6888b4590a0a394e05Hung-ying Tyan 5394a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan private KeepAliveProcess mKeepAliveProcess; 5409352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan 541233718c3c5a4f5b4f564af93cb2e42d80a900904Hung-ying Tyan private SipSessionImpl mKeepAliveSession; 542233718c3c5a4f5b4f564af93cb2e42d80a900904Hung-ying Tyan 543307f15faafa5a38d9b3b314df22778cd11685d7brepo sync // the following three members are used for handling refer request. 544307f15faafa5a38d9b3b314df22778cd11685d7brepo sync SipSessionImpl mReferSession; 545307f15faafa5a38d9b3b314df22778cd11685d7brepo sync ReferredByHeader mReferredBy; 546307f15faafa5a38d9b3b314df22778cd11685d7brepo sync String mReplaces; 547307f15faafa5a38d9b3b314df22778cd11685d7brepo sync 5489352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan // lightweight timer 5499352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan class SessionTimer { 5509352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan private boolean mRunning = true; 5519352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan 5529352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan void start(final int timeout) { 5539352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan new Thread(new Runnable() { 5549352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan public void run() { 5559352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan sleep(timeout); 5569352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan if (mRunning) timeout(); 5579352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan } 55884a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan }, "SipSessionTimerThread").start(); 5599352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan } 5609352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan 5619352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan synchronized void cancel() { 5629352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan mRunning = false; 5639352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan this.notify(); 5649352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan } 5659352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan 5669352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan private void timeout() { 5679352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan synchronized (SipSessionGroup.this) { 5689352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan onError(SipErrorCode.TIME_OUT, "Session timed out!"); 5699352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan } 5709352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan } 5719352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan 5729352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan private synchronized void sleep(int timeout) { 5739352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan try { 5749352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan this.wait(timeout * 1000); 5759352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan } catch (InterruptedException e) { 5769352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan Log.e(TAG, "session timer interrupted!"); 5779352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan } 5789352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan } 5799352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan } 5802d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 5812d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang public SipSessionImpl(ISipSessionListener listener) { 5822d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang setListener(listener); 5832d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 5842d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 5852d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang SipSessionImpl duplicate() { 5862d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return new SipSessionImpl(mProxy.getListener()); 5872d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 5882d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 5892d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang private void reset() { 5902d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mInCall = false; 5912d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang removeSipSession(this); 5922d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mPeerProfile = null; 59384a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan mState = SipSession.State.READY_TO_CALL; 5942d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mInviteReceived = null; 5959ea96c6cade1f25d4d77dcbd24854df431548b36Hung-ying Tyan mPeerSessionDescription = null; 596ee8a884f3504c981be8a1d6888b4590a0a394e05Hung-ying Tyan mAuthenticationRetryCount = 0; 597307f15faafa5a38d9b3b314df22778cd11685d7brepo sync mReferSession = null; 598307f15faafa5a38d9b3b314df22778cd11685d7brepo sync mReferredBy = null; 599307f15faafa5a38d9b3b314df22778cd11685d7brepo sync mReplaces = null; 6009ea96c6cade1f25d4d77dcbd24854df431548b36Hung-ying Tyan 6019ea96c6cade1f25d4d77dcbd24854df431548b36Hung-ying Tyan if (mDialog != null) mDialog.delete(); 6022d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mDialog = null; 6039ea96c6cade1f25d4d77dcbd24854df431548b36Hung-ying Tyan 6049ea96c6cade1f25d4d77dcbd24854df431548b36Hung-ying Tyan try { 6059ea96c6cade1f25d4d77dcbd24854df431548b36Hung-ying Tyan if (mServerTransaction != null) mServerTransaction.terminate(); 6069ea96c6cade1f25d4d77dcbd24854df431548b36Hung-ying Tyan } catch (ObjectInUseException e) { 6079ea96c6cade1f25d4d77dcbd24854df431548b36Hung-ying Tyan // ignored 6089ea96c6cade1f25d4d77dcbd24854df431548b36Hung-ying Tyan } 6092d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mServerTransaction = null; 6109ea96c6cade1f25d4d77dcbd24854df431548b36Hung-ying Tyan 6119ea96c6cade1f25d4d77dcbd24854df431548b36Hung-ying Tyan try { 6129ea96c6cade1f25d4d77dcbd24854df431548b36Hung-ying Tyan if (mClientTransaction != null) mClientTransaction.terminate(); 6139ea96c6cade1f25d4d77dcbd24854df431548b36Hung-ying Tyan } catch (ObjectInUseException e) { 6149ea96c6cade1f25d4d77dcbd24854df431548b36Hung-ying Tyan // ignored 6159ea96c6cade1f25d4d77dcbd24854df431548b36Hung-ying Tyan } 6162d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mClientTransaction = null; 6179352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan 6189352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan cancelSessionTimer(); 619233718c3c5a4f5b4f564af93cb2e42d80a900904Hung-ying Tyan 620233718c3c5a4f5b4f564af93cb2e42d80a900904Hung-ying Tyan if (mKeepAliveSession != null) { 621233718c3c5a4f5b4f564af93cb2e42d80a900904Hung-ying Tyan mKeepAliveSession.stopKeepAliveProcess(); 622233718c3c5a4f5b4f564af93cb2e42d80a900904Hung-ying Tyan mKeepAliveSession = null; 623233718c3c5a4f5b4f564af93cb2e42d80a900904Hung-ying Tyan } 6242d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 6252d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 6262d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang public boolean isInCall() { 6272d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return mInCall; 6282d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 6292d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 6302d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang public String getLocalIp() { 6312d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return mLocalIp; 6322d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 6332d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 6342d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang public SipProfile getLocalProfile() { 6352d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return mLocalProfile; 6362d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 6372d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 6382d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang public SipProfile getPeerProfile() { 6392d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return mPeerProfile; 6402d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 6412d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 6422d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang public String getCallId() { 6432d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return SipHelper.getCallId(getTransaction()); 6442d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 6452d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 6462d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang private Transaction getTransaction() { 6472d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang if (mClientTransaction != null) return mClientTransaction; 6482d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang if (mServerTransaction != null) return mServerTransaction; 6492d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return null; 6502d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 6512d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 65297963794af1e18674dd111e3ad344d90b16c922cHung-ying Tyan public int getState() { 65397963794af1e18674dd111e3ad344d90b16c922cHung-ying Tyan return mState; 6542d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 6552d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 6562d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang public void setListener(ISipSessionListener listener) { 6572d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mProxy.setListener((listener instanceof SipSessionListenerProxy) 6582d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang ? ((SipSessionListenerProxy) listener).getListener() 6592d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang : listener); 6602d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 6612d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 662dba514c6d8e8263d4b8f31cb2fdebfc1d4f84c35Hung-ying Tyan // process the command in a new thread 663dba514c6d8e8263d4b8f31cb2fdebfc1d4f84c35Hung-ying Tyan private void doCommandAsync(final EventObject command) { 664dba514c6d8e8263d4b8f31cb2fdebfc1d4f84c35Hung-ying Tyan new Thread(new Runnable() { 665dba514c6d8e8263d4b8f31cb2fdebfc1d4f84c35Hung-ying Tyan public void run() { 666dba514c6d8e8263d4b8f31cb2fdebfc1d4f84c35Hung-ying Tyan try { 667dba514c6d8e8263d4b8f31cb2fdebfc1d4f84c35Hung-ying Tyan processCommand(command); 668c6548fd9eda7b58f5a2e2a9c01e3c7cafd42fafbHung-ying Tyan } catch (Throwable e) { 6694a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan Log.w(TAG, "command error: " + command + ": " 6704a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan + mLocalProfile.getUriString(), 6714a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan getRootCause(e)); 6723d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan onError(e); 673dba514c6d8e8263d4b8f31cb2fdebfc1d4f84c35Hung-ying Tyan } 674dba514c6d8e8263d4b8f31cb2fdebfc1d4f84c35Hung-ying Tyan } 67584a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan }, "SipSessionAsyncCmdThread").start(); 676dba514c6d8e8263d4b8f31cb2fdebfc1d4f84c35Hung-ying Tyan } 677dba514c6d8e8263d4b8f31cb2fdebfc1d4f84c35Hung-ying Tyan 6789352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan public void makeCall(SipProfile peerProfile, String sessionDescription, 6799352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan int timeout) { 6809352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan doCommandAsync(new MakeCallCommand(peerProfile, sessionDescription, 6819352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan timeout)); 6822d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 6832d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 6849352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan public void answerCall(String sessionDescription, int timeout) { 68506e8cdc0f81ead604d5adf9d7b3f982e10226fd2Hung-ying Tyan synchronized (SipSessionGroup.this) { 68606e8cdc0f81ead604d5adf9d7b3f982e10226fd2Hung-ying Tyan if (mPeerProfile == null) return; 687c133781723f64d1321685d02ad6a208286bf0a42repo sync doCommandAsync(new MakeCallCommand(mPeerProfile, 688c133781723f64d1321685d02ad6a208286bf0a42repo sync sessionDescription, timeout)); 6892d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 6902d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 6912d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 6922d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang public void endCall() { 693dba514c6d8e8263d4b8f31cb2fdebfc1d4f84c35Hung-ying Tyan doCommandAsync(END_CALL); 6942d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 6952d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 6969352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan public void changeCall(String sessionDescription, int timeout) { 69706e8cdc0f81ead604d5adf9d7b3f982e10226fd2Hung-ying Tyan synchronized (SipSessionGroup.this) { 69806e8cdc0f81ead604d5adf9d7b3f982e10226fd2Hung-ying Tyan if (mPeerProfile == null) return; 69906e8cdc0f81ead604d5adf9d7b3f982e10226fd2Hung-ying Tyan doCommandAsync(new MakeCallCommand(mPeerProfile, 70006e8cdc0f81ead604d5adf9d7b3f982e10226fd2Hung-ying Tyan sessionDescription, timeout)); 70106e8cdc0f81ead604d5adf9d7b3f982e10226fd2Hung-ying Tyan } 7022d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 7032d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 7042d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang public void register(int duration) { 705dba514c6d8e8263d4b8f31cb2fdebfc1d4f84c35Hung-ying Tyan doCommandAsync(new RegisterCommand(duration)); 7062d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 7072d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 7082d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang public void unregister() { 709dba514c6d8e8263d4b8f31cb2fdebfc1d4f84c35Hung-ying Tyan doCommandAsync(DEREGISTER); 7102d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 7112d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 7122d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang private void processCommand(EventObject command) throws SipException { 7130a6e717fb6846f66b8dc853e079f2166307bfc60Hung-ying Tyan if (isLoggable(command)) Log.d(TAG, "process cmd: " + command); 7142d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang if (!process(command)) { 7153d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan onError(SipErrorCode.IN_PROGRESS, 7163d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan "cannot initiate a new transaction to execute: " 7173d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan + command); 7182d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 7192d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 7202d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 7212d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang protected String generateTag() { 7222d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang // 32-bit randomness 7232d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return String.valueOf((long) (Math.random() * 0x100000000L)); 7242d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 7252d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 7262d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang public String toString() { 7272d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang try { 7282d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang String s = super.toString(); 72997963794af1e18674dd111e3ad344d90b16c922cHung-ying Tyan return s.substring(s.indexOf("@")) + ":" 73084a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan + SipSession.State.toString(mState); 7312d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } catch (Throwable e) { 7322d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return super.toString(); 7332d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 7342d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 7352d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 7362d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang public boolean process(EventObject evt) throws SipException { 737c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan if (isLoggable(this, evt)) Log.d(TAG, " ~~~~~ " + this + ": " 73884a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan + SipSession.State.toString(mState) + ": processing " 73997963794af1e18674dd111e3ad344d90b16c922cHung-ying Tyan + log(evt)); 7402d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang synchronized (SipSessionGroup.this) { 7412d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang if (isClosed()) return false; 7422d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 7434a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan if (mKeepAliveProcess != null) { 7444a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan // event consumed by keepalive process 7454a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan if (mKeepAliveProcess.process(evt)) return true; 7464a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } 7474a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan 7482d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang Dialog dialog = null; 7492d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang if (evt instanceof RequestEvent) { 7502d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang dialog = ((RequestEvent) evt).getDialog(); 7512d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } else if (evt instanceof ResponseEvent) { 7522d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang dialog = ((ResponseEvent) evt).getDialog(); 75399705b52ec952012bedc4aa8e1f62caff80a6a2fHung-ying Tyan extractExternalAddress((ResponseEvent) evt); 7542d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 7552d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang if (dialog != null) mDialog = dialog; 7562d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 7572d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang boolean processed; 7582d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 7592d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang switch (mState) { 76084a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan case SipSession.State.REGISTERING: 76184a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan case SipSession.State.DEREGISTERING: 7622d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang processed = registeringToReady(evt); 7632d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang break; 76484a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan case SipSession.State.READY_TO_CALL: 7652d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang processed = readyForCall(evt); 7662d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang break; 76784a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan case SipSession.State.INCOMING_CALL: 7682d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang processed = incomingCall(evt); 7692d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang break; 77084a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan case SipSession.State.INCOMING_CALL_ANSWERING: 7712d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang processed = incomingCallToInCall(evt); 7722d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang break; 77384a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan case SipSession.State.OUTGOING_CALL: 77484a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan case SipSession.State.OUTGOING_CALL_RING_BACK: 7752d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang processed = outgoingCall(evt); 7762d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang break; 77784a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan case SipSession.State.OUTGOING_CALL_CANCELING: 7782d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang processed = outgoingCallToReady(evt); 7792d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang break; 78084a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan case SipSession.State.IN_CALL: 7812d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang processed = inCall(evt); 7822d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang break; 783dc5bbe965f7a66238c3f9a6694f4410b3d52af3bHung-ying Tyan case SipSession.State.ENDING_CALL: 784dc5bbe965f7a66238c3f9a6694f4410b3d52af3bHung-ying Tyan processed = endingCall(evt); 785dc5bbe965f7a66238c3f9a6694f4410b3d52af3bHung-ying Tyan break; 7862d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang default: 7872d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang processed = false; 7882d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 7892d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return (processed || processExceptions(evt)); 7902d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 7912d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 7922d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 7932d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang private boolean processExceptions(EventObject evt) throws SipException { 7942d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang if (isRequestEvent(Request.BYE, evt)) { 7952d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang // terminate the call whenever a BYE is received 7962d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mSipHelper.sendResponse((RequestEvent) evt, Response.OK); 7972d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang endCallNormally(); 7982d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return true; 7992d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } else if (isRequestEvent(Request.CANCEL, evt)) { 8002d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mSipHelper.sendResponse((RequestEvent) evt, 8012d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang Response.CALL_OR_TRANSACTION_DOES_NOT_EXIST); 8022d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return true; 8032d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } else if (evt instanceof TransactionTerminatedEvent) { 804025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan if (isCurrentTransaction((TransactionTerminatedEvent) evt)) { 805025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan if (evt instanceof TimeoutEvent) { 806025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan processTimeout((TimeoutEvent) evt); 807025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan } else { 808025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan processTransactionTerminated( 809025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan (TransactionTerminatedEvent) evt); 810025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan } 811025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan return true; 8122d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 8130b4d2fb11405e2e785ec30cabe7bb311c654c0d2Chung-yih Wang } else if (isRequestEvent(Request.OPTIONS, evt)) { 8140b4d2fb11405e2e785ec30cabe7bb311c654c0d2Chung-yih Wang mSipHelper.sendResponse((RequestEvent) evt, Response.OK); 8150b4d2fb11405e2e785ec30cabe7bb311c654c0d2Chung-yih Wang return true; 8162d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } else if (evt instanceof DialogTerminatedEvent) { 8172d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang processDialogTerminated((DialogTerminatedEvent) evt); 8182d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return true; 8192d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 8202d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return false; 8212d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 8222d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 8232d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang private void processDialogTerminated(DialogTerminatedEvent event) { 8242d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang if (mDialog == event.getDialog()) { 8252d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang onError(new SipException("dialog terminated")); 8262d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } else { 8272d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang Log.d(TAG, "not the current dialog; current=" + mDialog 8282d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang + ", terminated=" + event.getDialog()); 8292d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 8302d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 8312d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 832025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan private boolean isCurrentTransaction(TransactionTerminatedEvent event) { 833025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan Transaction current = event.isServerTransaction() 834025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan ? mServerTransaction 835025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan : mClientTransaction; 836025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan Transaction target = event.isServerTransaction() 837025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan ? event.getServerTransaction() 838025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan : event.getClientTransaction(); 839025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan 840025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan if ((current != target) && (mState != SipSession.State.PINGING)) { 841025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan Log.d(TAG, "not the current transaction; current=" 842025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan + toString(current) + ", target=" + toString(target)); 843025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan return false; 844025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan } else if (current != null) { 845025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan Log.d(TAG, "transaction terminated: " + toString(current)); 846025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan return true; 847fccd5bc78f94b7dcfbcf78ddca83719c9cd1a74fHung-ying Tyan } else { 848fccd5bc78f94b7dcfbcf78ddca83719c9cd1a74fHung-ying Tyan // no transaction; shouldn't be here; ignored 849fccd5bc78f94b7dcfbcf78ddca83719c9cd1a74fHung-ying Tyan return true; 850025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan } 851025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan } 852025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan 853025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan private String toString(Transaction transaction) { 854025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan if (transaction == null) return "null"; 855025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan Request request = transaction.getRequest(); 856025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan Dialog dialog = transaction.getDialog(); 857025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan CSeqHeader cseq = (CSeqHeader) request.getHeader(CSeqHeader.NAME); 858025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan return String.format("req=%s,%s,s=%s,ds=%s,", request.getMethod(), 859025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan cseq.getSeqNumber(), transaction.getState(), 860025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan ((dialog == null) ? "-" : dialog.getState())); 861025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan } 862025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan 8633d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan private void processTransactionTerminated( 8643d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan TransactionTerminatedEvent event) { 8653d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan switch (mState) { 86684a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan case SipSession.State.IN_CALL: 86784a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan case SipSession.State.READY_TO_CALL: 8683d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan Log.d(TAG, "Transaction terminated; do nothing"); 8693d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan break; 8703d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan default: 8713d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan Log.d(TAG, "Transaction terminated early: " + this); 8723d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan onError(SipErrorCode.TRANSACTION_TERMINTED, 8733d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan "transaction terminated"); 8743d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan } 8753d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan } 8763d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan 8772d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang private void processTimeout(TimeoutEvent event) { 878025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan Log.d(TAG, "processing Timeout..."); 8792d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang switch (mState) { 88084a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan case SipSession.State.REGISTERING: 88184a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan case SipSession.State.DEREGISTERING: 8823d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan reset(); 8833d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan mProxy.onRegistrationTimeout(this); 8843d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan break; 88584a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan case SipSession.State.INCOMING_CALL: 88684a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan case SipSession.State.INCOMING_CALL_ANSWERING: 88784a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan case SipSession.State.OUTGOING_CALL: 88884a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan case SipSession.State.OUTGOING_CALL_CANCELING: 8893d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan onError(SipErrorCode.TIME_OUT, event.toString()); 8903d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan break; 8912d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 8923d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan default: 8933d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan Log.d(TAG, " do nothing"); 8943d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan break; 8952d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 8962d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 8972d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 8982d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang private int getExpiryTime(Response response) { 8995f760064e1975a50e4abb63e560731c8b2c7b56cChia-chi Yeh int time = -1; 9005f760064e1975a50e4abb63e560731c8b2c7b56cChia-chi Yeh ContactHeader contact = (ContactHeader) response.getHeader(ContactHeader.NAME); 9015f760064e1975a50e4abb63e560731c8b2c7b56cChia-chi Yeh if (contact != null) { 9025f760064e1975a50e4abb63e560731c8b2c7b56cChia-chi Yeh time = contact.getExpires(); 9032d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 9045f760064e1975a50e4abb63e560731c8b2c7b56cChia-chi Yeh ExpiresHeader expires = (ExpiresHeader) response.getHeader(ExpiresHeader.NAME); 9055f760064e1975a50e4abb63e560731c8b2c7b56cChia-chi Yeh if (expires != null && (time < 0 || time > expires.getExpires())) { 9065f760064e1975a50e4abb63e560731c8b2c7b56cChia-chi Yeh time = expires.getExpires(); 9075f760064e1975a50e4abb63e560731c8b2c7b56cChia-chi Yeh } 90854eabd6c929c6f56da28421839b0ef2945cda876Chia-chi Yeh if (time <= 0) { 90954eabd6c929c6f56da28421839b0ef2945cda876Chia-chi Yeh time = EXPIRY_TIME; 91054eabd6c929c6f56da28421839b0ef2945cda876Chia-chi Yeh } 9115f760064e1975a50e4abb63e560731c8b2c7b56cChia-chi Yeh expires = (ExpiresHeader) response.getHeader(MinExpiresHeader.NAME); 9125f760064e1975a50e4abb63e560731c8b2c7b56cChia-chi Yeh if (expires != null && time < expires.getExpires()) { 9135f760064e1975a50e4abb63e560731c8b2c7b56cChia-chi Yeh time = expires.getExpires(); 9145f760064e1975a50e4abb63e560731c8b2c7b56cChia-chi Yeh } 915cb6ee06f62c20ae036a206667097f20b837b11abChia-chi Yeh if (DEBUG) { 916cb6ee06f62c20ae036a206667097f20b837b11abChia-chi Yeh Log.v(TAG, "Expiry time = " + time); 917cb6ee06f62c20ae036a206667097f20b837b11abChia-chi Yeh } 91854eabd6c929c6f56da28421839b0ef2945cda876Chia-chi Yeh return time; 9192d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 9202d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 9212d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang private boolean registeringToReady(EventObject evt) 9222d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang throws SipException { 9232d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang if (expectResponse(Request.REGISTER, evt)) { 9242d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang ResponseEvent event = (ResponseEvent) evt; 9252d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang Response response = event.getResponse(); 9262d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 9272d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang int statusCode = response.getStatusCode(); 9282d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang switch (statusCode) { 9292d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang case Response.OK: 93097963794af1e18674dd111e3ad344d90b16c922cHung-ying Tyan int state = mState; 93184a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan onRegistrationDone((state == SipSession.State.REGISTERING) 9322d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang ? getExpiryTime(((ResponseEvent) evt).getResponse()) 9332d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang : -1); 9342d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return true; 9352d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang case Response.UNAUTHORIZED: 9362d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang case Response.PROXY_AUTHENTICATION_REQUIRED: 937ee8a884f3504c981be8a1d6888b4590a0a394e05Hung-ying Tyan handleAuthentication(event); 9382d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return true; 9392d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang default: 9402d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang if (statusCode >= 500) { 9413d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan onRegistrationFailed(response); 9422d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return true; 9432d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 9442d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 9452d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 9462d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return false; 9472d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 9482d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 949903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan private boolean handleAuthentication(ResponseEvent event) 950903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan throws SipException { 951903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan Response response = event.getResponse(); 952903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan String nonce = getNonceFromResponse(response); 953ee8a884f3504c981be8a1d6888b4590a0a394e05Hung-ying Tyan if (nonce == null) { 954ee8a884f3504c981be8a1d6888b4590a0a394e05Hung-ying Tyan onError(SipErrorCode.SERVER_ERROR, 955ee8a884f3504c981be8a1d6888b4590a0a394e05Hung-ying Tyan "server does not provide challenge"); 956903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan return false; 957ee8a884f3504c981be8a1d6888b4590a0a394e05Hung-ying Tyan } else if (mAuthenticationRetryCount < 2) { 958903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan mClientTransaction = mSipHelper.handleChallenge( 959903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan event, getAccountManager()); 960903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan mDialog = mClientTransaction.getDialog(); 961ee8a884f3504c981be8a1d6888b4590a0a394e05Hung-ying Tyan mAuthenticationRetryCount++; 962ee8a884f3504c981be8a1d6888b4590a0a394e05Hung-ying Tyan if (isLoggable(this, event)) { 963ee8a884f3504c981be8a1d6888b4590a0a394e05Hung-ying Tyan Log.d(TAG, " authentication retry count=" 964ee8a884f3504c981be8a1d6888b4590a0a394e05Hung-ying Tyan + mAuthenticationRetryCount); 965ee8a884f3504c981be8a1d6888b4590a0a394e05Hung-ying Tyan } 966903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan return true; 967ee8a884f3504c981be8a1d6888b4590a0a394e05Hung-ying Tyan } else { 968a936b256eb1611b5d8b88d0cd61f21225152cc82Hung-ying Tyan if (crossDomainAuthenticationRequired(response)) { 969a936b256eb1611b5d8b88d0cd61f21225152cc82Hung-ying Tyan onError(SipErrorCode.CROSS_DOMAIN_AUTHENTICATION, 970a936b256eb1611b5d8b88d0cd61f21225152cc82Hung-ying Tyan getRealmFromResponse(response)); 971a936b256eb1611b5d8b88d0cd61f21225152cc82Hung-ying Tyan } else { 972a936b256eb1611b5d8b88d0cd61f21225152cc82Hung-ying Tyan onError(SipErrorCode.INVALID_CREDENTIALS, 973a936b256eb1611b5d8b88d0cd61f21225152cc82Hung-ying Tyan "incorrect username or password"); 974a936b256eb1611b5d8b88d0cd61f21225152cc82Hung-ying Tyan } 975ee8a884f3504c981be8a1d6888b4590a0a394e05Hung-ying Tyan return false; 976903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan } 977903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan } 978903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan 97900a22064efef4f574e439079aae2deae1a087a31Hung-ying Tyan private boolean crossDomainAuthenticationRequired(Response response) { 98000a22064efef4f574e439079aae2deae1a087a31Hung-ying Tyan String realm = getRealmFromResponse(response); 98100a22064efef4f574e439079aae2deae1a087a31Hung-ying Tyan if (realm == null) realm = ""; 98200a22064efef4f574e439079aae2deae1a087a31Hung-ying Tyan return !mLocalProfile.getSipDomain().trim().equals(realm.trim()); 98300a22064efef4f574e439079aae2deae1a087a31Hung-ying Tyan } 98400a22064efef4f574e439079aae2deae1a087a31Hung-ying Tyan 9852d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang private AccountManager getAccountManager() { 9862d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return new AccountManager() { 9872d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang public UserCredentials getCredentials(ClientTransaction 9882d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang challengedTransaction, String realm) { 9892d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return new UserCredentials() { 9902d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang public String getUserName() { 9910f7de88cb9eef781117fa2f2b69ba2698237637eChung-yih Wang String username = mLocalProfile.getAuthUserName(); 9920f7de88cb9eef781117fa2f2b69ba2698237637eChung-yih Wang return (!TextUtils.isEmpty(username) ? username : 9930f7de88cb9eef781117fa2f2b69ba2698237637eChung-yih Wang mLocalProfile.getUserName()); 9942d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 9952d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 9962d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang public String getPassword() { 9972d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return mPassword; 9982d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 9992d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 10002d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang public String getSipDomain() { 10012d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return mLocalProfile.getSipDomain(); 10022d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 10032d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang }; 10042d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 10052d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang }; 10062d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 10072d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 100800a22064efef4f574e439079aae2deae1a087a31Hung-ying Tyan private String getRealmFromResponse(Response response) { 100900a22064efef4f574e439079aae2deae1a087a31Hung-ying Tyan WWWAuthenticate wwwAuth = (WWWAuthenticate)response.getHeader( 101000a22064efef4f574e439079aae2deae1a087a31Hung-ying Tyan SIPHeaderNames.WWW_AUTHENTICATE); 101100a22064efef4f574e439079aae2deae1a087a31Hung-ying Tyan if (wwwAuth != null) return wwwAuth.getRealm(); 101200a22064efef4f574e439079aae2deae1a087a31Hung-ying Tyan ProxyAuthenticate proxyAuth = (ProxyAuthenticate)response.getHeader( 101300a22064efef4f574e439079aae2deae1a087a31Hung-ying Tyan SIPHeaderNames.PROXY_AUTHENTICATE); 101400a22064efef4f574e439079aae2deae1a087a31Hung-ying Tyan return (proxyAuth == null) ? null : proxyAuth.getRealm(); 101500a22064efef4f574e439079aae2deae1a087a31Hung-ying Tyan } 101600a22064efef4f574e439079aae2deae1a087a31Hung-ying Tyan 10172d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang private String getNonceFromResponse(Response response) { 10187d137e40cd36290c6bfb5beaf66f4018ae92c97fChung-yih Wang WWWAuthenticate wwwAuth = (WWWAuthenticate)response.getHeader( 10197d137e40cd36290c6bfb5beaf66f4018ae92c97fChung-yih Wang SIPHeaderNames.WWW_AUTHENTICATE); 10207d137e40cd36290c6bfb5beaf66f4018ae92c97fChung-yih Wang if (wwwAuth != null) return wwwAuth.getNonce(); 10217d137e40cd36290c6bfb5beaf66f4018ae92c97fChung-yih Wang ProxyAuthenticate proxyAuth = (ProxyAuthenticate)response.getHeader( 10227d137e40cd36290c6bfb5beaf66f4018ae92c97fChung-yih Wang SIPHeaderNames.PROXY_AUTHENTICATE); 10237d137e40cd36290c6bfb5beaf66f4018ae92c97fChung-yih Wang return (proxyAuth == null) ? null : proxyAuth.getNonce(); 10242d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 10252d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 1026307f15faafa5a38d9b3b314df22778cd11685d7brepo sync private String getResponseString(int statusCode) { 1027307f15faafa5a38d9b3b314df22778cd11685d7brepo sync StatusLine statusLine = new StatusLine(); 1028307f15faafa5a38d9b3b314df22778cd11685d7brepo sync statusLine.setStatusCode(statusCode); 1029307f15faafa5a38d9b3b314df22778cd11685d7brepo sync statusLine.setReasonPhrase(SIPResponse.getReasonPhrase(statusCode)); 1030307f15faafa5a38d9b3b314df22778cd11685d7brepo sync return statusLine.encode(); 1031307f15faafa5a38d9b3b314df22778cd11685d7brepo sync } 1032307f15faafa5a38d9b3b314df22778cd11685d7brepo sync 10332d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang private boolean readyForCall(EventObject evt) throws SipException { 10342d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang // expect MakeCallCommand, RegisterCommand, DEREGISTER 10352d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang if (evt instanceof MakeCallCommand) { 1036fb3a98b1d8d0ad040980d509c4c5341928b9460bHung-ying Tyan mState = SipSession.State.OUTGOING_CALL; 10372d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang MakeCallCommand cmd = (MakeCallCommand) evt; 10382d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mPeerProfile = cmd.getPeerProfile(); 1039307f15faafa5a38d9b3b314df22778cd11685d7brepo sync if (mReferSession != null) { 1040307f15faafa5a38d9b3b314df22778cd11685d7brepo sync mSipHelper.sendReferNotify(mReferSession.mDialog, 1041307f15faafa5a38d9b3b314df22778cd11685d7brepo sync getResponseString(Response.TRYING)); 1042307f15faafa5a38d9b3b314df22778cd11685d7brepo sync } 1043307f15faafa5a38d9b3b314df22778cd11685d7brepo sync mClientTransaction = mSipHelper.sendInvite( 1044307f15faafa5a38d9b3b314df22778cd11685d7brepo sync mLocalProfile, mPeerProfile, cmd.getSessionDescription(), 1045307f15faafa5a38d9b3b314df22778cd11685d7brepo sync generateTag(), mReferredBy, mReplaces); 10462d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mDialog = mClientTransaction.getDialog(); 10472d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang addSipSession(this); 10489352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan startSessionTimer(cmd.getTimeout()); 10499ea96c6cade1f25d4d77dcbd24854df431548b36Hung-ying Tyan mProxy.onCalling(this); 10502d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return true; 10512d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } else if (evt instanceof RegisterCommand) { 1052fb3a98b1d8d0ad040980d509c4c5341928b9460bHung-ying Tyan mState = SipSession.State.REGISTERING; 10532d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang int duration = ((RegisterCommand) evt).getDuration(); 10542d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mClientTransaction = mSipHelper.sendRegister(mLocalProfile, 10552d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang generateTag(), duration); 10562d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mDialog = mClientTransaction.getDialog(); 10572d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang addSipSession(this); 10582d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mProxy.onRegistering(this); 10592d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return true; 10602d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } else if (DEREGISTER == evt) { 1061fb3a98b1d8d0ad040980d509c4c5341928b9460bHung-ying Tyan mState = SipSession.State.DEREGISTERING; 10622d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mClientTransaction = mSipHelper.sendRegister(mLocalProfile, 10632d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang generateTag(), 0); 10642d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mDialog = mClientTransaction.getDialog(); 10652d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang addSipSession(this); 10662d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mProxy.onRegistering(this); 10672d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return true; 10682d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 10692d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return false; 10702d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 10712d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 10722d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang private boolean incomingCall(EventObject evt) throws SipException { 10732d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang // expect MakeCallCommand(answering) , END_CALL cmd , Cancel 10742d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang if (evt instanceof MakeCallCommand) { 10752d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang // answer call 1076fb3a98b1d8d0ad040980d509c4c5341928b9460bHung-ying Tyan mState = SipSession.State.INCOMING_CALL_ANSWERING; 10772d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mServerTransaction = mSipHelper.sendInviteOk(mInviteReceived, 10782d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mLocalProfile, 10792d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang ((MakeCallCommand) evt).getSessionDescription(), 108099705b52ec952012bedc4aa8e1f62caff80a6a2fHung-ying Tyan mServerTransaction, 108199705b52ec952012bedc4aa8e1f62caff80a6a2fHung-ying Tyan mExternalIp, mExternalPort); 10829352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan startSessionTimer(((MakeCallCommand) evt).getTimeout()); 10832d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return true; 10842d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } else if (END_CALL == evt) { 10852d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mSipHelper.sendInviteBusyHere(mInviteReceived, 10862d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mServerTransaction); 10872d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang endCallNormally(); 10882d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return true; 10892d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } else if (isRequestEvent(Request.CANCEL, evt)) { 10902d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang RequestEvent event = (RequestEvent) evt; 10912d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mSipHelper.sendResponse(event, Response.OK); 10922d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mSipHelper.sendInviteRequestTerminated( 10932d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mInviteReceived.getRequest(), mServerTransaction); 10942d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang endCallNormally(); 10952d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return true; 10962d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 10972d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return false; 10982d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 10992d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 11002d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang private boolean incomingCallToInCall(EventObject evt) 11012d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang throws SipException { 11022d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang // expect ACK, CANCEL request 11032d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang if (isRequestEvent(Request.ACK, evt)) { 11042093561a58e602450f6e4f2aae4831edd1b840f4repo sync String sdp = extractContent(((RequestEvent) evt).getRequest()); 11052093561a58e602450f6e4f2aae4831edd1b840f4repo sync if (sdp != null) mPeerSessionDescription = sdp; 11062093561a58e602450f6e4f2aae4831edd1b840f4repo sync if (mPeerSessionDescription == null) { 11072093561a58e602450f6e4f2aae4831edd1b840f4repo sync onError(SipErrorCode.CLIENT_ERROR, "peer sdp is empty"); 11082093561a58e602450f6e4f2aae4831edd1b840f4repo sync } else { 11092093561a58e602450f6e4f2aae4831edd1b840f4repo sync establishCall(false); 11102093561a58e602450f6e4f2aae4831edd1b840f4repo sync } 11112d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return true; 11122d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } else if (isRequestEvent(Request.CANCEL, evt)) { 11132d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang // http://tools.ietf.org/html/rfc3261#section-9.2 11142d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang // Final response has been sent; do nothing here. 11152d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return true; 11162d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 11172d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return false; 11182d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 11192d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 11202d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang private boolean outgoingCall(EventObject evt) throws SipException { 11212d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang if (expectResponse(Request.INVITE, evt)) { 11222d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang ResponseEvent event = (ResponseEvent) evt; 11232d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang Response response = event.getResponse(); 11242d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 11252d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang int statusCode = response.getStatusCode(); 11262d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang switch (statusCode) { 11272d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang case Response.RINGING: 11286057cd00d95c756b78f22c67279cb982bc0674efHung-ying Tyan case Response.CALL_IS_BEING_FORWARDED: 11296057cd00d95c756b78f22c67279cb982bc0674efHung-ying Tyan case Response.QUEUED: 11306057cd00d95c756b78f22c67279cb982bc0674efHung-ying Tyan case Response.SESSION_PROGRESS: 11316057cd00d95c756b78f22c67279cb982bc0674efHung-ying Tyan // feedback any provisional responses (except TRYING) as 11326057cd00d95c756b78f22c67279cb982bc0674efHung-ying Tyan // ring back for better UX 113384a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan if (mState == SipSession.State.OUTGOING_CALL) { 113484a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan mState = SipSession.State.OUTGOING_CALL_RING_BACK; 11359352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan cancelSessionTimer(); 11369ea96c6cade1f25d4d77dcbd24854df431548b36Hung-ying Tyan mProxy.onRingingBack(this); 11372d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 11382d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return true; 11392d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang case Response.OK: 1140307f15faafa5a38d9b3b314df22778cd11685d7brepo sync if (mReferSession != null) { 1141307f15faafa5a38d9b3b314df22778cd11685d7brepo sync mSipHelper.sendReferNotify(mReferSession.mDialog, 1142307f15faafa5a38d9b3b314df22778cd11685d7brepo sync getResponseString(Response.OK)); 1143307f15faafa5a38d9b3b314df22778cd11685d7brepo sync // since we don't need to remember the session anymore. 1144307f15faafa5a38d9b3b314df22778cd11685d7brepo sync mReferSession = null; 1145307f15faafa5a38d9b3b314df22778cd11685d7brepo sync } 11462d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mSipHelper.sendInviteAck(event, mDialog); 114795b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh mPeerSessionDescription = extractContent(response); 1148233718c3c5a4f5b4f564af93cb2e42d80a900904Hung-ying Tyan establishCall(true); 11492d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return true; 11500e0633828928481658c0e09e5893f6214b57ba38Chung-yih Wang case Response.UNAUTHORIZED: 11512d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang case Response.PROXY_AUTHENTICATION_REQUIRED: 1152a936b256eb1611b5d8b88d0cd61f21225152cc82Hung-ying Tyan if (handleAuthentication(event)) { 1153903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan addSipSession(this); 1154903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan } 11552d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return true; 11562d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang case Response.REQUEST_PENDING: 11572d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang // TODO: 11582d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang // rfc3261#section-14.1; re-schedule invite 11592d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return true; 11602d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang default: 1161307f15faafa5a38d9b3b314df22778cd11685d7brepo sync if (mReferSession != null) { 1162307f15faafa5a38d9b3b314df22778cd11685d7brepo sync mSipHelper.sendReferNotify(mReferSession.mDialog, 1163307f15faafa5a38d9b3b314df22778cd11685d7brepo sync getResponseString(Response.SERVICE_UNAVAILABLE)); 1164307f15faafa5a38d9b3b314df22778cd11685d7brepo sync } 11652d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang if (statusCode >= 400) { 11662d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang // error: an ack is sent automatically by the stack 1167903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan onError(response); 11682d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return true; 11692d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } else if (statusCode >= 300) { 11702d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang // TODO: handle 3xx (redirect) 11712d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } else { 11722d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return true; 11732d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 11742d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 11752d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return false; 11762d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } else if (END_CALL == evt) { 11772d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang // RFC says that UA should not send out cancel when no 11782d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang // response comes back yet. We are cheating for not checking 11792d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang // response. 118084a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan mState = SipSession.State.OUTGOING_CALL_CANCELING; 1181fb3a98b1d8d0ad040980d509c4c5341928b9460bHung-ying Tyan mSipHelper.sendCancel(mClientTransaction); 11829352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan startSessionTimer(CANCEL_CALL_TIMER); 11832d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return true; 11840a6e717fb6846f66b8dc853e079f2166307bfc60Hung-ying Tyan } else if (isRequestEvent(Request.INVITE, evt)) { 11850a6e717fb6846f66b8dc853e079f2166307bfc60Hung-ying Tyan // Call self? Send BUSY HERE so server may redirect the call to 11860a6e717fb6846f66b8dc853e079f2166307bfc60Hung-ying Tyan // voice mailbox. 11870a6e717fb6846f66b8dc853e079f2166307bfc60Hung-ying Tyan RequestEvent event = (RequestEvent) evt; 11880a6e717fb6846f66b8dc853e079f2166307bfc60Hung-ying Tyan mSipHelper.sendInviteBusyHere(event, 11890a6e717fb6846f66b8dc853e079f2166307bfc60Hung-ying Tyan event.getServerTransaction()); 11900a6e717fb6846f66b8dc853e079f2166307bfc60Hung-ying Tyan return true; 11912d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 11922d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return false; 11932d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 11942d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 11952d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang private boolean outgoingCallToReady(EventObject evt) 11962d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang throws SipException { 11972d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang if (evt instanceof ResponseEvent) { 11982d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang ResponseEvent event = (ResponseEvent) evt; 11992d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang Response response = event.getResponse(); 12002d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang int statusCode = response.getStatusCode(); 12012d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang if (expectResponse(Request.CANCEL, evt)) { 1202025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan if (statusCode == Response.OK) { 1203025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan // do nothing; wait for REQUEST_TERMINATED 1204025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan return true; 1205025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan } 1206025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan } else if (expectResponse(Request.INVITE, evt)) { 12079352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan switch (statusCode) { 12089352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan case Response.OK: 1209025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan outgoingCall(evt); // abort Cancel 12109352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan return true; 12119352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan case Response.REQUEST_TERMINATED: 12129352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan endCallNormally(); 12139352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan return true; 12142d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 12152d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } else { 12162d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return false; 12172d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 12182d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 12192d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang if (statusCode >= 400) { 12203d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan onError(response); 12212d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return true; 12222d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 12232d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } else if (evt instanceof TransactionTerminatedEvent) { 12242d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang // rfc3261#section-14.1: 12252d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang // if re-invite gets timed out, terminate the dialog; but 12262d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang // re-invite is not reliable, just let it go and pretend 12272d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang // nothing happened. 12282d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang onError(new SipException("timed out")); 12292d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 12302d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return false; 12312d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 12322d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 1233307f15faafa5a38d9b3b314df22778cd11685d7brepo sync private boolean processReferRequest(RequestEvent event) 1234307f15faafa5a38d9b3b314df22778cd11685d7brepo sync throws SipException { 1235307f15faafa5a38d9b3b314df22778cd11685d7brepo sync try { 1236307f15faafa5a38d9b3b314df22778cd11685d7brepo sync ReferToHeader referto = (ReferToHeader) event.getRequest() 1237307f15faafa5a38d9b3b314df22778cd11685d7brepo sync .getHeader(ReferTo.NAME); 1238307f15faafa5a38d9b3b314df22778cd11685d7brepo sync Address address = referto.getAddress(); 1239307f15faafa5a38d9b3b314df22778cd11685d7brepo sync SipURI uri = (SipURI) address.getURI(); 1240307f15faafa5a38d9b3b314df22778cd11685d7brepo sync String replacesHeader = uri.getHeader(ReplacesHeader.NAME); 1241307f15faafa5a38d9b3b314df22778cd11685d7brepo sync String username = uri.getUser(); 1242307f15faafa5a38d9b3b314df22778cd11685d7brepo sync if (username == null) { 1243307f15faafa5a38d9b3b314df22778cd11685d7brepo sync mSipHelper.sendResponse(event, Response.BAD_REQUEST); 1244307f15faafa5a38d9b3b314df22778cd11685d7brepo sync return false; 1245307f15faafa5a38d9b3b314df22778cd11685d7brepo sync } 1246307f15faafa5a38d9b3b314df22778cd11685d7brepo sync // send notify accepted 1247307f15faafa5a38d9b3b314df22778cd11685d7brepo sync mSipHelper.sendResponse(event, Response.ACCEPTED); 1248307f15faafa5a38d9b3b314df22778cd11685d7brepo sync SipSessionImpl newSession = createNewSession(event, 1249307f15faafa5a38d9b3b314df22778cd11685d7brepo sync this.mProxy.getListener(), 1250307f15faafa5a38d9b3b314df22778cd11685d7brepo sync mSipHelper.getServerTransaction(event), 1251307f15faafa5a38d9b3b314df22778cd11685d7brepo sync SipSession.State.READY_TO_CALL); 1252307f15faafa5a38d9b3b314df22778cd11685d7brepo sync newSession.mReferSession = this; 1253307f15faafa5a38d9b3b314df22778cd11685d7brepo sync newSession.mReferredBy = (ReferredByHeader) event.getRequest() 1254307f15faafa5a38d9b3b314df22778cd11685d7brepo sync .getHeader(ReferredByHeader.NAME); 1255307f15faafa5a38d9b3b314df22778cd11685d7brepo sync newSession.mReplaces = replacesHeader; 1256307f15faafa5a38d9b3b314df22778cd11685d7brepo sync newSession.mPeerProfile = createPeerProfile(referto); 1257307f15faafa5a38d9b3b314df22778cd11685d7brepo sync newSession.mProxy.onCallTransferring(newSession, 1258307f15faafa5a38d9b3b314df22778cd11685d7brepo sync null); 1259307f15faafa5a38d9b3b314df22778cd11685d7brepo sync return true; 1260307f15faafa5a38d9b3b314df22778cd11685d7brepo sync } catch (IllegalArgumentException e) { 1261307f15faafa5a38d9b3b314df22778cd11685d7brepo sync throw new SipException("createPeerProfile()", e); 1262307f15faafa5a38d9b3b314df22778cd11685d7brepo sync } 1263307f15faafa5a38d9b3b314df22778cd11685d7brepo sync } 1264307f15faafa5a38d9b3b314df22778cd11685d7brepo sync 12652d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang private boolean inCall(EventObject evt) throws SipException { 12662d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang // expect END_CALL cmd, BYE request, hold call (MakeCallCommand) 12672d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang // OK retransmission is handled in SipStack 12682d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang if (END_CALL == evt) { 12692d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang // rfc3261#section-15.1.1 1270dc5bbe965f7a66238c3f9a6694f4410b3d52af3bHung-ying Tyan mState = SipSession.State.ENDING_CALL; 12712d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mSipHelper.sendBye(mDialog); 1272dc5bbe965f7a66238c3f9a6694f4410b3d52af3bHung-ying Tyan mProxy.onCallEnded(this); 1273dc5bbe965f7a66238c3f9a6694f4410b3d52af3bHung-ying Tyan startSessionTimer(END_CALL_TIMER); 12742d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return true; 12752d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } else if (isRequestEvent(Request.INVITE, evt)) { 12762d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang // got Re-INVITE 127784a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan mState = SipSession.State.INCOMING_CALL; 1278fb3a98b1d8d0ad040980d509c4c5341928b9460bHung-ying Tyan RequestEvent event = mInviteReceived = (RequestEvent) evt; 127995b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh mPeerSessionDescription = extractContent(event.getRequest()); 12802d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mServerTransaction = null; 12812d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mProxy.onRinging(this, mPeerProfile, mPeerSessionDescription); 12822d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return true; 12832d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } else if (isRequestEvent(Request.BYE, evt)) { 12842d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mSipHelper.sendResponse((RequestEvent) evt, Response.OK); 12852d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang endCallNormally(); 12862d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return true; 1287307f15faafa5a38d9b3b314df22778cd11685d7brepo sync } else if (isRequestEvent(Request.REFER, evt)) { 1288307f15faafa5a38d9b3b314df22778cd11685d7brepo sync return processReferRequest((RequestEvent) evt); 12892d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } else if (evt instanceof MakeCallCommand) { 12902d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang // to change call 1291fb3a98b1d8d0ad040980d509c4c5341928b9460bHung-ying Tyan mState = SipSession.State.OUTGOING_CALL; 12922d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mClientTransaction = mSipHelper.sendReinvite(mDialog, 12932d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang ((MakeCallCommand) evt).getSessionDescription()); 12949352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan startSessionTimer(((MakeCallCommand) evt).getTimeout()); 12952d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return true; 1296307f15faafa5a38d9b3b314df22778cd11685d7brepo sync } else if (evt instanceof ResponseEvent) { 1297307f15faafa5a38d9b3b314df22778cd11685d7brepo sync if (expectResponse(Request.NOTIFY, evt)) return true; 12982d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 12992d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return false; 13002d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 13012d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 1302dc5bbe965f7a66238c3f9a6694f4410b3d52af3bHung-ying Tyan private boolean endingCall(EventObject evt) throws SipException { 1303dc5bbe965f7a66238c3f9a6694f4410b3d52af3bHung-ying Tyan if (expectResponse(Request.BYE, evt)) { 1304dc5bbe965f7a66238c3f9a6694f4410b3d52af3bHung-ying Tyan ResponseEvent event = (ResponseEvent) evt; 1305dc5bbe965f7a66238c3f9a6694f4410b3d52af3bHung-ying Tyan Response response = event.getResponse(); 1306dc5bbe965f7a66238c3f9a6694f4410b3d52af3bHung-ying Tyan 1307dc5bbe965f7a66238c3f9a6694f4410b3d52af3bHung-ying Tyan int statusCode = response.getStatusCode(); 1308dc5bbe965f7a66238c3f9a6694f4410b3d52af3bHung-ying Tyan switch (statusCode) { 1309dc5bbe965f7a66238c3f9a6694f4410b3d52af3bHung-ying Tyan case Response.UNAUTHORIZED: 1310dc5bbe965f7a66238c3f9a6694f4410b3d52af3bHung-ying Tyan case Response.PROXY_AUTHENTICATION_REQUIRED: 1311dc5bbe965f7a66238c3f9a6694f4410b3d52af3bHung-ying Tyan if (handleAuthentication(event)) { 1312dc5bbe965f7a66238c3f9a6694f4410b3d52af3bHung-ying Tyan return true; 1313dc5bbe965f7a66238c3f9a6694f4410b3d52af3bHung-ying Tyan } else { 1314dc5bbe965f7a66238c3f9a6694f4410b3d52af3bHung-ying Tyan // can't authenticate; pass through to end session 1315dc5bbe965f7a66238c3f9a6694f4410b3d52af3bHung-ying Tyan } 1316dc5bbe965f7a66238c3f9a6694f4410b3d52af3bHung-ying Tyan } 1317dc5bbe965f7a66238c3f9a6694f4410b3d52af3bHung-ying Tyan cancelSessionTimer(); 1318dc5bbe965f7a66238c3f9a6694f4410b3d52af3bHung-ying Tyan reset(); 1319dc5bbe965f7a66238c3f9a6694f4410b3d52af3bHung-ying Tyan return true; 1320dc5bbe965f7a66238c3f9a6694f4410b3d52af3bHung-ying Tyan } 1321dc5bbe965f7a66238c3f9a6694f4410b3d52af3bHung-ying Tyan return false; 1322dc5bbe965f7a66238c3f9a6694f4410b3d52af3bHung-ying Tyan } 1323dc5bbe965f7a66238c3f9a6694f4410b3d52af3bHung-ying Tyan 13249352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan // timeout in seconds 13259352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan private void startSessionTimer(int timeout) { 13269352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan if (timeout > 0) { 13274a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan mSessionTimer = new SessionTimer(); 13284a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan mSessionTimer.start(timeout); 13299352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan } 13309352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan } 13319352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan 13329352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan private void cancelSessionTimer() { 13334a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan if (mSessionTimer != null) { 13344a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan mSessionTimer.cancel(); 13354a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan mSessionTimer = null; 13369352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan } 13379352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan } 13389352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan 1339903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan private String createErrorMessage(Response response) { 1340624d5b4e8c20516516d0bff74479b9f5abdfe61cHung-ying Tyan return String.format("%s (%d)", response.getReasonPhrase(), 1341624d5b4e8c20516516d0bff74479b9f5abdfe61cHung-ying Tyan response.getStatusCode()); 1342903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan } 1343903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan 1344233718c3c5a4f5b4f564af93cb2e42d80a900904Hung-ying Tyan private void enableKeepAlive() { 1345233718c3c5a4f5b4f564af93cb2e42d80a900904Hung-ying Tyan if (mKeepAliveSession != null) { 1346233718c3c5a4f5b4f564af93cb2e42d80a900904Hung-ying Tyan mKeepAliveSession.stopKeepAliveProcess(); 1347233718c3c5a4f5b4f564af93cb2e42d80a900904Hung-ying Tyan } else { 1348233718c3c5a4f5b4f564af93cb2e42d80a900904Hung-ying Tyan mKeepAliveSession = duplicate(); 1349233718c3c5a4f5b4f564af93cb2e42d80a900904Hung-ying Tyan } 1350233718c3c5a4f5b4f564af93cb2e42d80a900904Hung-ying Tyan try { 1351233718c3c5a4f5b4f564af93cb2e42d80a900904Hung-ying Tyan mKeepAliveSession.startKeepAliveProcess( 1352233718c3c5a4f5b4f564af93cb2e42d80a900904Hung-ying Tyan INCALL_KEEPALIVE_INTERVAL, mPeerProfile, null); 1353233718c3c5a4f5b4f564af93cb2e42d80a900904Hung-ying Tyan } catch (SipException e) { 1354233718c3c5a4f5b4f564af93cb2e42d80a900904Hung-ying Tyan Log.w(TAG, "keepalive cannot be enabled; ignored", e); 1355233718c3c5a4f5b4f564af93cb2e42d80a900904Hung-ying Tyan mKeepAliveSession.stopKeepAliveProcess(); 1356233718c3c5a4f5b4f564af93cb2e42d80a900904Hung-ying Tyan } 1357233718c3c5a4f5b4f564af93cb2e42d80a900904Hung-ying Tyan } 1358233718c3c5a4f5b4f564af93cb2e42d80a900904Hung-ying Tyan 1359233718c3c5a4f5b4f564af93cb2e42d80a900904Hung-ying Tyan private void establishCall(boolean enableKeepAlive) { 136084a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan mState = SipSession.State.IN_CALL; 13619352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan cancelSessionTimer(); 13628ba4566c01c5848b378d1d86e9041730f5b5a13fHung-ying Tyan if (!mInCall && enableKeepAlive) enableKeepAlive(); 13638ba4566c01c5848b378d1d86e9041730f5b5a13fHung-ying Tyan mInCall = true; 13642d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mProxy.onCallEstablished(this, mPeerSessionDescription); 13652d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 13662d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 13672d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang private void endCallNormally() { 13682d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang reset(); 13692d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mProxy.onCallEnded(this); 13702d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 13712d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 137297963794af1e18674dd111e3ad344d90b16c922cHung-ying Tyan private void endCallOnError(int errorCode, String message) { 13732d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang reset(); 137497963794af1e18674dd111e3ad344d90b16c922cHung-ying Tyan mProxy.onError(this, errorCode, message); 1375903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan } 1376903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan 1377903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan private void endCallOnBusy() { 1378903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan reset(); 1379903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan mProxy.onCallBusy(this); 13802d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 13812d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 138297963794af1e18674dd111e3ad344d90b16c922cHung-ying Tyan private void onError(int errorCode, String message) { 13839352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan cancelSessionTimer(); 13843d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan switch (mState) { 138584a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan case SipSession.State.REGISTERING: 138684a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan case SipSession.State.DEREGISTERING: 13873d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan onRegistrationFailed(errorCode, message); 13883d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan break; 13893d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan default: 13904189d99b6e4877352049b7447b7f0734ef99b9e8Hung-ying Tyan endCallOnError(errorCode, message); 13912d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 13922d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 13932d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 13943d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan 13953d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan private void onError(Throwable exception) { 13963d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan exception = getRootCause(exception); 13973d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan onError(getErrorCode(exception), exception.toString()); 13983d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan } 13993d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan 1400903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan private void onError(Response response) { 14013d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan int statusCode = response.getStatusCode(); 1402ae076d3981fda732d54b6c6e37e5659b2e7ba130Hung-ying Tyan if (!mInCall && (statusCode == Response.BUSY_HERE)) { 14033d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan endCallOnBusy(); 1404903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan } else { 14053d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan onError(getErrorCode(statusCode), createErrorMessage(response)); 1406903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan } 1407903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan } 1408903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan 140997963794af1e18674dd111e3ad344d90b16c922cHung-ying Tyan private int getErrorCode(int responseStatusCode) { 1410903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan switch (responseStatusCode) { 1411ae076d3981fda732d54b6c6e37e5659b2e7ba130Hung-ying Tyan case Response.TEMPORARILY_UNAVAILABLE: 1412ae076d3981fda732d54b6c6e37e5659b2e7ba130Hung-ying Tyan case Response.FORBIDDEN: 1413ae076d3981fda732d54b6c6e37e5659b2e7ba130Hung-ying Tyan case Response.GONE: 1414903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan case Response.NOT_FOUND: 1415ae076d3981fda732d54b6c6e37e5659b2e7ba130Hung-ying Tyan case Response.NOT_ACCEPTABLE: 1416ae076d3981fda732d54b6c6e37e5659b2e7ba130Hung-ying Tyan case Response.NOT_ACCEPTABLE_HERE: 1417ae076d3981fda732d54b6c6e37e5659b2e7ba130Hung-ying Tyan return SipErrorCode.PEER_NOT_REACHABLE; 1418ae076d3981fda732d54b6c6e37e5659b2e7ba130Hung-ying Tyan 1419ae076d3981fda732d54b6c6e37e5659b2e7ba130Hung-ying Tyan case Response.REQUEST_URI_TOO_LONG: 1420903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan case Response.ADDRESS_INCOMPLETE: 1421ae076d3981fda732d54b6c6e37e5659b2e7ba130Hung-ying Tyan case Response.AMBIGUOUS: 1422903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan return SipErrorCode.INVALID_REMOTE_URI; 1423ae076d3981fda732d54b6c6e37e5659b2e7ba130Hung-ying Tyan 1424903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan case Response.REQUEST_TIMEOUT: 1425903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan return SipErrorCode.TIME_OUT; 1426ae076d3981fda732d54b6c6e37e5659b2e7ba130Hung-ying Tyan 1427903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan default: 1428903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan if (responseStatusCode < 500) { 1429903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan return SipErrorCode.CLIENT_ERROR; 1430903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan } else { 1431903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan return SipErrorCode.SERVER_ERROR; 1432903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan } 1433903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan } 1434903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan } 1435903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan 143697963794af1e18674dd111e3ad344d90b16c922cHung-ying Tyan private int getErrorCode(Throwable exception) { 1437903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan String message = exception.getMessage(); 1438903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan if (exception instanceof UnknownHostException) { 1439c6548fd9eda7b58f5a2e2a9c01e3c7cafd42fafbHung-ying Tyan return SipErrorCode.SERVER_UNREACHABLE; 1440903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan } else if (exception instanceof IOException) { 1441903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan return SipErrorCode.SOCKET_ERROR; 1442903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan } else { 1443903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan return SipErrorCode.CLIENT_ERROR; 1444903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan } 1445903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan } 1446903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan 14472d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang private void onRegistrationDone(int duration) { 14483d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan reset(); 14492d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mProxy.onRegistrationDone(this, duration); 14502d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 14512d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 145297963794af1e18674dd111e3ad344d90b16c922cHung-ying Tyan private void onRegistrationFailed(int errorCode, String message) { 14533d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan reset(); 145497963794af1e18674dd111e3ad344d90b16c922cHung-ying Tyan mProxy.onRegistrationFailed(this, errorCode, message); 1455903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan } 1456903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan 14572d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang private void onRegistrationFailed(Throwable exception) { 1458903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan exception = getRootCause(exception); 1459903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan onRegistrationFailed(getErrorCode(exception), 1460903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan exception.toString()); 14612d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 14623d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan 14633d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan private void onRegistrationFailed(Response response) { 14643d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan int statusCode = response.getStatusCode(); 14653d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan onRegistrationFailed(getErrorCode(statusCode), 14663d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan createErrorMessage(response)); 14673d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan } 14684a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan 14694a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan // Notes: SipSessionListener will be replaced by the keepalive process 14704a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan // @param interval in seconds 14714a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan public void startKeepAliveProcess(int interval, 14724a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan KeepAliveProcessCallback callback) throws SipException { 14734a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan synchronized (SipSessionGroup.this) { 14744a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan startKeepAliveProcess(interval, mLocalProfile, callback); 14754a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } 14764a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } 14774a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan 14784a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan // Notes: SipSessionListener will be replaced by the keepalive process 14794a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan // @param interval in seconds 14804a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan public void startKeepAliveProcess(int interval, SipProfile peerProfile, 14814a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan KeepAliveProcessCallback callback) throws SipException { 14824a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan synchronized (SipSessionGroup.this) { 14834a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan if (mKeepAliveProcess != null) { 14844a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan throw new SipException("Cannot create more than one " 14854a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan + "keepalive process in a SipSession"); 14864a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } 14874a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan mPeerProfile = peerProfile; 14884a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan mKeepAliveProcess = new KeepAliveProcess(); 14894a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan mProxy.setListener(mKeepAliveProcess); 14904a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan mKeepAliveProcess.start(interval, callback); 14914a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } 14924a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } 14934a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan 14944a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan public void stopKeepAliveProcess() { 14954a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan synchronized (SipSessionGroup.this) { 14964a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan if (mKeepAliveProcess != null) { 14974a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan mKeepAliveProcess.stop(); 14984a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan mKeepAliveProcess = null; 14994a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } 15004a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } 15014a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } 15024a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan 15034a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan class KeepAliveProcess extends SipSessionAdapter implements Runnable { 15044a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan private static final String TAG = "SipKeepAlive"; 15054a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan private boolean mRunning = false; 15064a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan private KeepAliveProcessCallback mCallback; 15074a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan 15084a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan private boolean mPortChanged = false; 15094a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan private int mRPort = 0; 1510e65f3a896f03bba5327ce4f3989c0422855450caHung-ying Tyan private int mInterval; // just for debugging 15114a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan 15124a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan // @param interval in seconds 15134a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan void start(int interval, KeepAliveProcessCallback callback) { 15144a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan if (mRunning) return; 15154a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan mRunning = true; 1516e65f3a896f03bba5327ce4f3989c0422855450caHung-ying Tyan mInterval = interval; 15174a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan mCallback = new KeepAliveProcessCallbackProxy(callback); 15184a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan mWakeupTimer.set(interval * 1000, this); 15194a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan if (DEBUG) { 15204a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan Log.d(TAG, "start keepalive:" 15214a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan + mLocalProfile.getUriString()); 15224a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } 15234a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan 15244a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan // No need to run the first time in a separate thread for now 15254a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan run(); 15264a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } 15274a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan 15284a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan // return true if the event is consumed 15294a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan boolean process(EventObject evt) throws SipException { 15304a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan if (mRunning && (mState == SipSession.State.PINGING)) { 15314a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan if (evt instanceof ResponseEvent) { 15324a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan if (parseOptionsResult(evt)) { 15334a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan if (mPortChanged) { 153499705b52ec952012bedc4aa8e1f62caff80a6a2fHung-ying Tyan resetExternalAddress(); 15354a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan stop(); 15364a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } else { 15374a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan cancelSessionTimer(); 15384a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan removeSipSession(SipSessionImpl.this); 15394a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } 15404a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan mCallback.onResponse(mPortChanged); 15414a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan return true; 15424a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } 15434a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } 15444a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } 15454a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan return false; 15464a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } 15474a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan 15484a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan // SipSessionAdapter 15494a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan // To react to the session timeout event and network error. 15504a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan @Override 15514a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan public void onError(ISipSession session, int errorCode, String message) { 15524a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan stop(); 15534a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan mCallback.onError(errorCode, message); 15544a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } 15554a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan 15564a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan // SipWakeupTimer timeout handler 15574a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan // To send out keepalive message. 15584a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan @Override 15594a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan public void run() { 15604a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan synchronized (SipSessionGroup.this) { 15614a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan if (!mRunning) return; 15624a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan 15634a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan if (DEBUG_PING) { 156499705b52ec952012bedc4aa8e1f62caff80a6a2fHung-ying Tyan String peerUri = (mPeerProfile == null) 156599705b52ec952012bedc4aa8e1f62caff80a6a2fHung-ying Tyan ? "null" 156699705b52ec952012bedc4aa8e1f62caff80a6a2fHung-ying Tyan : mPeerProfile.getUriString(); 15674a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan Log.d(TAG, "keepalive: " + mLocalProfile.getUriString() 156899705b52ec952012bedc4aa8e1f62caff80a6a2fHung-ying Tyan + " --> " + peerUri + ", interval=" + mInterval); 15694a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } 15704a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan try { 15714a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan sendKeepAlive(); 15724a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } catch (Throwable t) { 1573cb6ee06f62c20ae036a206667097f20b837b11abChia-chi Yeh if (DEBUG) { 1574cb6ee06f62c20ae036a206667097f20b837b11abChia-chi Yeh Log.w(TAG, "keepalive error: " 1575cb6ee06f62c20ae036a206667097f20b837b11abChia-chi Yeh + mLocalProfile.getUriString(), getRootCause(t)); 1576cb6ee06f62c20ae036a206667097f20b837b11abChia-chi Yeh } 15774a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan // It's possible that the keepalive process is being stopped 15784a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan // during session.sendKeepAlive() so need to check mRunning 15794a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan // again here. 15804a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan if (mRunning) SipSessionImpl.this.onError(t); 15814a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } 15824a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } 15834a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } 15844a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan 15854a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan void stop() { 15864a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan synchronized (SipSessionGroup.this) { 15874a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan if (DEBUG) { 15884a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan Log.d(TAG, "stop keepalive:" + mLocalProfile.getUriString() 15894a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan + ",RPort=" + mRPort); 15904a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } 15914a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan mRunning = false; 15924a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan mWakeupTimer.cancel(this); 15934a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan reset(); 15944a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } 15954a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } 15964a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan 15974a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan private void sendKeepAlive() throws SipException, InterruptedException { 15984a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan synchronized (SipSessionGroup.this) { 15994a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan mState = SipSession.State.PINGING; 16004a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan mClientTransaction = mSipHelper.sendOptions( 16014a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan mLocalProfile, mPeerProfile, generateTag()); 16024a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan mDialog = mClientTransaction.getDialog(); 16034a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan addSipSession(SipSessionImpl.this); 16044a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan 16054a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan startSessionTimer(KEEPALIVE_TIMEOUT); 16064a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan // when timed out, onError() will be called with SipErrorCode.TIME_OUT 16074a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } 16084a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } 16094a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan 16104a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan private boolean parseOptionsResult(EventObject evt) { 16114a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan if (expectResponse(Request.OPTIONS, evt)) { 16124a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan ResponseEvent event = (ResponseEvent) evt; 16134a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan int rPort = getRPortFromResponse(event.getResponse()); 16144a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan if (rPort != -1) { 16154a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan if (mRPort == 0) mRPort = rPort; 16164a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan if (mRPort != rPort) { 16174a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan mPortChanged = true; 16184a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan if (DEBUG) Log.d(TAG, String.format( 16194a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan "rport is changed: %d <> %d", mRPort, rPort)); 16204a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan mRPort = rPort; 16214a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } else { 16224a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan if (DEBUG) Log.d(TAG, "rport is the same: " + rPort); 16234a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } 16244a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } else { 16254a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan if (DEBUG) Log.w(TAG, "peer did not respond rport"); 16264a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } 16274a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan return true; 16284a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } 16294a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan return false; 16304a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } 16314a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan 16324a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan private int getRPortFromResponse(Response response) { 16334a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan ViaHeader viaHeader = (ViaHeader)(response.getHeader( 16344a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan SIPHeaderNames.VIA)); 16354a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan return (viaHeader == null) ? -1 : viaHeader.getRPort(); 16364a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } 16374a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } 16382d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 16392d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 16402d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang /** 16412d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang * @return true if the event is a request event matching the specified 16422d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang * method; false otherwise 16432d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang */ 16442d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang private static boolean isRequestEvent(String method, EventObject event) { 16452d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang try { 16462d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang if (event instanceof RequestEvent) { 16472d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang RequestEvent requestEvent = (RequestEvent) event; 16482d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return method.equals(requestEvent.getRequest().getMethod()); 16492d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 16502d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } catch (Throwable e) { 16512d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 16522d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return false; 16532d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 16542d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 16552d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang private static String getCseqMethod(Message message) { 16562d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return ((CSeqHeader) message.getHeader(CSeqHeader.NAME)).getMethod(); 16572d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 16582d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 16592d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang /** 16602d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang * @return true if the event is a response event and the CSeqHeader method 16612d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang * match the given arguments; false otherwise 16622d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang */ 16632d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang private static boolean expectResponse( 16642d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang String expectedMethod, EventObject evt) { 16652d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang if (evt instanceof ResponseEvent) { 16662d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang ResponseEvent event = (ResponseEvent) evt; 16672d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang Response response = event.getResponse(); 16682d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return expectedMethod.equalsIgnoreCase(getCseqMethod(response)); 16692d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 16702d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return false; 16712d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 16722d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 16732d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang /** 16742d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang * @return true if the event is a response event and the response code and 16752d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang * CSeqHeader method match the given arguments; false otherwise 16762d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang */ 16772d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang private static boolean expectResponse( 16782d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang int responseCode, String expectedMethod, EventObject evt) { 16792d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang if (evt instanceof ResponseEvent) { 16802d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang ResponseEvent event = (ResponseEvent) evt; 16812d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang Response response = event.getResponse(); 16822d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang if (response.getStatusCode() == responseCode) { 16832d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return expectedMethod.equalsIgnoreCase(getCseqMethod(response)); 16842d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 16852d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 16862d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return false; 16872d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 16882d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 1689307f15faafa5a38d9b3b314df22778cd11685d7brepo sync private static SipProfile createPeerProfile(HeaderAddress header) 16902d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang throws SipException { 16912d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang try { 1692307f15faafa5a38d9b3b314df22778cd11685d7brepo sync Address address = header.getAddress(); 16932d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang SipURI uri = (SipURI) address.getURI(); 16942d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang String username = uri.getUser(); 16952d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang if (username == null) username = ANONYMOUS; 169658ee2acba8953814cc4bf65d2f28f7dd498b5779Hung-ying Tyan int port = uri.getPort(); 169758ee2acba8953814cc4bf65d2f28f7dd498b5779Hung-ying Tyan SipProfile.Builder builder = 169858ee2acba8953814cc4bf65d2f28f7dd498b5779Hung-ying Tyan new SipProfile.Builder(username, uri.getHost()) 169958ee2acba8953814cc4bf65d2f28f7dd498b5779Hung-ying Tyan .setDisplayName(address.getDisplayName()); 170058ee2acba8953814cc4bf65d2f28f7dd498b5779Hung-ying Tyan if (port > 0) builder.setPort(port); 170158ee2acba8953814cc4bf65d2f28f7dd498b5779Hung-ying Tyan return builder.build(); 170299bf4e45c4566172189735b34b368b76660ca57aHung-ying Tyan } catch (IllegalArgumentException e) { 17032d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang throw new SipException("createPeerProfile()", e); 17042d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } catch (ParseException e) { 17052d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang throw new SipException("createPeerProfile()", e); 17062d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 17072d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 17082d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 1709c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan private static boolean isLoggable(SipSessionImpl s) { 1710c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan if (s != null) { 1711c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan switch (s.mState) { 171284a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan case SipSession.State.PINGING: 1713c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan return DEBUG_PING; 1714c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan } 1715c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan } 1716c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan return DEBUG; 1717c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan } 1718c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan 17190a6e717fb6846f66b8dc853e079f2166307bfc60Hung-ying Tyan private static boolean isLoggable(EventObject evt) { 17200a6e717fb6846f66b8dc853e079f2166307bfc60Hung-ying Tyan return isLoggable(null, evt); 17210a6e717fb6846f66b8dc853e079f2166307bfc60Hung-ying Tyan } 17220a6e717fb6846f66b8dc853e079f2166307bfc60Hung-ying Tyan 1723c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan private static boolean isLoggable(SipSessionImpl s, EventObject evt) { 1724c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan if (!isLoggable(s)) return false; 1725c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan if (evt == null) return false; 1726c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan 17274a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan if (evt instanceof ResponseEvent) { 1728c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan Response response = ((ResponseEvent) evt).getResponse(); 1729c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan if (Request.OPTIONS.equals(response.getHeader(CSeqHeader.NAME))) { 1730c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan return DEBUG_PING; 1731c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan } 1732c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan return DEBUG; 1733c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan } else if (evt instanceof RequestEvent) { 17344a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan if (isRequestEvent(Request.OPTIONS, evt)) { 17354a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan return DEBUG_PING; 17364a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } 1737c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan return DEBUG; 1738c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan } 1739c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan return false; 1740c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan } 1741c7510581b81d63536db7d46ca8533106c8cf57c6Hung-ying Tyan 17422d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang private static String log(EventObject evt) { 17432d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang if (evt instanceof RequestEvent) { 17442d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return ((RequestEvent) evt).getRequest().toString(); 17452d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } else if (evt instanceof ResponseEvent) { 17462d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return ((ResponseEvent) evt).getResponse().toString(); 17472d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } else { 17482d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return evt.toString(); 17492d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 17502d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 17512d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 17522d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang private class RegisterCommand extends EventObject { 17532d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang private int mDuration; 17542d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 17552d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang public RegisterCommand(int duration) { 17562d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang super(SipSessionGroup.this); 17572d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mDuration = duration; 17582d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 17592d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 17602d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang public int getDuration() { 17612d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return mDuration; 17622d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 17632d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 17642d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 17652d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang private class MakeCallCommand extends EventObject { 176695b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh private String mSessionDescription; 17679352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan private int mTimeout; // in seconds 17682d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 17692d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang public MakeCallCommand(SipProfile peerProfile, 177095b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh String sessionDescription) { 17719352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan this(peerProfile, sessionDescription, -1); 17729352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan } 17739352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan 17749352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan public MakeCallCommand(SipProfile peerProfile, 17759352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan String sessionDescription, int timeout) { 17762d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang super(peerProfile); 17772d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang mSessionDescription = sessionDescription; 17789352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan mTimeout = timeout; 17792d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 17802d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 17812d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang public SipProfile getPeerProfile() { 17822d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return (SipProfile) getSource(); 17832d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 17842d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 178595b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh public String getSessionDescription() { 17862d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang return mSessionDescription; 17872d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang } 17882d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang 17899352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan public int getTimeout() { 17909352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan return mTimeout; 17919352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan } 17929352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan } 17934a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan 17944a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan /** Class to help safely run KeepAliveProcessCallback in a different thread. */ 17954a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan static class KeepAliveProcessCallbackProxy implements KeepAliveProcessCallback { 17964a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan private KeepAliveProcessCallback mCallback; 17974a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan 17984a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan KeepAliveProcessCallbackProxy(KeepAliveProcessCallback callback) { 17994a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan mCallback = callback; 18004a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } 18014a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan 18024a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan private void proxy(Runnable runnable) { 18034a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan // One thread for each calling back. 18044a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan // Note: Guarantee ordering if the issue becomes important. Currently, 18054a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan // the chance of handling two callback events at a time is none. 18064a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan new Thread(runnable, "SIP-KeepAliveProcessCallbackThread").start(); 18074a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } 18084a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan 18094a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan public void onResponse(final boolean portChanged) { 18104a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan if (mCallback == null) return; 18114a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan proxy(new Runnable() { 18124a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan public void run() { 18134a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan try { 18144a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan mCallback.onResponse(portChanged); 18154a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } catch (Throwable t) { 18164a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan Log.w(TAG, "onResponse", t); 18174a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } 18184a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } 18194a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan }); 18204a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } 18214a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan 18224a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan public void onError(final int errorCode, final String description) { 18234a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan if (mCallback == null) return; 18244a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan proxy(new Runnable() { 18254a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan public void run() { 18264a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan try { 18274a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan mCallback.onError(errorCode, description); 18284a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } catch (Throwable t) { 18294a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan Log.w(TAG, "onError", t); 18304a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } 18314a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } 18324a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan }); 18334a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } 18344a267a9158a62010cd76ab93681586ea8e3d6015Hung-ying Tyan } 18352d94231ef91c732f649ff7af9520ee9eac441b16Chung-yih Wang} 1836