Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 307f15fa authored by repo sync's avatar repo sync
Browse files

Add REFER handling.

Handle REFER requests including REFER with Replaces header.

bug:4958680
Change-Id: I96df95097b78bed67ab8abd309a1e57a45c6bc2f
parent 2d6bb338
Loading
Loading
Loading
Loading
+8 −3
Original line number Diff line number Diff line
@@ -57,6 +57,7 @@ public class SipAudioCall {
    private static final boolean RELEASE_SOCKET = true;
    private static final boolean DONT_RELEASE_SOCKET = false;
    private static final int SESSION_TIMEOUT = 5; // in seconds
    private static final int TRANSFER_TIMEOUT = 15; // in seconds

    /** Listener for events relating to a SIP call, such as when a call is being
     * recieved ("on ringing") or a call is outgoing ("on calling").
@@ -537,10 +538,14 @@ public class SipAudioCall {
                Log.v(TAG, "onCallTransferring mSipSession:"
                        + mSipSession + " newSession:" + newSession);
                mTransferringSession = newSession;
                // session changing request
                try {
                    if (sessionDescription == null) {
                        newSession.makeCall(newSession.getPeerProfile(),
                                createOffer().encode(), TRANSFER_TIMEOUT);
                    } else {
                        String answer = createAnswer(sessionDescription).encode();
                        newSession.answerCall(answer, SESSION_TIMEOUT);
                    }
                } catch (Throwable e) {
                    Log.e(TAG, "onCallTransferring()", e);
                    newSession.endCall();
+29 −3
Original line number Diff line number Diff line
@@ -19,6 +19,9 @@ package com.android.server.sip;
import gov.nist.javax.sip.SipStackExt;
import gov.nist.javax.sip.clientauthutils.AccountManager;
import gov.nist.javax.sip.clientauthutils.AuthenticationHelper;
import gov.nist.javax.sip.header.extensions.ReferencesHeader;
import gov.nist.javax.sip.header.extensions.ReferredByHeader;
import gov.nist.javax.sip.header.extensions.ReplacesHeader;

import android.net.sip.SipProfile;
import android.util.Log;
@@ -284,14 +287,18 @@ class SipHelper {
    }

    public ClientTransaction sendInvite(SipProfile caller, SipProfile callee,
            String sessionDescription, String tag)
            throws SipException {
            String sessionDescription, String tag, ReferredByHeader referredBy,
            String replaces) throws SipException {
        try {
            Request request = createRequest(Request.INVITE, caller, callee, tag);
            if (referredBy != null) request.addHeader(referredBy);
            if (replaces != null) {
                request.addHeader(mHeaderFactory.createHeader(
                        ReplacesHeader.NAME, replaces));
            }
            request.setContent(sessionDescription,
                    mHeaderFactory.createContentTypeHeader(
                            "application", "sdp"));

            ClientTransaction clientTransaction =
                    mSipProvider.getNewClientTransaction(request);
            if (DEBUG) Log.d(TAG, "send INVITE: " + request);
@@ -455,6 +462,25 @@ class SipHelper {
        }
    }

    public void sendReferNotify(Dialog dialog, String content)
            throws SipException {
        try {
            Request request = dialog.createRequest(Request.NOTIFY);
            request.addHeader(mHeaderFactory.createSubscriptionStateHeader(
                    "active;expires=60"));
            // set content here
            request.setContent(content,
                    mHeaderFactory.createContentTypeHeader(
                            "message", "sipfrag"));
            request.addHeader(mHeaderFactory.createEventHeader(
                    ReferencesHeader.REFER));
            if (DEBUG) Log.d(TAG, "send NOTIFY: " + request);
            dialog.sendRequest(mSipProvider.getNewClientTransaction(request));
        } catch (ParseException e) {
            throw new SipException("sendReferNotify()", e);
        }
    }

    public void sendInviteRequestTerminated(Request inviteRequest,
            ServerTransaction inviteTransaction) throws SipException {
        try {
+96 −24
Original line number Diff line number Diff line
@@ -18,12 +18,15 @@ 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.SIPHeaderNames;
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;
@@ -71,12 +74,15 @@ import javax.sip.address.SipURI;
import javax.sip.header.CSeqHeader;
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.
 */
@@ -390,25 +396,26 @@ class SipSessionGroup implements SipListener {
        }
    }

    private class SipSessionCallReceiverImpl extends SipSessionImpl {
        public SipSessionCallReceiverImpl(ISipSessionListener listener) {
            super(listener);
        }

    private SipSessionImpl createNewSession(RequestEvent event,
                ISipSessionListener listener, ServerTransaction transaction)
                throws SipException {
            ISipSessionListener listener, ServerTransaction transaction,
            int newState) throws SipException {
        SipSessionImpl newSession = new SipSessionImpl(listener);
        newSession.mServerTransaction = transaction;
            newSession.mState = SipSession.State.INCOMING_CALL;
        newSession.mState = newState;
        newSession.mDialog = newSession.mServerTransaction.getDialog();
        newSession.mInviteReceived = event;
            newSession.mPeerProfile = createPeerProfile(event.getRequest());
        newSession.mPeerProfile = createPeerProfile((HeaderAddress)
                event.getRequest().getHeader(FromHeader.NAME));
        newSession.mPeerSessionDescription =
                extractContent(event.getRequest());
        return newSession;
    }

    private class SipSessionCallReceiverImpl extends SipSessionImpl {
        public SipSessionCallReceiverImpl(ISipSessionListener listener) {
            super(listener);
        }

        private int processInviteWithReplaces(RequestEvent event,
                ReplacesHeader replaces) {
            String callId = replaces.getCallId();
@@ -452,7 +459,8 @@ class SipSessionGroup implements SipListener {
                    // got INVITE w/ replaces request.
                    newSession = createNewSession(event,
                            replacedSession.mProxy.getListener(),
                            mSipHelper.getServerTransaction(event));
                            mSipHelper.getServerTransaction(event),
                            SipSession.State.INCOMING_CALL);
                    newSession.mProxy.onCallTransferring(newSession,
                            newSession.mPeerSessionDescription);
                } else {
@@ -461,7 +469,8 @@ class SipSessionGroup implements SipListener {
            } else {
                // New Incoming call.
                newSession = createNewSession(event, mProxy,
                        mSipHelper.sendRinging(event, generateTag()));
                        mSipHelper.sendRinging(event, generateTag()),
                        SipSession.State.INCOMING_CALL);
                mProxy.onRinging(newSession, newSession.mPeerProfile,
                        newSession.mPeerSessionDescription);
            }
@@ -507,6 +516,11 @@ class SipSessionGroup implements SipListener {

        private SipSessionImpl mKeepAliveSession;

        // the following three members are used for handling refer request.
        SipSessionImpl mReferSession;
        ReferredByHeader mReferredBy;
        String mReplaces;

        // lightweight timer
        class SessionTimer {
            private boolean mRunning = true;
@@ -556,6 +570,9 @@ class SipSessionGroup implements SipListener {
            mInviteReceived = null;
            mPeerSessionDescription = null;
            mAuthenticationRetryCount = 0;
            mReferSession = null;
            mReferredBy = null;
            mReplaces = null;

            if (mDialog != null) mDialog.delete();
            mDialog = null;
@@ -969,15 +986,26 @@ class SipSessionGroup implements SipListener {
            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();
                mClientTransaction = mSipHelper.sendInvite(mLocalProfile,
                        mPeerProfile, cmd.getSessionDescription(),
                        generateTag());
                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());
@@ -1072,6 +1100,12 @@ class SipSessionGroup implements SipListener {
                    }
                    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);
@@ -1087,6 +1121,10 @@ class SipSessionGroup implements SipListener {
                    // 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);
@@ -1155,6 +1193,38 @@ class SipSessionGroup implements SipListener {
            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
@@ -1175,6 +1245,8 @@ class SipSessionGroup implements SipListener {
                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;
@@ -1182,6 +1254,8 @@ class SipSessionGroup implements SipListener {
                        ((MakeCallCommand) evt).getSessionDescription());
                startSessionTimer(((MakeCallCommand) evt).getTimeout());
                return true;
            } else if (evt instanceof ResponseEvent) {
                if (expectResponse(Request.NOTIFY, evt)) return true;
            }
            return false;
        }
@@ -1558,12 +1632,10 @@ class SipSessionGroup implements SipListener {
        return false;
    }

    private static SipProfile createPeerProfile(Request request)
    private static SipProfile createPeerProfile(HeaderAddress header)
            throws SipException {
        try {
            FromHeader fromHeader =
                    (FromHeader) request.getHeader(FromHeader.NAME);
            Address address = fromHeader.getAddress();
            Address address = header.getAddress();
            SipURI uri = (SipURI) address.getURI();
            String username = uri.getUser();
            if (username == null) username = ANONYMOUS;