diff options
Diffstat (limited to 'voip/java/com/android/server/sip/SipSessionGroup.java')
-rw-r--r-- | voip/java/com/android/server/sip/SipSessionGroup.java | 1863 |
1 files changed, 0 insertions, 1863 deletions
diff --git a/voip/java/com/android/server/sip/SipSessionGroup.java b/voip/java/com/android/server/sip/SipSessionGroup.java deleted file mode 100644 index e820f356bea3..000000000000 --- a/voip/java/com/android/server/sip/SipSessionGroup.java +++ /dev/null @@ -1,1863 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.sip; - -import gov.nist.javax.sip.clientauthutils.AccountManager; -import gov.nist.javax.sip.clientauthutils.UserCredentials; -import gov.nist.javax.sip.header.ProxyAuthenticate; -import gov.nist.javax.sip.header.ReferTo; -import gov.nist.javax.sip.header.SIPHeaderNames; -import gov.nist.javax.sip.header.StatusLine; -import gov.nist.javax.sip.header.WWWAuthenticate; -import gov.nist.javax.sip.header.extensions.ReferredByHeader; -import gov.nist.javax.sip.header.extensions.ReplacesHeader; -import gov.nist.javax.sip.message.SIPMessage; -import gov.nist.javax.sip.message.SIPResponse; - -import android.net.sip.ISipSession; -import android.net.sip.ISipSessionListener; -import android.net.sip.SipErrorCode; -import android.net.sip.SipProfile; -import android.net.sip.SipSession; -import android.net.sip.SipSessionAdapter; -import android.text.TextUtils; -import android.telephony.Rlog; - -import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.net.DatagramSocket; -import java.net.InetAddress; -import java.net.UnknownHostException; -import java.text.ParseException; -import java.util.EventObject; -import java.util.HashMap; -import java.util.Map; -import java.util.Properties; - -import javax.sip.ClientTransaction; -import javax.sip.Dialog; -import javax.sip.DialogTerminatedEvent; -import javax.sip.IOExceptionEvent; -import javax.sip.ObjectInUseException; -import javax.sip.RequestEvent; -import javax.sip.ResponseEvent; -import javax.sip.ServerTransaction; -import javax.sip.SipException; -import javax.sip.SipFactory; -import javax.sip.SipListener; -import javax.sip.SipProvider; -import javax.sip.SipStack; -import javax.sip.TimeoutEvent; -import javax.sip.Transaction; -import javax.sip.TransactionTerminatedEvent; -import javax.sip.address.Address; -import javax.sip.address.SipURI; -import javax.sip.header.CSeqHeader; -import javax.sip.header.ContactHeader; -import javax.sip.header.ExpiresHeader; -import javax.sip.header.FromHeader; -import javax.sip.header.HeaderAddress; -import javax.sip.header.MinExpiresHeader; -import javax.sip.header.ReferToHeader; -import javax.sip.header.ViaHeader; -import javax.sip.message.Message; -import javax.sip.message.Request; -import javax.sip.message.Response; - - -/** - * Manages {@link ISipSession}'s for a SIP account. - */ -class SipSessionGroup implements SipListener { - private static final String TAG = "SipSession"; - private static final boolean DBG = false; - private static final boolean DBG_PING = false; - private static final String ANONYMOUS = "anonymous"; - // Limit the size of thread pool to 1 for the order issue when the phone is - // waken up from sleep and there are many packets to be processed in the SIP - // stack. Note: The default thread pool size in NIST SIP stack is -1 which is - // unlimited. - private static final String THREAD_POOL_SIZE = "1"; - private static final int EXPIRY_TIME = 3600; // in seconds - private static final int CANCEL_CALL_TIMER = 3; // in seconds - private static final int END_CALL_TIMER = 3; // in seconds - private static final int KEEPALIVE_TIMEOUT = 5; // in seconds - private static final int INCALL_KEEPALIVE_INTERVAL = 10; // in seconds - private static final long WAKE_LOCK_HOLDING_TIME = 500; // in milliseconds - - private static final EventObject DEREGISTER = new EventObject("Deregister"); - private static final EventObject END_CALL = new EventObject("End call"); - - private final SipProfile mLocalProfile; - private final String mPassword; - - private SipStack mSipStack; - private SipHelper mSipHelper; - - // session that processes INVITE requests - private SipSessionImpl mCallReceiverSession; - private String mLocalIp; - - private SipWakeupTimer mWakeupTimer; - private SipWakeLock mWakeLock; - - // call-id-to-SipSession map - private Map<String, SipSessionImpl> mSessionMap = - new HashMap<String, SipSessionImpl>(); - - // external address observed from any response - private String mExternalIp; - private int mExternalPort; - - /** - * @param profile the local profile with password crossed out - * @param password the password of the profile - * @throws SipException if cannot assign requested address - */ - public SipSessionGroup(SipProfile profile, String password, - SipWakeupTimer timer, SipWakeLock wakeLock) throws SipException { - mLocalProfile = profile; - mPassword = password; - mWakeupTimer = timer; - mWakeLock = wakeLock; - reset(); - } - - // TODO: remove this method once SipWakeupTimer can better handle variety - // of timeout values - void setWakeupTimer(SipWakeupTimer timer) { - mWakeupTimer = timer; - } - - synchronized void reset() throws SipException { - Properties properties = new Properties(); - - String protocol = mLocalProfile.getProtocol(); - int port = mLocalProfile.getPort(); - String server = mLocalProfile.getProxyAddress(); - - if (!TextUtils.isEmpty(server)) { - properties.setProperty("javax.sip.OUTBOUND_PROXY", - server + ':' + port + '/' + protocol); - } else { - server = mLocalProfile.getSipDomain(); - } - if (server.startsWith("[") && server.endsWith("]")) { - server = server.substring(1, server.length() - 1); - } - - String local = null; - try { - for (InetAddress remote : InetAddress.getAllByName(server)) { - DatagramSocket socket = new DatagramSocket(); - socket.connect(remote, port); - if (socket.isConnected()) { - local = socket.getLocalAddress().getHostAddress(); - port = socket.getLocalPort(); - socket.close(); - break; - } - socket.close(); - } - } catch (Exception e) { - // ignore. - } - if (local == null) { - // We are unable to reach the server. Just bail out. - return; - } - - close(); - mLocalIp = local; - - properties.setProperty("javax.sip.STACK_NAME", getStackName()); - properties.setProperty( - "gov.nist.javax.sip.THREAD_POOL_SIZE", THREAD_POOL_SIZE); - mSipStack = SipFactory.getInstance().createSipStack(properties); - try { - SipProvider provider = mSipStack.createSipProvider( - mSipStack.createListeningPoint(local, port, protocol)); - provider.addSipListener(this); - mSipHelper = new SipHelper(mSipStack, provider); - } catch (SipException e) { - throw e; - } catch (Exception e) { - throw new SipException("failed to initialize SIP stack", e); - } - - if (DBG) log("reset: start stack for " + mLocalProfile.getUriString()); - mSipStack.start(); - } - - synchronized void onConnectivityChanged() { - SipSessionImpl[] ss = mSessionMap.values().toArray( - new SipSessionImpl[mSessionMap.size()]); - // Iterate on the copied array instead of directly on mSessionMap to - // avoid ConcurrentModificationException being thrown when - // SipSessionImpl removes itself from mSessionMap in onError() in the - // following loop. - for (SipSessionImpl s : ss) { - s.onError(SipErrorCode.DATA_CONNECTION_LOST, - "data connection lost"); - } - } - - synchronized void resetExternalAddress() { - if (DBG) { - log("resetExternalAddress: " + mSipStack); - } - mExternalIp = null; - mExternalPort = 0; - } - - public SipProfile getLocalProfile() { - return mLocalProfile; - } - - public String getLocalProfileUri() { - return mLocalProfile.getUriString(); - } - - private String getStackName() { - return "stack" + System.currentTimeMillis(); - } - - public synchronized void close() { - if (DBG) log("close: " + mLocalProfile.getUriString()); - onConnectivityChanged(); - mSessionMap.clear(); - closeToNotReceiveCalls(); - if (mSipStack != null) { - mSipStack.stop(); - mSipStack = null; - mSipHelper = null; - } - resetExternalAddress(); - } - - public synchronized boolean isClosed() { - return (mSipStack == null); - } - - // For internal use, require listener not to block in callbacks. - public synchronized void openToReceiveCalls(ISipSessionListener listener) { - if (mCallReceiverSession == null) { - mCallReceiverSession = new SipSessionCallReceiverImpl(listener); - } else { - mCallReceiverSession.setListener(listener); - } - } - - public synchronized void closeToNotReceiveCalls() { - mCallReceiverSession = null; - } - - public ISipSession createSession(ISipSessionListener listener) { - return (isClosed() ? null : new SipSessionImpl(listener)); - } - - synchronized boolean containsSession(String callId) { - return mSessionMap.containsKey(callId); - } - - private synchronized SipSessionImpl getSipSession(EventObject event) { - String key = SipHelper.getCallId(event); - SipSessionImpl session = mSessionMap.get(key); - if ((session != null) && isLoggable(session)) { - if (DBG) log("getSipSession: event=" + key); - if (DBG) log("getSipSession: active sessions:"); - for (String k : mSessionMap.keySet()) { - if (DBG) log("getSipSession: ..." + k + ": " + mSessionMap.get(k)); - } - } - return ((session != null) ? session : mCallReceiverSession); - } - - private synchronized void addSipSession(SipSessionImpl newSession) { - removeSipSession(newSession); - String key = newSession.getCallId(); - mSessionMap.put(key, newSession); - if (isLoggable(newSession)) { - if (DBG) log("addSipSession: key='" + key + "'"); - for (String k : mSessionMap.keySet()) { - if (DBG) log("addSipSession: " + k + ": " + mSessionMap.get(k)); - } - } - } - - private synchronized void removeSipSession(SipSessionImpl session) { - if (session == mCallReceiverSession) return; - String key = session.getCallId(); - SipSessionImpl s = mSessionMap.remove(key); - // sanity check - if ((s != null) && (s != session)) { - if (DBG) log("removeSession: " + session + " is not associated with key '" - + key + "'"); - mSessionMap.put(key, s); - for (Map.Entry<String, SipSessionImpl> entry - : mSessionMap.entrySet()) { - if (entry.getValue() == s) { - key = entry.getKey(); - mSessionMap.remove(key); - } - } - } - - if ((s != null) && isLoggable(s)) { - if (DBG) log("removeSession: " + session + " @key '" + key + "'"); - for (String k : mSessionMap.keySet()) { - if (DBG) log("removeSession: " + k + ": " + mSessionMap.get(k)); - } - } - } - - @Override - public void processRequest(final RequestEvent event) { - if (isRequestEvent(Request.INVITE, event)) { - if (DBG) log("processRequest: mWakeLock.acquire got INVITE, thread:" - + Thread.currentThread()); - // Acquire a wake lock and keep it for WAKE_LOCK_HOLDING_TIME; - // should be large enough to bring up the app. - mWakeLock.acquire(WAKE_LOCK_HOLDING_TIME); - } - process(event); - } - - @Override - public void processResponse(ResponseEvent event) { - process(event); - } - - @Override - public void processIOException(IOExceptionEvent event) { - process(event); - } - - @Override - public void processTimeout(TimeoutEvent event) { - process(event); - } - - @Override - public void processTransactionTerminated(TransactionTerminatedEvent event) { - process(event); - } - - @Override - public void processDialogTerminated(DialogTerminatedEvent event) { - process(event); - } - - private synchronized void process(EventObject event) { - SipSessionImpl session = getSipSession(event); - try { - boolean isLoggable = isLoggable(session, event); - boolean processed = (session != null) && session.process(event); - if (isLoggable && processed) { - log("process: event new state after: " - + SipSession.State.toString(session.mState)); - } - } catch (Throwable e) { - loge("process: error event=" + event, getRootCause(e)); - session.onError(e); - } - } - - private String extractContent(Message message) { - // Currently we do not support secure MIME bodies. - byte[] bytes = message.getRawContent(); - if (bytes != null) { - try { - if (message instanceof SIPMessage) { - return ((SIPMessage) message).getMessageContent(); - } else { - return new String(bytes, "UTF-8"); - } - } catch (UnsupportedEncodingException e) { - } - } - return null; - } - - private void extractExternalAddress(ResponseEvent evt) { - Response response = evt.getResponse(); - ViaHeader viaHeader = (ViaHeader)(response.getHeader( - SIPHeaderNames.VIA)); - if (viaHeader == null) return; - int rport = viaHeader.getRPort(); - String externalIp = viaHeader.getReceived(); - if ((rport > 0) && (externalIp != null)) { - mExternalIp = externalIp; - mExternalPort = rport; - if (DBG) { - log("extractExternalAddress: external addr " + externalIp + ":" + rport - + " on " + mSipStack); - } - } - } - - private Throwable getRootCause(Throwable exception) { - Throwable cause = exception.getCause(); - while (cause != null) { - exception = cause; - cause = exception.getCause(); - } - return exception; - } - - private SipSessionImpl createNewSession(RequestEvent event, - ISipSessionListener listener, ServerTransaction transaction, - int newState) throws SipException { - SipSessionImpl newSession = new SipSessionImpl(listener); - newSession.mServerTransaction = transaction; - newSession.mState = newState; - newSession.mDialog = newSession.mServerTransaction.getDialog(); - newSession.mInviteReceived = event; - newSession.mPeerProfile = createPeerProfile((HeaderAddress) - event.getRequest().getHeader(FromHeader.NAME)); - newSession.mPeerSessionDescription = - extractContent(event.getRequest()); - return newSession; - } - - private class SipSessionCallReceiverImpl extends SipSessionImpl { - private static final String SSCRI_TAG = "SipSessionCallReceiverImpl"; - private static final boolean SSCRI_DBG = true; - - public SipSessionCallReceiverImpl(ISipSessionListener listener) { - super(listener); - } - - private int processInviteWithReplaces(RequestEvent event, - ReplacesHeader replaces) { - String callId = replaces.getCallId(); - SipSessionImpl session = mSessionMap.get(callId); - if (session == null) { - return Response.CALL_OR_TRANSACTION_DOES_NOT_EXIST; - } - - Dialog dialog = session.mDialog; - if (dialog == null) return Response.DECLINE; - - if (!dialog.getLocalTag().equals(replaces.getToTag()) || - !dialog.getRemoteTag().equals(replaces.getFromTag())) { - // No match is found, returns 481. - return Response.CALL_OR_TRANSACTION_DOES_NOT_EXIST; - } - - ReferredByHeader referredBy = (ReferredByHeader) event.getRequest() - .getHeader(ReferredByHeader.NAME); - if ((referredBy == null) || - !dialog.getRemoteParty().equals(referredBy.getAddress())) { - return Response.CALL_OR_TRANSACTION_DOES_NOT_EXIST; - } - return Response.OK; - } - - private void processNewInviteRequest(RequestEvent event) - throws SipException { - ReplacesHeader replaces = (ReplacesHeader) event.getRequest() - .getHeader(ReplacesHeader.NAME); - SipSessionImpl newSession = null; - if (replaces != null) { - int response = processInviteWithReplaces(event, replaces); - if (SSCRI_DBG) { - log("processNewInviteRequest: " + replaces - + " response=" + response); - } - if (response == Response.OK) { - SipSessionImpl replacedSession = - mSessionMap.get(replaces.getCallId()); - // got INVITE w/ replaces request. - newSession = createNewSession(event, - replacedSession.mProxy.getListener(), - mSipHelper.getServerTransaction(event), - SipSession.State.INCOMING_CALL); - newSession.mProxy.onCallTransferring(newSession, - newSession.mPeerSessionDescription); - } else { - mSipHelper.sendResponse(event, response); - } - } else { - // New Incoming call. - newSession = createNewSession(event, mProxy, - mSipHelper.sendRinging(event, generateTag()), - SipSession.State.INCOMING_CALL); - mProxy.onRinging(newSession, newSession.mPeerProfile, - newSession.mPeerSessionDescription); - } - if (newSession != null) addSipSession(newSession); - } - - @Override - public boolean process(EventObject evt) throws SipException { - if (isLoggable(this, evt)) log("process: " + this + ": " - + SipSession.State.toString(mState) + ": processing " - + logEvt(evt)); - if (isRequestEvent(Request.INVITE, evt)) { - processNewInviteRequest((RequestEvent) evt); - return true; - } else if (isRequestEvent(Request.OPTIONS, evt)) { - mSipHelper.sendResponse((RequestEvent) evt, Response.OK); - return true; - } else { - return false; - } - } - - private void log(String s) { - Rlog.d(SSCRI_TAG, s); - } - } - - static interface KeepAliveProcessCallback { - /** Invoked when the response of keeping alive comes back. */ - void onResponse(boolean portChanged); - void onError(int errorCode, String description); - } - - class SipSessionImpl extends ISipSession.Stub { - private static final String SSI_TAG = "SipSessionImpl"; - private static final boolean SSI_DBG = true; - - SipProfile mPeerProfile; - SipSessionListenerProxy mProxy = new SipSessionListenerProxy(); - int mState = SipSession.State.READY_TO_CALL; - RequestEvent mInviteReceived; - Dialog mDialog; - ServerTransaction mServerTransaction; - ClientTransaction mClientTransaction; - String mPeerSessionDescription; - boolean mInCall; - SessionTimer mSessionTimer; - int mAuthenticationRetryCount; - - private SipKeepAlive mSipKeepAlive; - - private SipSessionImpl mSipSessionImpl; - - // the following three members are used for handling refer request. - SipSessionImpl mReferSession; - ReferredByHeader mReferredBy; - String mReplaces; - - // lightweight timer - class SessionTimer { - private boolean mRunning = true; - - void start(final int timeout) { - new Thread(new Runnable() { - @Override - public void run() { - sleep(timeout); - if (mRunning) timeout(); - } - }, "SipSessionTimerThread").start(); - } - - synchronized void cancel() { - mRunning = false; - this.notify(); - } - - private void timeout() { - synchronized (SipSessionGroup.this) { - onError(SipErrorCode.TIME_OUT, "Session timed out!"); - } - } - - private synchronized void sleep(int timeout) { - try { - this.wait(timeout * 1000); - } catch (InterruptedException e) { - loge("session timer interrupted!", e); - } - } - } - - public SipSessionImpl(ISipSessionListener listener) { - setListener(listener); - } - - SipSessionImpl duplicate() { - return new SipSessionImpl(mProxy.getListener()); - } - - private void reset() { - mInCall = false; - removeSipSession(this); - mPeerProfile = null; - mState = SipSession.State.READY_TO_CALL; - mInviteReceived = null; - mPeerSessionDescription = null; - mAuthenticationRetryCount = 0; - mReferSession = null; - mReferredBy = null; - mReplaces = null; - - if (mDialog != null) mDialog.delete(); - mDialog = null; - - try { - if (mServerTransaction != null) mServerTransaction.terminate(); - } catch (ObjectInUseException e) { - // ignored - } - mServerTransaction = null; - - try { - if (mClientTransaction != null) mClientTransaction.terminate(); - } catch (ObjectInUseException e) { - // ignored - } - mClientTransaction = null; - - cancelSessionTimer(); - - if (mSipSessionImpl != null) { - mSipSessionImpl.stopKeepAliveProcess(); - mSipSessionImpl = null; - } - } - - @Override - public boolean isInCall() { - return mInCall; - } - - @Override - public String getLocalIp() { - return mLocalIp; - } - - @Override - public SipProfile getLocalProfile() { - return mLocalProfile; - } - - @Override - public SipProfile getPeerProfile() { - return mPeerProfile; - } - - @Override - public String getCallId() { - return SipHelper.getCallId(getTransaction()); - } - - private Transaction getTransaction() { - if (mClientTransaction != null) return mClientTransaction; - if (mServerTransaction != null) return mServerTransaction; - return null; - } - - @Override - public int getState() { - return mState; - } - - @Override - public void setListener(ISipSessionListener listener) { - mProxy.setListener((listener instanceof SipSessionListenerProxy) - ? ((SipSessionListenerProxy) listener).getListener() - : listener); - } - - // process the command in a new thread - private void doCommandAsync(final EventObject command) { - new Thread(new Runnable() { - @Override - public void run() { - try { - processCommand(command); - } catch (Throwable e) { - loge("command error: " + command + ": " - + mLocalProfile.getUriString(), - getRootCause(e)); - onError(e); - } - } - }, "SipSessionAsyncCmdThread").start(); - } - - @Override - public void makeCall(SipProfile peerProfile, String sessionDescription, - int timeout) { - doCommandAsync(new MakeCallCommand(peerProfile, sessionDescription, - timeout)); - } - - @Override - public void answerCall(String sessionDescription, int timeout) { - synchronized (SipSessionGroup.this) { - if (mPeerProfile == null) return; - doCommandAsync(new MakeCallCommand(mPeerProfile, - sessionDescription, timeout)); - } - } - - @Override - public void endCall() { - doCommandAsync(END_CALL); - } - - @Override - public void changeCall(String sessionDescription, int timeout) { - synchronized (SipSessionGroup.this) { - if (mPeerProfile == null) return; - doCommandAsync(new MakeCallCommand(mPeerProfile, - sessionDescription, timeout)); - } - } - - @Override - public void register(int duration) { - doCommandAsync(new RegisterCommand(duration)); - } - - @Override - public void unregister() { - doCommandAsync(DEREGISTER); - } - - private void processCommand(EventObject command) throws SipException { - if (isLoggable(command)) log("process cmd: " + command); - if (!process(command)) { - onError(SipErrorCode.IN_PROGRESS, - "cannot initiate a new transaction to execute: " - + command); - } - } - - protected String generateTag() { - // 32-bit randomness - return String.valueOf((long) (Math.random() * 0x100000000L)); - } - - @Override - public String toString() { - try { - String s = super.toString(); - return s.substring(s.indexOf("@")) + ":" - + SipSession.State.toString(mState); - } catch (Throwable e) { - return super.toString(); - } - } - - public boolean process(EventObject evt) throws SipException { - if (isLoggable(this, evt)) log(" ~~~~~ " + this + ": " - + SipSession.State.toString(mState) + ": processing " - + logEvt(evt)); - synchronized (SipSessionGroup.this) { - if (isClosed()) return false; - - if (mSipKeepAlive != null) { - // event consumed by keepalive process - if (mSipKeepAlive.process(evt)) return true; - } - - Dialog dialog = null; - if (evt instanceof RequestEvent) { - dialog = ((RequestEvent) evt).getDialog(); - } else if (evt instanceof ResponseEvent) { - dialog = ((ResponseEvent) evt).getDialog(); - extractExternalAddress((ResponseEvent) evt); - } - if (dialog != null) mDialog = dialog; - - boolean processed; - - switch (mState) { - case SipSession.State.REGISTERING: - case SipSession.State.DEREGISTERING: - processed = registeringToReady(evt); - break; - case SipSession.State.READY_TO_CALL: - processed = readyForCall(evt); - break; - case SipSession.State.INCOMING_CALL: - processed = incomingCall(evt); - break; - case SipSession.State.INCOMING_CALL_ANSWERING: - processed = incomingCallToInCall(evt); - break; - case SipSession.State.OUTGOING_CALL: - case SipSession.State.OUTGOING_CALL_RING_BACK: - processed = outgoingCall(evt); - break; - case SipSession.State.OUTGOING_CALL_CANCELING: - processed = outgoingCallToReady(evt); - break; - case SipSession.State.IN_CALL: - processed = inCall(evt); - break; - case SipSession.State.ENDING_CALL: - processed = endingCall(evt); - break; - default: - processed = false; - } - return (processed || processExceptions(evt)); - } - } - - private boolean processExceptions(EventObject evt) throws SipException { - if (isRequestEvent(Request.BYE, evt)) { - // terminate the call whenever a BYE is received - mSipHelper.sendResponse((RequestEvent) evt, Response.OK); - endCallNormally(); - return true; - } else if (isRequestEvent(Request.CANCEL, evt)) { - mSipHelper.sendResponse((RequestEvent) evt, - Response.CALL_OR_TRANSACTION_DOES_NOT_EXIST); - return true; - } else if (evt instanceof TransactionTerminatedEvent) { - if (isCurrentTransaction((TransactionTerminatedEvent) evt)) { - if (evt instanceof TimeoutEvent) { - processTimeout((TimeoutEvent) evt); - } else { - processTransactionTerminated( - (TransactionTerminatedEvent) evt); - } - return true; - } - } else if (isRequestEvent(Request.OPTIONS, evt)) { - mSipHelper.sendResponse((RequestEvent) evt, Response.OK); - return true; - } else if (evt instanceof DialogTerminatedEvent) { - processDialogTerminated((DialogTerminatedEvent) evt); - return true; - } - return false; - } - - private void processDialogTerminated(DialogTerminatedEvent event) { - if (mDialog == event.getDialog()) { - onError(new SipException("dialog terminated")); - } else { - if (SSI_DBG) log("not the current dialog; current=" + mDialog - + ", terminated=" + event.getDialog()); - } - } - - private boolean isCurrentTransaction(TransactionTerminatedEvent event) { - Transaction current = event.isServerTransaction() - ? mServerTransaction - : mClientTransaction; - Transaction target = event.isServerTransaction() - ? event.getServerTransaction() - : event.getClientTransaction(); - - if ((current != target) && (mState != SipSession.State.PINGING)) { - if (SSI_DBG) log("not the current transaction; current=" - + toString(current) + ", target=" + toString(target)); - return false; - } else if (current != null) { - if (SSI_DBG) log("transaction terminated: " + toString(current)); - return true; - } else { - // no transaction; shouldn't be here; ignored - return true; - } - } - - private String toString(Transaction transaction) { - if (transaction == null) return "null"; - Request request = transaction.getRequest(); - Dialog dialog = transaction.getDialog(); - CSeqHeader cseq = (CSeqHeader) request.getHeader(CSeqHeader.NAME); - return String.format("req=%s,%s,s=%s,ds=%s,", request.getMethod(), - cseq.getSeqNumber(), transaction.getState(), - ((dialog == null) ? "-" : dialog.getState())); - } - - private void processTransactionTerminated( - TransactionTerminatedEvent event) { - switch (mState) { - case SipSession.State.IN_CALL: - case SipSession.State.READY_TO_CALL: - if (SSI_DBG) log("Transaction terminated; do nothing"); - break; - default: - if (SSI_DBG) log("Transaction terminated early: " + this); - onError(SipErrorCode.TRANSACTION_TERMINTED, - "transaction terminated"); - } - } - - private void processTimeout(TimeoutEvent event) { - if (SSI_DBG) log("processing Timeout..."); - switch (mState) { - case SipSession.State.REGISTERING: - case SipSession.State.DEREGISTERING: - reset(); - mProxy.onRegistrationTimeout(this); - break; - case SipSession.State.INCOMING_CALL: - case SipSession.State.INCOMING_CALL_ANSWERING: - case SipSession.State.OUTGOING_CALL: - case SipSession.State.OUTGOING_CALL_CANCELING: - onError(SipErrorCode.TIME_OUT, event.toString()); - break; - - default: - if (SSI_DBG) log(" do nothing"); - break; - } - } - - private int getExpiryTime(Response response) { - int time = -1; - ContactHeader contact = (ContactHeader) response.getHeader(ContactHeader.NAME); - if (contact != null) { - time = contact.getExpires(); - } - ExpiresHeader expires = (ExpiresHeader) response.getHeader(ExpiresHeader.NAME); - if (expires != null && (time < 0 || time > expires.getExpires())) { - time = expires.getExpires(); - } - if (time <= 0) { - time = EXPIRY_TIME; - } - expires = (ExpiresHeader) response.getHeader(MinExpiresHeader.NAME); - if (expires != null && time < expires.getExpires()) { - time = expires.getExpires(); - } - if (SSI_DBG) { - log("Expiry time = " + time); - } - return time; - } - - private boolean registeringToReady(EventObject evt) - throws SipException { - if (expectResponse(Request.REGISTER, evt)) { - ResponseEvent event = (ResponseEvent) evt; - Response response = event.getResponse(); - - int statusCode = response.getStatusCode(); - switch (statusCode) { - case Response.OK: - int state = mState; - onRegistrationDone((state == SipSession.State.REGISTERING) - ? getExpiryTime(((ResponseEvent) evt).getResponse()) - : -1); - return true; - case Response.UNAUTHORIZED: - case Response.PROXY_AUTHENTICATION_REQUIRED: - handleAuthentication(event); - return true; - default: - if (statusCode >= 500) { - onRegistrationFailed(response); - return true; - } - } - } - return false; - } - - private boolean handleAuthentication(ResponseEvent event) - throws SipException { - Response response = event.getResponse(); - String nonce = getNonceFromResponse(response); - if (nonce == null) { - onError(SipErrorCode.SERVER_ERROR, - "server does not provide challenge"); - return false; - } else if (mAuthenticationRetryCount < 2) { - mClientTransaction = mSipHelper.handleChallenge( - event, getAccountManager()); - mDialog = mClientTransaction.getDialog(); - mAuthenticationRetryCount++; - if (isLoggable(this, event)) { - if (SSI_DBG) log(" authentication retry count=" - + mAuthenticationRetryCount); - } - return true; - } else { - if (crossDomainAuthenticationRequired(response)) { - onError(SipErrorCode.CROSS_DOMAIN_AUTHENTICATION, - getRealmFromResponse(response)); - } else { - onError(SipErrorCode.INVALID_CREDENTIALS, - "incorrect username or password"); - } - return false; - } - } - - private boolean crossDomainAuthenticationRequired(Response response) { - String realm = getRealmFromResponse(response); - if (realm == null) realm = ""; - return !mLocalProfile.getSipDomain().trim().equals(realm.trim()); - } - - private AccountManager getAccountManager() { - return new AccountManager() { - @Override - public UserCredentials getCredentials(ClientTransaction - challengedTransaction, String realm) { - return new UserCredentials() { - @Override - public String getUserName() { - String username = mLocalProfile.getAuthUserName(); - return (!TextUtils.isEmpty(username) ? username : - mLocalProfile.getUserName()); - } - - @Override - public String getPassword() { - return mPassword; - } - - @Override - public String getSipDomain() { - return mLocalProfile.getSipDomain(); - } - }; - } - }; - } - - private String getRealmFromResponse(Response response) { - WWWAuthenticate wwwAuth = (WWWAuthenticate)response.getHeader( - SIPHeaderNames.WWW_AUTHENTICATE); - if (wwwAuth != null) return wwwAuth.getRealm(); - ProxyAuthenticate proxyAuth = (ProxyAuthenticate)response.getHeader( - SIPHeaderNames.PROXY_AUTHENTICATE); - return (proxyAuth == null) ? null : proxyAuth.getRealm(); - } - - private String getNonceFromResponse(Response response) { - WWWAuthenticate wwwAuth = (WWWAuthenticate)response.getHeader( - SIPHeaderNames.WWW_AUTHENTICATE); - if (wwwAuth != null) return wwwAuth.getNonce(); - ProxyAuthenticate proxyAuth = (ProxyAuthenticate)response.getHeader( - SIPHeaderNames.PROXY_AUTHENTICATE); - return (proxyAuth == null) ? null : proxyAuth.getNonce(); - } - - private String getResponseString(int statusCode) { - StatusLine statusLine = new StatusLine(); - statusLine.setStatusCode(statusCode); - statusLine.setReasonPhrase(SIPResponse.getReasonPhrase(statusCode)); - return statusLine.encode(); - } - - private boolean readyForCall(EventObject evt) throws SipException { - // expect MakeCallCommand, RegisterCommand, DEREGISTER - if (evt instanceof MakeCallCommand) { - mState = SipSession.State.OUTGOING_CALL; - MakeCallCommand cmd = (MakeCallCommand) evt; - mPeerProfile = cmd.getPeerProfile(); - if (mReferSession != null) { - mSipHelper.sendReferNotify(mReferSession.mDialog, - getResponseString(Response.TRYING)); - } - mClientTransaction = mSipHelper.sendInvite( - mLocalProfile, mPeerProfile, cmd.getSessionDescription(), - generateTag(), mReferredBy, mReplaces); - mDialog = mClientTransaction.getDialog(); - addSipSession(this); - startSessionTimer(cmd.getTimeout()); - mProxy.onCalling(this); - return true; - } else if (evt instanceof RegisterCommand) { - mState = SipSession.State.REGISTERING; - int duration = ((RegisterCommand) evt).getDuration(); - mClientTransaction = mSipHelper.sendRegister(mLocalProfile, - generateTag(), duration); - mDialog = mClientTransaction.getDialog(); - addSipSession(this); - mProxy.onRegistering(this); - return true; - } else if (DEREGISTER == evt) { - mState = SipSession.State.DEREGISTERING; - mClientTransaction = mSipHelper.sendRegister(mLocalProfile, - generateTag(), 0); - mDialog = mClientTransaction.getDialog(); - addSipSession(this); - mProxy.onRegistering(this); - return true; - } - return false; - } - - private boolean incomingCall(EventObject evt) throws SipException { - // expect MakeCallCommand(answering) , END_CALL cmd , Cancel - if (evt instanceof MakeCallCommand) { - // answer call - mState = SipSession.State.INCOMING_CALL_ANSWERING; - mServerTransaction = mSipHelper.sendInviteOk(mInviteReceived, - mLocalProfile, - ((MakeCallCommand) evt).getSessionDescription(), - mServerTransaction, - mExternalIp, mExternalPort); - startSessionTimer(((MakeCallCommand) evt).getTimeout()); - return true; - } else if (END_CALL == evt) { - mSipHelper.sendInviteBusyHere(mInviteReceived, - mServerTransaction); - endCallNormally(); - return true; - } else if (isRequestEvent(Request.CANCEL, evt)) { - RequestEvent event = (RequestEvent) evt; - mSipHelper.sendResponse(event, Response.OK); - mSipHelper.sendInviteRequestTerminated( - mInviteReceived.getRequest(), mServerTransaction); - endCallNormally(); - return true; - } - return false; - } - - private boolean incomingCallToInCall(EventObject evt) { - // expect ACK, CANCEL request - if (isRequestEvent(Request.ACK, evt)) { - String sdp = extractContent(((RequestEvent) evt).getRequest()); - if (sdp != null) mPeerSessionDescription = sdp; - if (mPeerSessionDescription == null) { - onError(SipErrorCode.CLIENT_ERROR, "peer sdp is empty"); - } else { - establishCall(false); - } - return true; - } else if (isRequestEvent(Request.CANCEL, evt)) { - // http://tools.ietf.org/html/rfc3261#section-9.2 - // Final response has been sent; do nothing here. - return true; - } - return false; - } - - private boolean outgoingCall(EventObject evt) throws SipException { - if (expectResponse(Request.INVITE, evt)) { - ResponseEvent event = (ResponseEvent) evt; - Response response = event.getResponse(); - - int statusCode = response.getStatusCode(); - switch (statusCode) { - case Response.RINGING: - case Response.CALL_IS_BEING_FORWARDED: - case Response.QUEUED: - case Response.SESSION_PROGRESS: - // feedback any provisional responses (except TRYING) as - // ring back for better UX - if (mState == SipSession.State.OUTGOING_CALL) { - mState = SipSession.State.OUTGOING_CALL_RING_BACK; - cancelSessionTimer(); - mProxy.onRingingBack(this); - } - return true; - case Response.OK: - if (mReferSession != null) { - mSipHelper.sendReferNotify(mReferSession.mDialog, - getResponseString(Response.OK)); - // since we don't need to remember the session anymore. - mReferSession = null; - } - mSipHelper.sendInviteAck(event, mDialog); - mPeerSessionDescription = extractContent(response); - establishCall(true); - return true; - case Response.UNAUTHORIZED: - case Response.PROXY_AUTHENTICATION_REQUIRED: - if (handleAuthentication(event)) { - addSipSession(this); - } - return true; - case Response.REQUEST_PENDING: - // TODO: rfc3261#section-14.1; re-schedule invite - return true; - default: - if (mReferSession != null) { - mSipHelper.sendReferNotify(mReferSession.mDialog, - getResponseString(Response.SERVICE_UNAVAILABLE)); - } - if (statusCode >= 400) { - // error: an ack is sent automatically by the stack - onError(response); - return true; - } else if (statusCode >= 300) { - // TODO: handle 3xx (redirect) - } else { - return true; - } - } - return false; - } else if (END_CALL == evt) { - // RFC says that UA should not send out cancel when no - // response comes back yet. We are cheating for not checking - // response. - mState = SipSession.State.OUTGOING_CALL_CANCELING; - mSipHelper.sendCancel(mClientTransaction); - startSessionTimer(CANCEL_CALL_TIMER); - return true; - } else if (isRequestEvent(Request.INVITE, evt)) { - // Call self? Send BUSY HERE so server may redirect the call to - // voice mailbox. - RequestEvent event = (RequestEvent) evt; - mSipHelper.sendInviteBusyHere(event, - event.getServerTransaction()); - return true; - } - return false; - } - - private boolean outgoingCallToReady(EventObject evt) - throws SipException { - if (evt instanceof ResponseEvent) { - ResponseEvent event = (ResponseEvent) evt; - Response response = event.getResponse(); - int statusCode = response.getStatusCode(); - if (expectResponse(Request.CANCEL, evt)) { - if (statusCode == Response.OK) { - // do nothing; wait for REQUEST_TERMINATED - return true; - } - } else if (expectResponse(Request.INVITE, evt)) { - switch (statusCode) { - case Response.OK: - outgoingCall(evt); // abort Cancel - return true; - case Response.REQUEST_TERMINATED: - endCallNormally(); - return true; - } - } else { - return false; - } - - if (statusCode >= 400) { - onError(response); - return true; - } - } else if (evt instanceof TransactionTerminatedEvent) { - // rfc3261#section-14.1: - // if re-invite gets timed out, terminate the dialog; but - // re-invite is not reliable, just let it go and pretend - // nothing happened. - onError(new SipException("timed out")); - } - return false; - } - - private boolean processReferRequest(RequestEvent event) - throws SipException { - try { - ReferToHeader referto = (ReferToHeader) event.getRequest() - .getHeader(ReferTo.NAME); - Address address = referto.getAddress(); - SipURI uri = (SipURI) address.getURI(); - String replacesHeader = uri.getHeader(ReplacesHeader.NAME); - String username = uri.getUser(); - if (username == null) { - mSipHelper.sendResponse(event, Response.BAD_REQUEST); - return false; - } - // send notify accepted - mSipHelper.sendResponse(event, Response.ACCEPTED); - SipSessionImpl newSession = createNewSession(event, - this.mProxy.getListener(), - mSipHelper.getServerTransaction(event), - SipSession.State.READY_TO_CALL); - newSession.mReferSession = this; - newSession.mReferredBy = (ReferredByHeader) event.getRequest() - .getHeader(ReferredByHeader.NAME); - newSession.mReplaces = replacesHeader; - newSession.mPeerProfile = createPeerProfile(referto); - newSession.mProxy.onCallTransferring(newSession, - null); - return true; - } catch (IllegalArgumentException e) { - throw new SipException("createPeerProfile()", e); - } - } - - private boolean inCall(EventObject evt) throws SipException { - // expect END_CALL cmd, BYE request, hold call (MakeCallCommand) - // OK retransmission is handled in SipStack - if (END_CALL == evt) { - // rfc3261#section-15.1.1 - mState = SipSession.State.ENDING_CALL; - mSipHelper.sendBye(mDialog); - mProxy.onCallEnded(this); - startSessionTimer(END_CALL_TIMER); - return true; - } else if (isRequestEvent(Request.INVITE, evt)) { - // got Re-INVITE - mState = SipSession.State.INCOMING_CALL; - RequestEvent event = mInviteReceived = (RequestEvent) evt; - mPeerSessionDescription = extractContent(event.getRequest()); - mServerTransaction = null; - mProxy.onRinging(this, mPeerProfile, mPeerSessionDescription); - return true; - } else if (isRequestEvent(Request.BYE, evt)) { - mSipHelper.sendResponse((RequestEvent) evt, Response.OK); - endCallNormally(); - return true; - } else if (isRequestEvent(Request.REFER, evt)) { - return processReferRequest((RequestEvent) evt); - } else if (evt instanceof MakeCallCommand) { - // to change call - mState = SipSession.State.OUTGOING_CALL; - mClientTransaction = mSipHelper.sendReinvite(mDialog, - ((MakeCallCommand) evt).getSessionDescription()); - startSessionTimer(((MakeCallCommand) evt).getTimeout()); - return true; - } else if (evt instanceof ResponseEvent) { - if (expectResponse(Request.NOTIFY, evt)) return true; - } - return false; - } - - private boolean endingCall(EventObject evt) throws SipException { - if (expectResponse(Request.BYE, evt)) { - ResponseEvent event = (ResponseEvent) evt; - Response response = event.getResponse(); - - int statusCode = response.getStatusCode(); - switch (statusCode) { - case Response.UNAUTHORIZED: - case Response.PROXY_AUTHENTICATION_REQUIRED: - if (handleAuthentication(event)) { - return true; - } else { - // can't authenticate; pass through to end session - } - } - cancelSessionTimer(); - reset(); - return true; - } - return false; - } - - // timeout in seconds - private void startSessionTimer(int timeout) { - if (timeout > 0) { - mSessionTimer = new SessionTimer(); - mSessionTimer.start(timeout); - } - } - - private void cancelSessionTimer() { - if (mSessionTimer != null) { - mSessionTimer.cancel(); - mSessionTimer = null; - } - } - - private String createErrorMessage(Response response) { - return String.format("%s (%d)", response.getReasonPhrase(), - response.getStatusCode()); - } - - private void enableKeepAlive() { - if (mSipSessionImpl != null) { - mSipSessionImpl.stopKeepAliveProcess(); - } else { - mSipSessionImpl = duplicate(); - } - try { - mSipSessionImpl.startKeepAliveProcess( - INCALL_KEEPALIVE_INTERVAL, mPeerProfile, null); - } catch (SipException e) { - loge("keepalive cannot be enabled; ignored", e); - mSipSessionImpl.stopKeepAliveProcess(); - } - } - - private void establishCall(boolean enableKeepAlive) { - mState = SipSession.State.IN_CALL; - cancelSessionTimer(); - if (!mInCall && enableKeepAlive) enableKeepAlive(); - mInCall = true; - mProxy.onCallEstablished(this, mPeerSessionDescription); - } - - private void endCallNormally() { - reset(); - mProxy.onCallEnded(this); - } - - private void endCallOnError(int errorCode, String message) { - reset(); - mProxy.onError(this, errorCode, message); - } - - private void endCallOnBusy() { - reset(); - mProxy.onCallBusy(this); - } - - private void onError(int errorCode, String message) { - cancelSessionTimer(); - switch (mState) { - case SipSession.State.REGISTERING: - case SipSession.State.DEREGISTERING: - onRegistrationFailed(errorCode, message); - break; - default: - endCallOnError(errorCode, message); - } - } - - - private void onError(Throwable exception) { - exception = getRootCause(exception); - onError(getErrorCode(exception), exception.toString()); - } - - private void onError(Response response) { - int statusCode = response.getStatusCode(); - if (!mInCall && (statusCode == Response.BUSY_HERE)) { - endCallOnBusy(); - } else { - onError(getErrorCode(statusCode), createErrorMessage(response)); - } - } - - private int getErrorCode(int responseStatusCode) { - switch (responseStatusCode) { - case Response.TEMPORARILY_UNAVAILABLE: - case Response.FORBIDDEN: - case Response.GONE: - case Response.NOT_FOUND: - case Response.NOT_ACCEPTABLE: - case Response.NOT_ACCEPTABLE_HERE: - return SipErrorCode.PEER_NOT_REACHABLE; - - case Response.REQUEST_URI_TOO_LONG: - case Response.ADDRESS_INCOMPLETE: - case Response.AMBIGUOUS: - return SipErrorCode.INVALID_REMOTE_URI; - - case Response.REQUEST_TIMEOUT: - return SipErrorCode.TIME_OUT; - - default: - if (responseStatusCode < 500) { - return SipErrorCode.CLIENT_ERROR; - } else { - return SipErrorCode.SERVER_ERROR; - } - } - } - - private int getErrorCode(Throwable exception) { - String message = exception.getMessage(); - if (exception instanceof UnknownHostException) { - return SipErrorCode.SERVER_UNREACHABLE; - } else if (exception instanceof IOException) { - return SipErrorCode.SOCKET_ERROR; - } else { - return SipErrorCode.CLIENT_ERROR; - } - } - - private void onRegistrationDone(int duration) { - reset(); - mProxy.onRegistrationDone(this, duration); - } - - private void onRegistrationFailed(int errorCode, String message) { - reset(); - mProxy.onRegistrationFailed(this, errorCode, message); - } - - private void onRegistrationFailed(Response response) { - int statusCode = response.getStatusCode(); - onRegistrationFailed(getErrorCode(statusCode), - createErrorMessage(response)); - } - - // Notes: SipSessionListener will be replaced by the keepalive process - // @param interval in seconds - public void startKeepAliveProcess(int interval, - KeepAliveProcessCallback callback) throws SipException { - synchronized (SipSessionGroup.this) { - startKeepAliveProcess(interval, mLocalProfile, callback); - } - } - - // Notes: SipSessionListener will be replaced by the keepalive process - // @param interval in seconds - public void startKeepAliveProcess(int interval, SipProfile peerProfile, - KeepAliveProcessCallback callback) throws SipException { - synchronized (SipSessionGroup.this) { - if (mSipKeepAlive != null) { - throw new SipException("Cannot create more than one " - + "keepalive process in a SipSession"); - } - mPeerProfile = peerProfile; - mSipKeepAlive = new SipKeepAlive(); - mProxy.setListener(mSipKeepAlive); - mSipKeepAlive.start(interval, callback); - } - } - - public void stopKeepAliveProcess() { - synchronized (SipSessionGroup.this) { - if (mSipKeepAlive != null) { - mSipKeepAlive.stop(); - mSipKeepAlive = null; - } - } - } - - class SipKeepAlive extends SipSessionAdapter implements Runnable { - private static final String SKA_TAG = "SipKeepAlive"; - private static final boolean SKA_DBG = true; - - private boolean mRunning = false; - private KeepAliveProcessCallback mCallback; - - private boolean mPortChanged = false; - private int mRPort = 0; - private int mInterval; // just for debugging - - // @param interval in seconds - void start(int interval, KeepAliveProcessCallback callback) { - if (mRunning) return; - mRunning = true; - mInterval = interval; - mCallback = new KeepAliveProcessCallbackProxy(callback); - mWakeupTimer.set(interval * 1000, this); - if (SKA_DBG) { - log("start keepalive:" - + mLocalProfile.getUriString()); - } - - // No need to run the first time in a separate thread for now - run(); - } - - // return true if the event is consumed - boolean process(EventObject evt) { - if (mRunning && (mState == SipSession.State.PINGING)) { - if (evt instanceof ResponseEvent) { - if (parseOptionsResult(evt)) { - if (mPortChanged) { - resetExternalAddress(); - stop(); - } else { - cancelSessionTimer(); - removeSipSession(SipSessionImpl.this); - } - mCallback.onResponse(mPortChanged); - return true; - } - } - } - return false; - } - - // SipSessionAdapter - // To react to the session timeout event and network error. - @Override - public void onError(ISipSession session, int errorCode, String message) { - stop(); - mCallback.onError(errorCode, message); - } - - // SipWakeupTimer timeout handler - // To send out keepalive message. - @Override - public void run() { - synchronized (SipSessionGroup.this) { - if (!mRunning) return; - - if (DBG_PING) { - String peerUri = (mPeerProfile == null) - ? "null" - : mPeerProfile.getUriString(); - log("keepalive: " + mLocalProfile.getUriString() - + " --> " + peerUri + ", interval=" + mInterval); - } - try { - sendKeepAlive(); - } catch (Throwable t) { - if (SKA_DBG) { - loge("keepalive error: " - + mLocalProfile.getUriString(), getRootCause(t)); - } - // It's possible that the keepalive process is being stopped - // during session.sendKeepAlive() so need to check mRunning - // again here. - if (mRunning) SipSessionImpl.this.onError(t); - } - } - } - - void stop() { - synchronized (SipSessionGroup.this) { - if (SKA_DBG) { - log("stop keepalive:" + mLocalProfile.getUriString() - + ",RPort=" + mRPort); - } - mRunning = false; - mWakeupTimer.cancel(this); - reset(); - } - } - - private void sendKeepAlive() throws SipException { - synchronized (SipSessionGroup.this) { - mState = SipSession.State.PINGING; - mClientTransaction = mSipHelper.sendOptions( - mLocalProfile, mPeerProfile, generateTag()); - mDialog = mClientTransaction.getDialog(); - addSipSession(SipSessionImpl.this); - - startSessionTimer(KEEPALIVE_TIMEOUT); - // when timed out, onError() will be called with SipErrorCode.TIME_OUT - } - } - - private boolean parseOptionsResult(EventObject evt) { - if (expectResponse(Request.OPTIONS, evt)) { - ResponseEvent event = (ResponseEvent) evt; - int rPort = getRPortFromResponse(event.getResponse()); - if (rPort != -1) { - if (mRPort == 0) mRPort = rPort; - if (mRPort != rPort) { - mPortChanged = true; - if (SKA_DBG) log(String.format( - "rport is changed: %d <> %d", mRPort, rPort)); - mRPort = rPort; - } else { - if (SKA_DBG) log("rport is the same: " + rPort); - } - } else { - if (SKA_DBG) log("peer did not respond rport"); - } - return true; - } - return false; - } - - private int getRPortFromResponse(Response response) { - ViaHeader viaHeader = (ViaHeader)(response.getHeader( - SIPHeaderNames.VIA)); - return (viaHeader == null) ? -1 : viaHeader.getRPort(); - } - - private void log(String s) { - Rlog.d(SKA_TAG, s); - } - } - - private void log(String s) { - Rlog.d(SSI_TAG, s); - } - } - - /** - * @return true if the event is a request event matching the specified - * method; false otherwise - */ - private static boolean isRequestEvent(String method, EventObject event) { - try { - if (event instanceof RequestEvent) { - RequestEvent requestEvent = (RequestEvent) event; - return method.equals(requestEvent.getRequest().getMethod()); - } - } catch (Throwable e) { - } - return false; - } - - private static String getCseqMethod(Message message) { - return ((CSeqHeader) message.getHeader(CSeqHeader.NAME)).getMethod(); - } - - /** - * @return true if the event is a response event and the CSeqHeader method - * match the given arguments; false otherwise - */ - private static boolean expectResponse( - String expectedMethod, EventObject evt) { - if (evt instanceof ResponseEvent) { - ResponseEvent event = (ResponseEvent) evt; - Response response = event.getResponse(); - return expectedMethod.equalsIgnoreCase(getCseqMethod(response)); - } - return false; - } - - private static SipProfile createPeerProfile(HeaderAddress header) - throws SipException { - try { - Address address = header.getAddress(); - SipURI uri = (SipURI) address.getURI(); - String username = uri.getUser(); - if (username == null) username = ANONYMOUS; - int port = uri.getPort(); - SipProfile.Builder builder = - new SipProfile.Builder(username, uri.getHost()) - .setDisplayName(address.getDisplayName()); - if (port > 0) builder.setPort(port); - return builder.build(); - } catch (IllegalArgumentException e) { - throw new SipException("createPeerProfile()", e); - } catch (ParseException e) { - throw new SipException("createPeerProfile()", e); - } - } - - private static boolean isLoggable(SipSessionImpl s) { - if (s != null) { - switch (s.mState) { - case SipSession.State.PINGING: - return DBG_PING; - } - } - return DBG; - } - - private static boolean isLoggable(EventObject evt) { - return isLoggable(null, evt); - } - - private static boolean isLoggable(SipSessionImpl s, EventObject evt) { - if (!isLoggable(s)) return false; - if (evt == null) return false; - - if (evt instanceof ResponseEvent) { - Response response = ((ResponseEvent) evt).getResponse(); - if (Request.OPTIONS.equals(response.getHeader(CSeqHeader.NAME))) { - return DBG_PING; - } - return DBG; - } else if (evt instanceof RequestEvent) { - if (isRequestEvent(Request.OPTIONS, evt)) { - return DBG_PING; - } - return DBG; - } - return false; - } - - private static String logEvt(EventObject evt) { - if (evt instanceof RequestEvent) { - return ((RequestEvent) evt).getRequest().toString(); - } else if (evt instanceof ResponseEvent) { - return ((ResponseEvent) evt).getResponse().toString(); - } else { - return evt.toString(); - } - } - - private class RegisterCommand extends EventObject { - private int mDuration; - - public RegisterCommand(int duration) { - super(SipSessionGroup.this); - mDuration = duration; - } - - public int getDuration() { - return mDuration; - } - } - - private class MakeCallCommand extends EventObject { - private String mSessionDescription; - private int mTimeout; // in seconds - - public MakeCallCommand(SipProfile peerProfile, - String sessionDescription, int timeout) { - super(peerProfile); - mSessionDescription = sessionDescription; - mTimeout = timeout; - } - - public SipProfile getPeerProfile() { - return (SipProfile) getSource(); - } - - public String getSessionDescription() { - return mSessionDescription; - } - - public int getTimeout() { - return mTimeout; - } - } - - /** Class to help safely run KeepAliveProcessCallback in a different thread. */ - static class KeepAliveProcessCallbackProxy implements KeepAliveProcessCallback { - private static final String KAPCP_TAG = "KeepAliveProcessCallbackProxy"; - private KeepAliveProcessCallback mCallback; - - KeepAliveProcessCallbackProxy(KeepAliveProcessCallback callback) { - mCallback = callback; - } - - private void proxy(Runnable runnable) { - // One thread for each calling back. - // Note: Guarantee ordering if the issue becomes important. Currently, - // the chance of handling two callback events at a time is none. - new Thread(runnable, "SIP-KeepAliveProcessCallbackThread").start(); - } - - @Override - public void onResponse(final boolean portChanged) { - if (mCallback == null) return; - proxy(new Runnable() { - @Override - public void run() { - try { - mCallback.onResponse(portChanged); - } catch (Throwable t) { - loge("onResponse", t); - } - } - }); - } - - @Override - public void onError(final int errorCode, final String description) { - if (mCallback == null) return; - proxy(new Runnable() { - @Override - public void run() { - try { - mCallback.onError(errorCode, description); - } catch (Throwable t) { - loge("onError", t); - } - } - }); - } - - private void loge(String s, Throwable t) { - Rlog.e(KAPCP_TAG, s, t); - } - } - - private void log(String s) { - Rlog.d(TAG, s); - } - - private void loge(String s, Throwable t) { - Rlog.e(TAG, s, t); - } -} |